@bitkyc08/opencodex 2.52.0-preview.20260911 → 2.52.0-preview.20260912

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 (166) hide show
  1. package/gui/dist/assets/index-D_t6sCWs.js +115 -0
  2. package/gui/dist/assets/index-EdoPnm9_.css +1 -0
  3. package/gui/dist/index.html +2 -2
  4. package/gui/dist/provider-icons/devin.svg +49 -0
  5. package/gui/dist/provider-icons/omo.svg +42 -0
  6. package/package.json +3 -1
  7. package/src/AGENTS.md +1 -1
  8. package/src/adapters/cline-pass-deepseek-v4-tool-replay.ts +0 -1
  9. package/src/adapters/command-code.ts +0 -1
  10. package/src/adapters/cursor/checkpoint-store.ts +37 -0
  11. package/src/adapters/cursor/live-transport.ts +74 -2
  12. package/src/adapters/cursor.ts +6 -0
  13. package/src/adapters/devin/cloud-direct/auth.ts +264 -0
  14. package/src/adapters/devin/cloud-direct/catalog.ts +306 -0
  15. package/src/adapters/devin/cloud-direct/chat.ts +1274 -0
  16. package/src/adapters/devin/cloud-direct/index.ts +65 -0
  17. package/src/adapters/devin/cloud-direct/metadata.ts +134 -0
  18. package/src/adapters/devin/cloud-direct/wire.ts +206 -0
  19. package/src/adapters/devin/live-models.ts +133 -0
  20. package/src/adapters/devin-cli/acp.ts +204 -0
  21. package/src/adapters/devin-cli/adapter.ts +345 -0
  22. package/src/adapters/devin-cli/binary.ts +69 -0
  23. package/src/adapters/devin-cli/models.ts +57 -0
  24. package/src/adapters/devin.ts +326 -0
  25. package/src/adapters/google.ts +1 -1
  26. package/src/adapters/openai-chat.ts +31 -10
  27. package/src/adapters/openai-responses.ts +16 -1
  28. package/src/adapters/registry.ts +25 -1
  29. package/src/bridge.ts +8 -2
  30. package/src/claude/inbound-cache-stabilize.ts +130 -0
  31. package/src/claude/inbound.ts +45 -5
  32. package/src/cli/account-auth.ts +60 -1
  33. package/src/cli/account-extended.ts +23 -30
  34. package/src/cli/account.ts +2 -1
  35. package/src/cli/capabilities.ts +50 -8
  36. package/src/cli/config-command.ts +2 -2
  37. package/src/cli/dispatch.ts +14 -2
  38. package/src/cli/export-command.ts +11 -25
  39. package/src/cli/help.ts +2 -2
  40. package/src/cli/opencode.ts +5 -0
  41. package/src/cli/registry.ts +15 -4
  42. package/src/clients/config-export/cline.ts +71 -0
  43. package/src/clients/config-export/contracts.ts +10 -1
  44. package/src/clients/config-export/model-metadata.ts +33 -0
  45. package/src/clients/config-export/zcode.ts +23 -12
  46. package/src/clients/config-export.ts +156 -4
  47. package/src/codex/account-store.ts +7 -2
  48. package/src/codex/auth-context.ts +1 -1
  49. package/src/codex/catalog/effort.ts +4 -3
  50. package/src/codex/catalog/metadata.ts +8 -4
  51. package/src/codex/catalog/native-models.ts +24 -4
  52. package/src/codex/catalog/parsing.ts +19 -1
  53. package/src/codex/catalog/provider-fetch.ts +57 -0
  54. package/src/codex/catalog/sync.ts +1 -1
  55. package/src/codex/context-compat.ts +97 -0
  56. package/src/codex/context-owner.ts +201 -0
  57. package/src/codex/history-provider.ts +161 -14
  58. package/src/codex/inject-coordination.ts +3 -2
  59. package/src/codex/inject.ts +128 -9
  60. package/src/codex/pool-rotation.ts +8 -292
  61. package/src/codex/quota.ts +41 -12
  62. package/src/codex/retired-model-migration.ts +41 -0
  63. package/src/codex/routing.ts +327 -22
  64. package/src/codex/warmup.ts +2 -2
  65. package/src/combos/failover.ts +5 -0
  66. package/src/combos/resolve.ts +11 -15
  67. package/src/config.ts +46 -14
  68. package/src/generated/compatibility-version.json +293 -113
  69. package/src/grok/grpc-web.ts +120 -0
  70. package/src/grok/reset-coupon-ledger.ts +139 -0
  71. package/src/grok/reset-coupons.ts +278 -0
  72. package/src/integrations/catalog-refresh.ts +1 -1
  73. package/src/integrations/cline-document.ts +73 -0
  74. package/src/integrations/cline-io.ts +149 -0
  75. package/src/integrations/cline-transaction.ts +42 -0
  76. package/src/integrations/config-io.ts +21 -2
  77. package/src/integrations/journal.ts +43 -1
  78. package/src/integrations/omp-yaml-source.ts +123 -4
  79. package/src/integrations/ownership-policy.ts +7 -0
  80. package/src/integrations/ownership.ts +36 -0
  81. package/src/integrations/registry.ts +27 -0
  82. package/src/integrations/state.ts +5 -2
  83. package/src/integrations/store.ts +6 -0
  84. package/src/integrations/writer.ts +53 -9
  85. package/src/lab/subject/behavior-fingerprint.ts +1 -1
  86. package/src/lib/abort.ts +36 -0
  87. package/src/lib/local-destinations.ts +1 -1
  88. package/src/lib/upstream-retry.ts +36 -1
  89. package/src/oauth/account-quota-rank.ts +11 -0
  90. package/src/oauth/callback-server.ts +10 -5
  91. package/src/oauth/devin/api-base.ts +63 -0
  92. package/src/oauth/devin/login.ts +1 -0
  93. package/src/oauth/devin/register-user.ts +186 -0
  94. package/src/oauth/devin/types.ts +71 -0
  95. package/src/oauth/devin-cli.ts +149 -0
  96. package/src/oauth/devin.ts +170 -0
  97. package/src/oauth/generic-account-failover.ts +169 -1
  98. package/src/oauth/index.ts +20 -1
  99. package/src/oauth/login-cli.ts +19 -5
  100. package/src/oauth/pool-kernel.ts +321 -0
  101. package/src/oauth/pool-settings-capability.ts +127 -9
  102. package/src/oauth/store.ts +7 -3
  103. package/src/oauth/token-guardian.ts +1 -1
  104. package/src/providers/codebuddy-models.ts +0 -3
  105. package/src/providers/command-code-efforts.ts +23 -4
  106. package/src/providers/default-aliases.ts +4 -0
  107. package/src/providers/derive.ts +8 -0
  108. package/src/providers/devin-cli-authmode-migration.ts +57 -0
  109. package/src/providers/key-failover.ts +175 -0
  110. package/src/providers/model-rename-startup.ts +21 -1
  111. package/src/providers/qoder-models.ts +0 -1
  112. package/src/providers/quota-key-accounts.ts +50 -0
  113. package/src/providers/quota-routing-cache.ts +65 -6
  114. package/src/providers/quota-types.ts +7 -0
  115. package/src/providers/quota.ts +113 -30
  116. package/src/providers/registry.ts +237 -77
  117. package/src/providers/stale-context-window-migration.ts +92 -0
  118. package/src/providers/zai-responses-migration.ts +45 -0
  119. package/src/quota/reset-observer.ts +2 -1
  120. package/src/quota/reset-seen-store.ts +9 -1
  121. package/src/remote-control/crypto.ts +442 -0
  122. package/src/remote-control/host.ts +175 -0
  123. package/src/remote-control/index.ts +100 -0
  124. package/src/remote-control/protocol.ts +200 -0
  125. package/src/remote-control/relay.ts +162 -0
  126. package/src/remote-control/workspace-agent-protocol.ts +246 -0
  127. package/src/remote-control/workspace-rpc-framing.ts +128 -0
  128. package/src/remote-control/workspace-tools.ts +237 -0
  129. package/src/remote-control/workspace-utf8.ts +24 -0
  130. package/src/responses/custom-tool-compat.ts +23 -0
  131. package/src/router.ts +6 -1
  132. package/src/routing/compatibility/behavior.ts +3 -0
  133. package/src/server/adapter-resolve.ts +6 -2
  134. package/src/server/auth-cors.ts +71 -6
  135. package/src/server/chat-completions.ts +5 -3
  136. package/src/server/chat-native.ts +9 -0
  137. package/src/server/claude-messages.ts +25 -30
  138. package/src/server/context-history.ts +207 -0
  139. package/src/server/images.ts +28 -1
  140. package/src/server/index.ts +42 -22
  141. package/src/server/live.ts +2 -1
  142. package/src/server/management/agent-settings-routes.ts +7 -1
  143. package/src/server/management/config-routes.ts +3 -3
  144. package/src/server/management/grok-coupon-routes.ts +287 -0
  145. package/src/server/management/integration-routes.ts +6 -1
  146. package/src/server/management/oauth-account-routes.ts +124 -7
  147. package/src/server/management/provider-routes.ts +77 -2
  148. package/src/server/management/route-registry.ts +6 -1
  149. package/src/server/management-api.ts +7 -0
  150. package/src/server/request-log.ts +4 -0
  151. package/src/server/responses/codex-ws-exchange.ts +90 -14
  152. package/src/server/responses/codex-ws-wire.ts +51 -1
  153. package/src/server/responses/compact.ts +8 -1
  154. package/src/server/responses/core.ts +188 -22
  155. package/src/server/responses/ws-upstream.ts +1 -1
  156. package/src/server/responses-undeclared-tool-guard.ts +261 -12
  157. package/src/server/zai-responses-startup.ts +21 -0
  158. package/src/types/config.ts +33 -3
  159. package/src/types/provider.ts +54 -4
  160. package/src/types/request.ts +1 -1
  161. package/src/types/tools.ts +36 -6
  162. package/src/update/job.ts +33 -11
  163. package/src/vision/plan.ts +40 -37
  164. package/src/web-search/index.ts +1 -0
  165. package/gui/dist/assets/index-BoBRSehJ.css +0 -1
  166. package/gui/dist/assets/index-Dx0xv2EA.js +0 -115
@@ -1,6 +1,7 @@
1
1
  import type { CodexAccountMode, FastWire, OcxProviderConfig } from "../types";
2
2
  import { fastWireDeclarationError } from "./fastwire";
3
3
  import { KIRO_MODELS, KIRO_MODEL_CONTEXT_WINDOWS, KIRO_MODEL_REASONING_EFFORTS } from "./kiro-models";
4
+ import { DEVIN_MODEL_CONTEXT_WINDOWS } from "../adapters/devin/live-models";
4
5
  import { ANTIGRAVITY_MODELS, ANTIGRAVITY_MODEL_CONTEXT_WINDOWS, ANTIGRAVITY_MODEL_EFFORTS, ANTIGRAVITY_MODEL_INPUT_MODALITIES } from "./antigravity-models";
5
6
  import type { ProviderBaseUrlChoice } from "./base-url-choices";
6
7
  import {
@@ -229,6 +230,19 @@ export interface ProviderRegistryEntry {
229
230
  * per model. DeepSeek documents `POST /responses` with no `/v1` segment.
230
231
  */
231
232
  responsesPath?: string;
233
+ /**
234
+ * Relative send path for the `openai-chat` wire, seeded into saved config exactly like
235
+ * `responsesPath`. Needed when one upstream serves both wires under different prefixes,
236
+ * because a per-model wire override changes the adapter and not the base URL.
237
+ */
238
+ chatCompletionsPath?: string;
239
+ /**
240
+ * Endpoints this entry used to live at, kept so a saved custom provider that still points
241
+ * at one keeps receiving this row's metadata through `registryEntryForProviderDestination`.
242
+ * Destination matching is by adapter plus normalized base URL, so moving a row's wire or
243
+ * prefix would otherwise orphan every config a user wrote against the old address.
244
+ */
245
+ destinationAliases?: readonly { readonly baseUrl: string; readonly adapter: string }[];
232
246
  /**
233
247
  * Responses upstream that stores nothing server-side. Stateful request parameters
234
248
  * are dropped and `store` is pinned false, and orphaned tool results left by a
@@ -321,6 +335,12 @@ export interface ProviderRegistryEntry {
321
335
  noTemperatureModels?: string[];
322
336
  noTopPModels?: string[];
323
337
  noPenaltyModels?: string[];
338
+ /**
339
+ * Registry-only seed for `OcxProviderConfig.noJsonSchemaModels`. Merged into the
340
+ * resolved provider at route time rather than persisted as user config, the same way
341
+ * `directReasoningEffortModels` above is registry-owned.
342
+ */
343
+ noJsonSchemaModels?: string[];
324
344
  /** Opt this provider into parallel tool calls (see OcxProviderConfig.parallelToolCalls). */
325
345
  parallelToolCalls?: boolean;
326
346
  /** Opt this provider into forwarding prompt_cache_key (OpenAI-specific; strict backends reject it). */
@@ -354,7 +374,7 @@ export interface ProviderRegistryEntry {
354
374
 
355
375
  export type ProviderConfigSeed = Pick<
356
376
  OcxProviderConfig,
357
- "adapter" | "baseUrl" | "apiKeyTransport" | "responsesPath" | "authMode" | "keyOptional" | "freeTier" | "modelSuffixBracketStrip" | "defaultModel" | "models"
377
+ "adapter" | "baseUrl" | "apiKeyTransport" | "responsesPath" | "chatCompletionsPath" | "authMode" | "keyOptional" | "freeTier" | "modelSuffixBracketStrip" | "defaultModel" | "models"
358
378
  | "liveModels" | "contextWindow" | "modelContextWindows" | "modelInputModalities"
359
379
  | "modelDisplayNames"
360
380
  | "modelMaxInputTokens" | "defaultMaxOutputTokens" | "modelMaxOutputTokens"
@@ -437,6 +457,30 @@ const ZAI_GLM_5X_MODELS = [...ZAI_GLM_53_MODELS, ...ZAI_GLM_52_MODELS];
437
457
  * `preserveReasoningContentModels`, where flash DOES belong.
438
458
  */
439
459
  const ZAI_GLM_5X_SIDECAR_VISION_MODELS = ZAI_GLM_5X_MODELS.filter(id => id !== "glm-5.3-flash");
460
+ /**
461
+ * Positive input-modality declaration for the Chat-path GLM rows.
462
+ *
463
+ * `noVisionModels` already keeps Flash out of the vision sidecar, but that is a NEGATIVE
464
+ * statement: it stops a detour without telling the catalog what the model can read. With
465
+ * no `modelInputModalities` entry, `configuredInputModalities` returns undefined and the
466
+ * catalog falls through to the `["text"]` floor, so every client export (ZCode, Pi, OMP)
467
+ * listed a native VLM as text-only and its picker refused to attach an image.
468
+ *
469
+ * The Responses sibling row below already declares this positively, so the same model was
470
+ * described two different ways in one registry.
471
+ *
472
+ * Authoritative source: `GET https://api.z.ai/api/v1/models` returns `input_modalities:
473
+ * ["text"]` for glm-5.3 and `["text", "image"]` for glm-5.3-flash (captured in
474
+ * devlog/_plan/260912_zcode_protocol_and_catalog/evidence/zai-responses-models.json).
475
+ * docs.z.ai/devpack/latest-model says the same in prose: "GLM-5.3 is a text-only model...
476
+ * GLM-5.3-FLASH is a multimodal model". Upstream also lists video and file for Flash;
477
+ * neither the internal vocabulary nor the export vocabulary can express them, so `image`
478
+ * is where this stops.
479
+ */
480
+ const ZAI_GLM_5X_INPUT_MODALITIES: Record<string, string[]> = {
481
+ ...Object.fromEntries(ZAI_GLM_5X_SIDECAR_VISION_MODELS.map(id => [id, ["text"]])),
482
+ "glm-5.3-flash": ["text", "image"],
483
+ };
440
484
  const ZAI_GLM_52_REASONING_EFFORTS = ["low", "medium", "high", "xhigh", "max"];
441
485
  /**
442
486
  * GLM-5.3 does NOT share 5.2's five-tier ladder. docs.z.ai/devpack/latest-model folds every
@@ -610,7 +654,30 @@ const THINKING_BUDGET_MODELS = [
610
654
  "qwen3.5-plus", "qwen3.6-plus", "qwen3.7-max", "qwen3.7-plus",
611
655
  ];
612
656
  const OPENCODE_GO_THINKING_BUDGET_MODELS = ["qwen3.5-plus", "qwen3.6-plus", "qwen3.7-max", "qwen3.7-plus"];
613
- const DEEPSEEK_THINKING_MODELS = ["deepseek-v4-pro", "deepseek-v4-flash"];
657
+ /*
658
+ * DeepSeek moved the whole V4 name set on 2026-09-10. V4.1-Flash ships as deepseek-flash
659
+ * on the first-party API; deepseek-v4-flash and the vision preview retire as models but
660
+ * keep routing there as compatibility aliases, and deepseek-v4-pro follows from
661
+ * 2026-09-14 04:00 UTC. Evidence: https://api-docs.deepseek.com/news/news260910/.
662
+ *
663
+ * The spelling differs by who serves it, so one shared list cannot express it: the
664
+ * first-party API answers to deepseek-flash, while the Zen gateway exposes the route as
665
+ * deepseek-v4.1-flash (issue #4253, PR #4258). Vendor-hosted rosters (Volcengine plan
666
+ * snapshots, Alibaba) publish on their own schedule and keep the legacy set until they say
667
+ * otherwise - a first-party retirement notice does not end their deployment.
668
+ */
669
+ const DEEPSEEK_V4_LEGACY_MODELS = ["deepseek-v4-flash"];
670
+ /*
671
+ * `deepseek-v4-pro` is deliberately absent from both live sets. DeepSeek retires it from
672
+ * 2026-09-14 04:00 UTC and routes its requests to V4.1-Flash until a V4.1 Pro exists, so a
673
+ * row here would advertise a Pro context window and Pro pricing for a route that serves
674
+ * Flash. The retirement is followed through every roster in this file, including the
675
+ * vendor-hosted ones; providers that discover their models live are handled by
676
+ * `ROUTED_MODEL_COMPATIBILITY_EXCLUSIONS` because deleting a row there removes the
677
+ * model's capabilities rather than the model.
678
+ */
679
+ const DEEPSEEK_NATIVE_THINKING_MODELS = ["deepseek-flash", "deepseek-v4-flash"];
680
+ const DEEPSEEK_GATEWAY_THINKING_MODELS = ["deepseek-v4.1-flash", "deepseek-v4-flash"];
614
681
  /*
615
682
  * DeepSeek's experimental vision preview (released 2026-08-21, api-docs.deepseek.com):
616
683
  * text+image input on the V4 Flash base. DeepSeek positions it as a preview id;
@@ -622,7 +689,7 @@ const DEEPSEEK_VISION_PREVIEW_MODEL = "deepseek-v4-flash-vision-exp";
622
689
  * CommandCode routes verified to accept image input end-to-end (#2406).
623
690
  *
624
691
  * Verified-negative and therefore deliberately ABSENT: deepseek/deepseek-v4-flash,
625
- * deepseek/deepseek-v4-pro, zai-org/GLM-5.2, zai-org/GLM-5.3, xai/grok-4.6. Those
692
+ * zai-org/GLM-5.2, zai-org/GLM-5.3, xai/grok-4.6. Those
626
693
  * routes accept the request and drop the image, which is worse than declining it — the
627
694
  * model answers about an image it never saw. Do not add an id here on family resemblance;
628
695
  * capability intersection trusts this map.
@@ -710,7 +777,7 @@ const DEEPSEEK_FLASH_REASONING_MAP: Record<string, string> = {
710
777
  };
711
778
  /**
712
779
  * Flash-versus-Pro classification for DeepSeek V4 model ids, including prefixed
713
- * (`deepseek/deepseek-v4-pro`) and suffixed (`deepseek-v4-flash-free`) forms.
780
+ * (`deepseek/deepseek-v4.1-flash`) and suffixed (`deepseek-v4-flash-free`) forms.
714
781
  * `tests/providers/provider-registry-parity.test.ts` enumerates every id the registry
715
782
  * actually passes here, so a future id this substring test would misread cannot
716
783
  * land silently.
@@ -727,7 +794,7 @@ const deepseekReasoningMapFor = (modelId: string): Record<string, string> =>
727
794
  // https://help.aliyun.com/en/model-studio/token-plan-quickstart
728
795
  const ALIBABA_TOKEN_PLAN_MODELS = [
729
796
  "qwen3.8-max", "qwen3.7-max", "qwen3.7-plus", "qwen3.6-flash",
730
- "glm-5.3", "glm-5.3-flash", "glm-5.2", "deepseek-v4-pro",
797
+ "glm-5.3", "glm-5.3-flash", "glm-5.2",
731
798
  ];
732
799
  const ALIBABA_TOKEN_PLAN_QWEN_MODELS = [
733
800
  "qwen3.8-max", "qwen3.7-max", "qwen3.7-plus", "qwen3.6-flash",
@@ -740,7 +807,6 @@ const ALIBABA_TOKEN_PLAN_INPUT_MODALITIES: Record<string, string[]> = {
740
807
  "glm-5.3": ["text"],
741
808
  "glm-5.3-flash": ["text", "image"],
742
809
  "glm-5.2": ["text"],
743
- "deepseek-v4-pro": ["text"],
744
810
  };
745
811
 
746
812
  // 260721 Alibaba Token Plan International (ap-southeast-1 / Singapore, hardened 260721).
@@ -749,7 +815,7 @@ const ALIBABA_TOKEN_PLAN_INPUT_MODALITIES: Record<string, string[]> = {
749
815
  // https://qwencloud.com/pricing/token-plan (qwen3.8 metadata)
750
816
  const ALIBABA_INTL_TOKEN_PLAN_MODELS = [
751
817
  "qwen3.8-max", "qwen3.7-max", "qwen3.7-plus", "qwen3.6-plus", "qwen3.6-flash",
752
- "deepseek-v4-pro", "deepseek-v4-flash", "deepseek-v3.2",
818
+ "deepseek-v4-flash", "deepseek-v3.2",
753
819
  "kimi-k2.7-code", "kimi-k2.6", "kimi-k2.5",
754
820
  "glm-5.3", "glm-5.3-flash", "glm-5.2", "glm-5.1", "glm-5",
755
821
  "MiniMax-M2.5",
@@ -782,7 +848,6 @@ const VOLCENGINE_ARK_MODELS = [
782
848
  "doubao-seed-2-1-pro-260628",
783
849
  "doubao-seed-2-1-turbo-260628",
784
850
  "doubao-seed-evolving",
785
- "deepseek-v4-pro-260425",
786
851
  "deepseek-v4-flash-260425",
787
852
  "deepseek-v3-2-251201",
788
853
  // No glm-5-3 row: Ark pins date-stamped snapshot ids (glm-5-2-260617) that cannot be
@@ -798,7 +863,6 @@ const VOLCENGINE_DOUBAO_THINKING_MODELS = [
798
863
  const VOLCENGINE_CODING_PLAN_MODELS = [
799
864
  "ark-code-latest",
800
865
  "doubao-seed-2.0-code",
801
- "deepseek-v4-pro",
802
866
  "deepseek-v4-flash",
803
867
  "glm-5.3",
804
868
  "glm-5.3-flash",
@@ -807,7 +871,6 @@ const VOLCENGINE_CODING_PLAN_MODELS = [
807
871
  "minimax-m3",
808
872
  ];
809
873
  const VOLCENGINE_AGENT_PLAN_MODELS = [
810
- "deepseek-v4-pro",
811
874
  "deepseek-v4-flash",
812
875
  "glm-5.3",
813
876
  "glm-5.3-flash",
@@ -829,7 +892,6 @@ const VOLCENGINE_PLAN_INPUT_MODALITIES: Record<string, string[]> = {
829
892
  const VOLCENGINE_PLAN_TEXT_ONLY_MODELS = [
830
893
  "ark-code-latest",
831
894
  "doubao-seed-2.0-code",
832
- "deepseek-v4-pro",
833
895
  "deepseek-v4-flash",
834
896
  "glm-5.3",
835
897
  "glm-5.2",
@@ -841,7 +903,6 @@ const ALIBABA_INTL_TOKEN_PLAN_INPUT_MODALITIES: Record<string, string[]> = {
841
903
  "qwen3.7-plus": ["text", "image"],
842
904
  "qwen3.6-plus": ["text", "image"],
843
905
  "qwen3.6-flash": ["text", "image"],
844
- "deepseek-v4-pro": ["text"],
845
906
  "deepseek-v4-flash": ["text"],
846
907
  "deepseek-v3.2": ["text"],
847
908
  "kimi-k2.7-code": ["text", "image"],
@@ -960,7 +1021,7 @@ const NVIDIA_NIM_VISION_INPUT_MODALITIES: Record<string, string[]> = Object.from
960
1021
  * reasoning suppression regardless of which list they appear in here.
961
1022
  */
962
1023
  const NVIDIA_NIM_NO_VISION_MODELS = [
963
- "deepseek-ai/deepseek-v4-flash", "deepseek-ai/deepseek-v4-pro",
1024
+ "deepseek-ai/deepseek-v4-flash",
964
1025
  "google/codegemma-7b",
965
1026
  "meta/llama-3.1-70b-instruct", "meta/llama-3.1-8b-instruct",
966
1027
  "meta/llama-3.2-1b-instruct", "meta/llama-3.2-3b-instruct",
@@ -1001,7 +1062,6 @@ const NEURALWATT_REASONING_HISTORY_MODELS = [
1001
1062
  // https://docs.baseten.co/inference/model-apis/vision
1002
1063
  const BASETEN_FULL_REASONING_EFFORTS = ["low", "medium", "high", "xhigh", "max"];
1003
1064
  const BASETEN_MODEL_REASONING_EFFORTS: Record<string, string[]> = {
1004
- "deepseek-ai/DeepSeek-V4-Pro": BASETEN_FULL_REASONING_EFFORTS,
1005
1065
  "thinkingmachines/inkling": BASETEN_FULL_REASONING_EFFORTS,
1006
1066
  "openai/gpt-oss-120b": BASETEN_FULL_REASONING_EFFORTS,
1007
1067
  "moonshotai/Kimi-K3": ["low", "high", "max"],
@@ -1012,7 +1072,6 @@ const BASETEN_MODEL_REASONING_EFFORTS: Record<string, string[]> = {
1012
1072
  "zai-org/GLM-5.2-Fast": ["high", "max"],
1013
1073
  };
1014
1074
  const BASETEN_MODEL_REASONING_EFFORT_MAP: Record<string, Record<string, string>> = {
1015
- "deepseek-ai/DeepSeek-V4-Pro": { none: "none", minimal: "minimal" },
1016
1075
  "thinkingmachines/inkling": { none: "none", minimal: "minimal" },
1017
1076
  "openai/gpt-oss-120b": { none: "none", minimal: "minimal" },
1018
1077
  "moonshotai/Kimi-K3": { none: "none" },
@@ -1022,7 +1081,6 @@ const BASETEN_MODEL_REASONING_EFFORT_MAP: Record<string, Record<string, string>>
1022
1081
  "zai-org/GLM-5.2-Fast": { none: "none" },
1023
1082
  };
1024
1083
  const BASETEN_MODEL_DEFAULT_REASONING_EFFORTS: Record<string, string> = {
1025
- "deepseek-ai/DeepSeek-V4-Pro": "medium",
1026
1084
  "thinkingmachines/inkling": "high",
1027
1085
  "openai/gpt-oss-120b": "medium",
1028
1086
  "moonshotai/Kimi-K3": "max",
@@ -1050,7 +1108,6 @@ const DIGITALOCEAN_CHAT_COMPLETION_MODELS = [
1050
1108
  "openai-gpt-5.6-luna",
1051
1109
  "qwen3-coder-flash",
1052
1110
  "qwen3.5-397b-a17b",
1053
- "deepseek-v4-pro",
1054
1111
  "deepseek-4-flash",
1055
1112
  "deepseek-3.2",
1056
1113
  "gemma-4-31B-it",
@@ -1135,7 +1192,6 @@ const CLINE_PASS_MODELS = [
1135
1192
  "cline-pass/kimi-k3",
1136
1193
  "cline-pass/kimi-k2.7-code",
1137
1194
  "cline-pass/kimi-k2.6",
1138
- "cline-pass/deepseek-v4-pro",
1139
1195
  "cline-pass/deepseek-v4-flash",
1140
1196
  "cline-pass/mimo-v2.5",
1141
1197
  "cline-pass/mimo-v2.5-pro",
@@ -1171,17 +1227,11 @@ const ORCAROUTER_MODELS = [
1171
1227
  "openai/gpt-5.5",
1172
1228
  "anthropic/claude-opus-4.8",
1173
1229
  "google/gemini-3.5-flash",
1174
- "deepseek/deepseek-v4-pro",
1175
1230
  "orcarouter/auto",
1176
1231
  ];
1177
- const ORCAROUTER_TEXT_ONLY_MODELS = ["deepseek/deepseek-v4-pro"];
1178
1232
  const ORCAROUTER_MODEL_REASONING_EFFORTS = {
1179
1233
  // Live /models currently exposes ids and modalities, not the accepted reasoning ladder.
1180
1234
  "openai/gpt-5.5": ["low", "medium", "high", "xhigh"],
1181
- "deepseek/deepseek-v4-pro": deepseekThinkingEffortsFor("deepseek/deepseek-v4-pro"),
1182
- };
1183
- const ORCAROUTER_MODEL_REASONING_EFFORT_MAP = {
1184
- "deepseek/deepseek-v4-pro": deepseekReasoningMapFor("deepseek/deepseek-v4-pro"),
1185
1235
  };
1186
1236
  const CLINE_PASS_MODEL_CONTEXT_WINDOWS: Record<string, number> = {
1187
1237
  "cline-pass/glm-5.3": 1_048_576,
@@ -1190,7 +1240,6 @@ const CLINE_PASS_MODEL_CONTEXT_WINDOWS: Record<string, number> = {
1190
1240
  "cline-pass/kimi-k3": 1_048_576,
1191
1241
  "cline-pass/kimi-k2.7-code": 262_144,
1192
1242
  "cline-pass/kimi-k2.6": 262_144,
1193
- "cline-pass/deepseek-v4-pro": 1_048_576,
1194
1243
  "cline-pass/deepseek-v4-flash": 1_048_576,
1195
1244
  "cline-pass/mimo-v2.5": 1_050_000,
1196
1245
  "cline-pass/mimo-v2.5-pro": 1_050_000,
@@ -1265,6 +1314,57 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1265
1314
  // still advertises image for noVision members so Codex can attach (sidecar option B).
1266
1315
  noVisionModels: [...CURSOR_NO_VISION_MODELS],
1267
1316
  },
1317
+ {
1318
+ // The signed-in Devin CLI as an account source.
1319
+ //
1320
+ // The CLI writes a `devin-session-token$<JWT>` to its own credentials.toml,
1321
+ // which is the same credential RegisterUser hands `ocx login devin` and which
1322
+ // the cloud-direct client already speaks. So this provider imports that token
1323
+ // and streams over Connect-RPC like its browser-login sibling, rather than
1324
+ // spawning `devin acp`.
1325
+ //
1326
+ // `oauth` classifies the ACCOUNT, not the transport. This is not a local
1327
+ // runtime: unlike Ollama or LM Studio it cannot answer at all until a vendor
1328
+ // account is signed in, and `local` grouped it with things that have no
1329
+ // account. It is also the only classification that reaches the dashboard
1330
+ // Accounts tab, which is built from OAUTH_PROVIDERS.
1331
+ //
1332
+ // The ACP adapter stays registered and tested. It is no longer reachable
1333
+ // under THIS id — `routedProviderConfig` pins the adapter from the registry
1334
+ // for any row whose name is a registry id — but a custom-named row such as
1335
+ // `{"devin-acp": {"adapter": "devin-cli", ...}}` is not pinned and still gets it.
1336
+ id: "devin-cli",
1337
+ label: "Devin CLI",
1338
+ adapter: "devin",
1339
+ baseUrl: "https://server.codeium.com",
1340
+ authKind: "oauth",
1341
+ featured: false,
1342
+ // Off, like `devin`. `deriveProviderPresets` keys the preset catalog off this
1343
+ // flag, so leaving it true would draw the row twice: an Accounts login row and
1344
+ // a preset tile.
1345
+ dashboardPreset: false,
1346
+ note: "Imports the credential your installed Devin CLI already holds (`devin auth login`), then streams over Cognition's Connect-RPC api-server like the `devin` provider. No browser sign-in and no key to paste. For the CLI's own local agent loop over ACP stdio instead, configure a custom-named provider row with \"adapter\": \"devin-cli\".",
1347
+ // Degraded-mode seed only; `liveModels` discovers the account's real roster,
1348
+ // which is where `swe-2` and the rest of the current catalog come from.
1349
+ models: ["swe-2", "swe-1-7", "gpt-5-6-sol", "gpt-6-astra", "claude-opus-5", "claude-fable-5-1", "claude-sonnet-5", "glm-5-3", "kimi-k3", "gemini-3-8-flash", "grok-4-6"],
1350
+ liveModels: true,
1351
+ defaultModel: "swe-2",
1352
+ modelContextWindows: DEVIN_MODEL_CONTEXT_WINDOWS,
1353
+ },
1354
+ {
1355
+ id: "devin",
1356
+ label: "Cognition (Devin/Windsurf)",
1357
+ adapter: "devin",
1358
+ baseUrl: "https://server.codeium.com",
1359
+ authKind: "oauth",
1360
+ featured: false,
1361
+ dashboardPreset: false,
1362
+ note: "Experimental unofficial Cognition/Devin bridge. ocx login devin opens Auth0 browser sign-in, then exchanges the token via Cognition's RegisterUser for a long-lived API key.",
1363
+ models: ["swe-1-7", "swe-1-7-lightning", "gpt-5-6-sol", "gpt-5-6-luna", "gpt-5-6-terra", "claude-opus-4-8", "claude-fable-5-1", "claude-sonnet-5", "glm-5-2", "kimi-k2-7", "grok-4-5"],
1364
+ liveModels: true,
1365
+ defaultModel: "swe-1-7",
1366
+ modelContextWindows: DEVIN_MODEL_CONTEXT_WINDOWS,
1367
+ },
1268
1368
  {
1269
1369
  id: "xai",
1270
1370
  label: "xAI Grok",
@@ -1434,10 +1534,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1434
1534
  models: ORCAROUTER_MODELS,
1435
1535
  liveModels: true,
1436
1536
  modelDiscovery: ORCAROUTER_MODEL_DISCOVERY,
1437
- noVisionModels: ORCAROUTER_TEXT_ONLY_MODELS,
1438
1537
  modelReasoningEfforts: ORCAROUTER_MODEL_REASONING_EFFORTS,
1439
- modelReasoningEffortMap: ORCAROUTER_MODEL_REASONING_EFFORT_MAP,
1440
- preserveReasoningContentModels: ORCAROUTER_TEXT_ONLY_MODELS,
1441
1538
  note: "Connect your OrcaRouter account with OAuth 2.0 + PKCE; the issued API key is stored in OpenCodex's existing credential store.",
1442
1539
  },
1443
1540
  {
@@ -1751,7 +1848,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1751
1848
  "kimi-k2.7-code-highspeed": [],
1752
1849
  ...Object.fromEntries(OPENCODE_GO_THINKING_TOGGLE_MODELS.map(id => [id, THINKING_TOGGLE_EFFORTS])),
1753
1850
  ...Object.fromEntries(OPENCODE_GO_THINKING_BUDGET_MODELS.map(id => [id, THINKING_BUDGET_EFFORTS])),
1754
- ...Object.fromEntries(DEEPSEEK_THINKING_MODELS.map(id => [id, deepseekThinkingEffortsFor(id)])),
1851
+ ...Object.fromEntries(DEEPSEEK_GATEWAY_THINKING_MODELS.map(id => [id, deepseekThinkingEffortsFor(id)])),
1755
1852
  },
1756
1853
  modelDefaultReasoningEfforts: { "grok-4.6": "high", "kimi-k3": "max" },
1757
1854
  // glm-5.2 uses identity labels now that `max` is a native Codex level (no alias map);
@@ -1759,7 +1856,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1759
1856
  modelReasoningEffortMap: {
1760
1857
  "kimi-k3": KIMI_CODING_K3_REASONING_EFFORT_MAP,
1761
1858
  ...Object.fromEntries(OPENCODE_GO_THINKING_TOGGLE_MODELS.map(id => [id, THINKING_TOGGLE_MAP])),
1762
- ...Object.fromEntries(DEEPSEEK_THINKING_MODELS.map(id => [id, deepseekReasoningMapFor(id)])),
1859
+ ...Object.fromEntries(DEEPSEEK_GATEWAY_THINKING_MODELS.map(id => [id, deepseekReasoningMapFor(id)])),
1763
1860
  },
1764
1861
  modelSupportsReasoningSummaries: {
1765
1862
  "glm-5.3": true,
@@ -1767,17 +1864,24 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1767
1864
  "glm-5.2": true,
1768
1865
  "glm-5.1": true,
1769
1866
  "glm-5": true,
1770
- ...Object.fromEntries(DEEPSEEK_THINKING_MODELS.map(id => [id, true])),
1867
+ ...Object.fromEntries(DEEPSEEK_GATEWAY_THINKING_MODELS.map(id => [id, true])),
1771
1868
  },
1772
1869
  thinkingToggleModels: OPENCODE_GO_THINKING_TOGGLE_MODELS,
1773
- thinkingBudgetModels: THINKING_BUDGET_MODELS,
1870
+ /*
1871
+ * The Go-specific list, not the shared one. The shared `THINKING_BUDGET_MODELS` also
1872
+ * carries Neuralwatt-only ids (`qwen3.5-397b`, `qwen3.6-35b`) that this preset never
1873
+ * gives a ladder to, so a live roster serving one of them armed the thinking-budget
1874
+ * wire path with nothing to advertise: the catalog showed no effort control while the
1875
+ * adapter still translated effort into `thinking_budget`.
1876
+ */
1877
+ thinkingBudgetModels: OPENCODE_GO_THINKING_BUDGET_MODELS,
1774
1878
  noReasoningModels: ["kimi-k2.7-code", "kimi-k2.7-code-highspeed"],
1775
1879
  // Text-only Zen Go models (jawcode metadata) — the vision sidecar describes images for
1776
1880
  // every model listed here (and the catalog advertises image input on their behalf).
1777
1881
  // Kimi K2.7 Code accepts text+image+video: do NOT list it here.
1778
1882
  noVisionModels: [
1779
1883
  "glm-5.3", "glm-5.2", "glm-5", "glm-5.1",
1780
- "deepseek-v4-flash", "deepseek-v4-pro",
1884
+ "deepseek-v4.1-flash", "deepseek-v4-flash",
1781
1885
  "mimo-v2-pro", "mimo-v2.5-pro",
1782
1886
  "minimax-m2.5", "minimax-m2.7",
1783
1887
  "qwen3.7-max",
@@ -1787,7 +1891,17 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1787
1891
  noPenaltyModels: ["kimi-k3", "kimi-k2.7-code", "kimi-k2.7-code-highspeed"],
1788
1892
  autoToolChoiceOnlyModels: ["kimi-k2.7-code", "kimi-k2.7-code-highspeed"],
1789
1893
  // Issue #78: DeepSeek V4 thinking mode requires reasoning_content replay on tool-call turns.
1790
- 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],
1894
+ preserveReasoningContentModels: ["glm-5.3", "glm-5.3-flash", "glm-5.2", "kimi-k3", "kimi-k2.7-code", "kimi-k2.7-code-highspeed", ...DEEPSEEK_GATEWAY_THINKING_MODELS],
1895
+ /*
1896
+ * Issues #1338 / #1415: this gateway answers a `response_format` of type
1897
+ * `json_schema` with HTTP 400 `This response_format type is unavailable now`
1898
+ * (quoted from the upstream body as `Error from provider (Console Go)`), which
1899
+ * breaks every Codex auto-review turn on a DeepSeek route. #1424 shipped the
1900
+ * operator-side opt-out; operators have been applying it by hand ever since.
1901
+ * The reported rejection is type-specific, so this narrower list downgrades the
1902
+ * request to `json_object` instead of claiming the whole field is unavailable.
1903
+ */
1904
+ noJsonSchemaModels: [...DEEPSEEK_GATEWAY_THINKING_MODELS],
1791
1905
  },
1792
1906
  {
1793
1907
  id: "neuralwatt",
@@ -1934,10 +2048,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1934
2048
  modelDiscovery: ORCAROUTER_MODEL_DISCOVERY,
1935
2049
  // Catalog discovery owns WHICH models exist. These entries only retain verified
1936
2050
  // request-shaping facts that the upstream catalog does not currently publish.
1937
- noVisionModels: ORCAROUTER_TEXT_ONLY_MODELS,
1938
2051
  modelReasoningEfforts: ORCAROUTER_MODEL_REASONING_EFFORTS,
1939
- modelReasoningEffortMap: ORCAROUTER_MODEL_REASONING_EFFORT_MAP,
1940
- preserveReasoningContentModels: ORCAROUTER_TEXT_ONLY_MODELS,
1941
2052
  note: "OpenAI-compatible adaptive router. Models and multimodal capabilities are discovered live from the public chat catalog. Use the OrcaRouter account entry for PKCE login.",
1942
2053
  },
1943
2054
  {
@@ -1994,7 +2105,14 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1994
2105
  // 2026-07-10: defaultModel is frozen pending Vertex-specific Tier-2 evidence; Gemini API
1995
2106
  // evidence from ai.google.dev does not establish Vertex publisher availability.
1996
2107
  { 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"] },
1997
- { id: "google-antigravity", alias: "agy", 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.8-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"] },
2108
+ // Antigravity discovers models with a POST to the CCA `:fetchAvailableModels` RPC, which
2109
+ // `buildModelsRequest` already built by hand. Declaring it here changes no request URL — the
2110
+ // relative path resolves to the same destination — but it lets `isRegistryModelDiscoveryUrl`
2111
+ // prove that URL, which is what admits a Clash/Surge/Mihomo TUN fake-IP answer (#4261). The
2112
+ // path must stay RELATIVE: this row sets `allowBaseUrlOverride`, and an absolute `url` would
2113
+ // retarget a user's custom base back to Google. A leading `./` is required because a bare
2114
+ // `v1internal:` reads as a URL scheme and `providerModelDiscoverySpecError` rejects it.
2115
+ { id: "google-antigravity", alias: "agy", 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.8-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"], modelDiscovery: { path: "./v1internal:fetchAvailableModels" } },
1998
2116
  { 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" },
1999
2117
  { 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" },
2000
2118
  { 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" },
@@ -2012,18 +2130,20 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
2012
2130
  // verified 2026-08-08).
2013
2131
  jawcodeBundle: "deepseek",
2014
2132
  // deepseek-chat/deepseek-reasoner were deprecated upstream on 2026-07-24 15:59 UTC;
2015
- // official identifiers are now deepseek-v4-flash / deepseek-v4-pro. They stay in
2133
+ // the current official identifier is deepseek-flash. They stay in
2016
2134
  // the list only as compatibility aliases so existing saved configs and requests
2017
2135
  // keep validating and routing (they previously mapped to v4-flash; devlog
2018
2136
  // _fin/260710_provider_hardening/002_research_cn.md). The current offerings are
2019
2137
  // the V4 ids — defaultModel and the model-specific wiring above use them.
2020
2138
  // deepseek-v4-flash-vision-exp: experimental vision preview (2026-08-21) —
2021
2139
  // expected to merge into deepseek-v4-flash later; see DEEPSEEK_VISION_PREVIEW_MODEL.
2022
- models: ["deepseek-chat", "deepseek-reasoner", ...DEEPSEEK_THINKING_MODELS, DEEPSEEK_VISION_PREVIEW_MODEL],
2023
- defaultModel: "deepseek-v4-flash",
2140
+ models: ["deepseek-chat", "deepseek-reasoner", ...DEEPSEEK_NATIVE_THINKING_MODELS, DEEPSEEK_VISION_PREVIEW_MODEL],
2141
+ // V4.1-Flash is the current first-party offering; `deepseek-v4-flash` now routes there
2142
+ // as a compatibility alias, so a new install should ask for the live id by name.
2143
+ defaultModel: "deepseek-flash",
2024
2144
  // Official DeepSeek Codex setup (codex-deepseek-setup.sh) advertises 1,048,576
2025
2145
  // for both V4 models; the older 1,000,000 figure was a rounded approximation.
2026
- modelContextWindows: { "deepseek-v4-flash": 1_048_576, "deepseek-v4-pro": 1_048_576, [DEEPSEEK_VISION_PREVIEW_MODEL]: 1_048_576 },
2146
+ modelContextWindows: { "deepseek-flash": 1_048_576, "deepseek-v4-flash": 1_048_576, [DEEPSEEK_VISION_PREVIEW_MODEL]: 1_048_576 },
2027
2147
  modelInputModalities: { [DEEPSEEK_VISION_PREVIEW_MODEL]: ["text", "image"] },
2028
2148
  // DeepSeek documents both V4 models as native Responses API models adapted for Codex
2029
2149
  // (model table marks Responses API ✓ for flash and pro; the /responses reference lists
@@ -2037,7 +2157,9 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
2037
2157
  // translating them into Responses would add a hop onto our newest upstream path
2038
2158
  // for no gain.
2039
2159
  "deepseek-v4-flash": { wire: "openai-responses", inbound: ["responses"] },
2040
- "deepseek-v4-pro": { wire: "openai-responses", inbound: ["responses"] },
2160
+ // Same Responses contract as the V4 ids it succeeds; without this row the new
2161
+ // default would fall back to the provider-wide Chat wire.
2162
+ "deepseek-flash": { wire: "openai-responses", inbound: ["responses"] },
2041
2163
  },
2042
2164
  // The #875-era bounded-JSON force (`modelResponsesUpstreamStreaming`) is retired
2043
2165
  // for this entry: the official guide documents a `response.completed` /
@@ -2052,7 +2174,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
2052
2174
  // devlog/_fin/260807_deepseek_responses_streaming/000_plan.md.
2053
2175
  // Current official streams normally carry a real terminal; retain a narrow grace
2054
2176
  // repair for the historical shape that closes after a complete graph without one.
2055
- modelResponsesTerminalRepair: { "deepseek-v4-flash": { graceMs: 5_000 }, "deepseek-v4-pro": { graceMs: 5_000 } },
2177
+ modelResponsesTerminalRepair: { "deepseek-flash": { graceMs: 5_000 }, "deepseek-v4-flash": { graceMs: 5_000 } },
2056
2178
  // DeepSeek's Responses route emits bare UUID item ids, which leave Codex
2057
2179
  // clients stuck on an uncommitted turn (#938). Client-facing only — raw
2058
2180
  // continuation snapshots keep the upstream ids.
@@ -2088,14 +2210,14 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
2088
2210
  - 대안 분석: Globally preserve reasoning_content for all OpenAI-compatible models; preserve it for legacy deepseek-reasoner too; mark only V4 thinking models in registry metadata.
2089
2211
  - 선택 근거: 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.
2090
2212
  */
2091
- modelReasoningEfforts: Object.fromEntries(DEEPSEEK_THINKING_MODELS.map(id => [id, deepseekThinkingEffortsFor(id)])),
2092
- modelReasoningEffortMap: Object.fromEntries(DEEPSEEK_THINKING_MODELS.map(id => [id, deepseekReasoningMapFor(id)])),
2093
- modelSupportsReasoningSummaries: Object.fromEntries(DEEPSEEK_THINKING_MODELS.map(id => [id, true])),
2094
- preserveReasoningContentModels: DEEPSEEK_THINKING_MODELS,
2213
+ modelReasoningEfforts: Object.fromEntries(DEEPSEEK_NATIVE_THINKING_MODELS.map(id => [id, deepseekThinkingEffortsFor(id)])),
2214
+ modelReasoningEffortMap: Object.fromEntries(DEEPSEEK_NATIVE_THINKING_MODELS.map(id => [id, deepseekReasoningMapFor(id)])),
2215
+ modelSupportsReasoningSummaries: Object.fromEntries(DEEPSEEK_NATIVE_THINKING_MODELS.map(id => [id, true])),
2216
+ preserveReasoningContentModels: DEEPSEEK_NATIVE_THINKING_MODELS,
2095
2217
  // Issue #88: every DeepSeek API model is text-only input (no image support upstream) — the
2096
2218
  // vision sidecar describes attached images for them, and the catalog advertises image input
2097
2219
  // on their behalf (same treatment as opencode-go's DeepSeek V4 entries above).
2098
- noVisionModels: ["deepseek-chat", "deepseek-reasoner", ...DEEPSEEK_THINKING_MODELS],
2220
+ noVisionModels: ["deepseek-chat", "deepseek-reasoner", ...DEEPSEEK_NATIVE_THINKING_MODELS],
2099
2221
  },
2100
2222
  // llama-3.3-70b was deprecated by Cerebras on 2026-02-16. Evidence: devlog/_plan/260710_provider_hardening/003_research_aggregators.md.
2101
2223
  { 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" },
@@ -2279,7 +2401,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
2279
2401
  // Official Command Code model-profile reasoning facts (shared with the OAuth
2280
2402
  // `command-code` entry). Without them the API-key preset never advertises a
2281
2403
  // reasoning picker, and the router's known-ids decode source misses the native
2282
- // slash ids — so a Codex-facing slug like `commandcode/deepseek-deepseek-v4-pro`
2404
+ // slash ids — so a Codex-facing slug like `commandcode/deepseek-deepseek-v4-flash`
2283
2405
  // is sent upstream verbatim and rejected with `unsupported_model`.
2284
2406
  modelReasoningEfforts: COMMAND_CODE_MODEL_REASONING_EFFORTS,
2285
2407
  // The DeepSeek vision preview id is preemptive for when the catalog serves it
@@ -2541,19 +2663,43 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
2541
2663
  // exact 131_072 every other source in this repo uses for that model. Coding Plan pricing stays
2542
2664
  // unpublished, so no cost entry is asserted.
2543
2665
  {
2544
- id: "zai", label: "Z.AI — GLM Coding Plan", baseUrl: "https://api.z.ai/api/coding/paas/v4", adapter: "openai-chat", authKind: "key",
2666
+ id: "zai", label: "Z.AI — GLM Coding Plan", baseUrl: "https://api.z.ai", adapter: "openai-responses", authKind: "key",
2667
+ // One subscription and one key, three protocols. docs.z.ai/guides/llm/glm-5.3 lists them:
2668
+ // Chat Completions at /api/coding/paas/v4, Responses at /api/v1, Anthropic Messages at
2669
+ // /api/anthropic. docs.z.ai/devpack/latest-model points Codex-family clients at /api/v1,
2670
+ // and the Chat path is the one that misbehaves in practice.
2671
+ //
2672
+ // Responses is the default and Chat stays reachable per model through `modelAdapters`.
2673
+ // The two wires sit under different prefixes, and a wire override swaps the adapter
2674
+ // without touching baseUrl, so each wire carries its own relative send path.
2675
+ //
2676
+ // Measured 2026-09-12 against a live key: every roster id answers 200 on
2677
+ // /api/v1/responses, and every one also answers 200 on the Chat prefix, so no model
2678
+ // needs a `modelWireDefaults` pin. /api/v1/chat/completions returns 403
2679
+ // model_access_denied, which is why the Chat path cannot simply hang off the new base.
2680
+ responsesPath: "/api/v1/responses",
2681
+ chatCompletionsPath: "/api/coding/paas/v4/chat/completions",
2682
+ // The address this row occupied before the move. A saved custom provider still pointing
2683
+ // at the Chat endpoint keeps receiving this row's metadata (#1100).
2684
+ destinationAliases: [{ baseUrl: "https://api.z.ai/api/coding/paas/v4", adapter: "openai-chat" }],
2545
2685
  dashboardUrl: "https://z.ai/manage-apikey/apikey-list", defaultModel: "glm-5.3",
2546
2686
  note: "GLM-5.3 coding subscription",
2547
2687
  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"],
2548
- 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 },
2549
- // Z.AI's OpenAI path returns 400 code 1211 for bracketed model ids.
2688
+ // The upstream catalog reports 1_048_576 for the 5.3 family, which is what the domestic
2689
+ // Responses row already carries. Both are documented as "1M"; this is that number.
2690
+ modelContextWindows: { "glm-5.3": 1_048_576, "glm-5.3[1m]": 1_048_576, "glm-5.3-flash": 1_048_576, "glm-5.2": 1_000_000, "glm-5.2[1m]": 1_000_000 },
2691
+ // Z.AI returns 400 for bracketed model ids on both wires; the aliases are local.
2550
2692
  modelSuffixBracketStrip: true,
2551
2693
  noVisionModels: ZAI_GLM_5X_SIDECAR_VISION_MODELS,
2694
+ modelInputModalities: ZAI_GLM_5X_INPUT_MODALITIES,
2552
2695
  modelReasoningEfforts: ZAI_GLM_5X_REASONING_EFFORTS,
2553
2696
  modelDefaultReasoningEfforts: Object.fromEntries(ZAI_GLM_53_MODELS.map(id => [id, "max"])),
2554
2697
  modelMaxOutputTokens: Object.fromEntries(ZAI_GLM_53_MODELS.map(id => [id, 131_072])),
2555
2698
  modelSupportsReasoningSummaries: Object.fromEntries(ZAI_GLM_5X_MODELS.map(id => [id, true])),
2556
2699
  preserveReasoningContentModels: ZAI_GLM_5X_MODELS,
2700
+ // Responses replay uses this provider-level flag; the model list above still covers a
2701
+ // caller who opts back into Chat.
2702
+ preserveResponsesReasoningContent: true,
2557
2703
  },
2558
2704
  // Zhipu's domestic BigModel platform: OpenAI-compatible pay-as-you-go on open.bigmodel.cn — a
2559
2705
  // different host and billing product from the `zai` coding-plan subscription above.
@@ -2630,6 +2776,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
2630
2776
  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 },
2631
2777
  modelSuffixBracketStrip: true,
2632
2778
  noVisionModels: ZAI_GLM_5X_SIDECAR_VISION_MODELS,
2779
+ modelInputModalities: ZAI_GLM_5X_INPUT_MODALITIES,
2633
2780
  modelReasoningEfforts: ZAI_GLM_5X_REASONING_EFFORTS,
2634
2781
  modelSupportsReasoningSummaries: Object.fromEntries(ZAI_GLM_5X_MODELS.map(id => [id, true])),
2635
2782
  preserveReasoningContentModels: ZAI_GLM_5X_MODELS,
@@ -2759,13 +2906,11 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
2759
2906
  ),
2760
2907
  thinkingToggleModels: VOLCENGINE_DOUBAO_THINKING_MODELS,
2761
2908
  preserveReasoningContentModels: [
2762
- "deepseek-v4-pro-260425",
2763
2909
  "deepseek-v4-flash-260425",
2764
2910
  "glm-5-2-260617",
2765
2911
  "glm-4-7-251222",
2766
2912
  ],
2767
2913
  noVisionModels: [
2768
- "deepseek-v4-pro-260425",
2769
2914
  "deepseek-v4-flash-260425",
2770
2915
  "deepseek-v3-2-251201",
2771
2916
  "glm-5-2-260617",
@@ -2787,12 +2932,12 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
2787
2932
  modelInputModalities: VOLCENGINE_PLAN_INPUT_MODALITIES,
2788
2933
  noVisionModels: VOLCENGINE_PLAN_TEXT_ONLY_MODELS,
2789
2934
  modelReasoningEfforts: Object.fromEntries(
2790
- DEEPSEEK_THINKING_MODELS.map(id => [id, deepseekThinkingEffortsFor(id)]),
2935
+ DEEPSEEK_V4_LEGACY_MODELS.map(id => [id, deepseekThinkingEffortsFor(id)]),
2791
2936
  ),
2792
2937
  modelReasoningEffortMap: Object.fromEntries(
2793
- DEEPSEEK_THINKING_MODELS.map(id => [id, deepseekReasoningMapFor(id)]),
2938
+ DEEPSEEK_V4_LEGACY_MODELS.map(id => [id, deepseekReasoningMapFor(id)]),
2794
2939
  ),
2795
- preserveReasoningContentModels: DEEPSEEK_THINKING_MODELS,
2940
+ preserveReasoningContentModels: DEEPSEEK_V4_LEGACY_MODELS,
2796
2941
  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.",
2797
2942
  },
2798
2943
  {
@@ -2806,7 +2951,9 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
2806
2951
  supportsServiceTier: false,
2807
2952
  preserveCustomDestination: true,
2808
2953
  dashboardUrl: "https://console.volcengine.com/ark/region:ark+cn-beijing/overview",
2809
- defaultModel: "deepseek-v4-pro",
2954
+ // Was `deepseek-v4-pro` until DeepSeek retired it; the plan roster's other DeepSeek
2955
+ // entry takes over so a fresh install still lands on a working default.
2956
+ defaultModel: "deepseek-v4-flash",
2810
2957
  models: VOLCENGINE_AGENT_PLAN_MODELS,
2811
2958
  liveModels: false,
2812
2959
  modelInputModalities: VOLCENGINE_PLAN_INPUT_MODALITIES,
@@ -2831,7 +2978,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
2831
2978
  modelInputModalities: ALIBABA_TOKEN_PLAN_INPUT_MODALITIES,
2832
2979
  modelContextWindows: {
2833
2980
  "qwen3.8-max": 983_616, "qwen3.7-max": 1_000_000, "qwen3.7-plus": 1_000_000,
2834
- "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,
2981
+ "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,
2835
2982
  },
2836
2983
  modelReasoningEfforts: {
2837
2984
  ...Object.fromEntries(ALIBABA_TOKEN_PLAN_QWEN_MODELS.map(id => [id, THINKING_BUDGET_EFFORTS])),
@@ -2839,14 +2986,12 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
2839
2986
  "glm-5.3": ZAI_GLM_53_REASONING_EFFORTS,
2840
2987
  "glm-5.3-flash": ZAI_GLM_53_REASONING_EFFORTS,
2841
2988
  "glm-5.2": ZAI_GLM_52_REASONING_EFFORTS,
2842
- "deepseek-v4-pro": deepseekThinkingEffortsFor("deepseek-v4-pro"),
2843
2989
  },
2844
2990
  modelDefaultReasoningEfforts: { "qwen3.8-max": "xhigh" },
2845
- modelReasoningEffortMap: { "deepseek-v4-pro": deepseekReasoningMapFor("deepseek-v4-pro") },
2846
2991
  directReasoningEffortModels: ["qwen3.8-max"],
2847
2992
  thinkingBudgetModels: ALIBABA_TOKEN_PLAN_QWEN_MODELS.filter(id => id !== "qwen3.8-max"),
2848
- 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"],
2849
- noVisionModels: ["glm-5.3", "glm-5.2", "deepseek-v4-pro"],
2993
+ preserveReasoningContentModels: ["glm-5.3", "glm-5.3-flash", "glm-5.2", "qwen3.8-max", "qwen3.7-max", "qwen3.7-plus", "qwen3.6-flash"],
2994
+ noVisionModels: ["glm-5.3", "glm-5.2"],
2850
2995
  },
2851
2996
  {
2852
2997
  id: "alibaba-token-plan-intl",
@@ -2866,7 +3011,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
2866
3011
  modelContextWindows: {
2867
3012
  "qwen3.8-max": 983_616,
2868
3013
  "qwen3.7-max": 1_000_000, "qwen3.7-plus": 1_000_000, "qwen3.6-plus": 1_000_000, "qwen3.6-flash": 1_000_000,
2869
- "deepseek-v4-pro": 1_000_000, "deepseek-v4-flash": 1_000_000, "deepseek-v3.2": 131_072,
3014
+ "deepseek-v4-flash": 1_000_000, "deepseek-v3.2": 131_072,
2870
3015
  "kimi-k2.7-code": 262_144, "kimi-k2.6": 262_144, "kimi-k2.5": 262_144,
2871
3016
  "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,
2872
3017
  "MiniMax-M2.5": 204_800,
@@ -2877,17 +3022,15 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
2877
3022
  "glm-5.3": ZAI_GLM_53_REASONING_EFFORTS,
2878
3023
  "glm-5.3-flash": ZAI_GLM_53_REASONING_EFFORTS,
2879
3024
  "glm-5.2": ZAI_GLM_52_REASONING_EFFORTS,
2880
- "deepseek-v4-pro": deepseekThinkingEffortsFor("deepseek-v4-pro"),
2881
3025
  "deepseek-v4-flash": deepseekThinkingEffortsFor("deepseek-v4-flash"),
2882
3026
  },
2883
3027
  modelReasoningEffortMap: {
2884
- "deepseek-v4-pro": deepseekReasoningMapFor("deepseek-v4-pro"),
2885
3028
  "deepseek-v4-flash": deepseekReasoningMapFor("deepseek-v4-flash"),
2886
3029
  },
2887
3030
  directReasoningEffortModels: ["qwen3.8-max"],
2888
3031
  thinkingBudgetModels: ALIBABA_INTL_TOKEN_PLAN_QWEN_MODELS.filter(id => id !== "qwen3.8-max"),
2889
- 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"],
2890
- noVisionModels: ["deepseek-v4-pro", "deepseek-v4-flash", "deepseek-v3.2", "glm-5.3", "glm-5.2", "glm-5.1", "glm-5", "MiniMax-M2.5"],
3032
+ preserveReasoningContentModels: ["glm-5.3", "glm-5.3-flash", "glm-5.2", "deepseek-v4-flash", "qwen3.8-max", "qwen3.7-max", "qwen3.7-plus", "qwen3.6-plus", "qwen3.6-flash"],
3033
+ noVisionModels: ["deepseek-v4-flash", "deepseek-v3.2", "glm-5.3", "glm-5.2", "glm-5.1", "glm-5", "MiniMax-M2.5"],
2891
3034
  noReasoningModels: ["kimi-k2.7-code", "kimi-k2.6", "kimi-k2.5", "deepseek-v3.2", "glm-5.1", "glm-5", "MiniMax-M2.5"],
2892
3035
  modelDefaultReasoningEfforts: { "qwen3.8-max": "xhigh" },
2893
3036
  },
@@ -2925,7 +3068,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
2925
3068
  authKind: "key",
2926
3069
  dashboardUrl: "https://ollama.com/settings/keys",
2927
3070
  // Live IDs verified 2026-07-10; qwen3-coder:480b retires 2026-07-15.
2928
- 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"],
3071
+ models: ["glm-5.3", "glm-5.3-flash", "glm-5.2", "qwen3-coder:480b", "gpt-oss:120b", "kimi-k2.6", "minimax-m3", "qwen3.5:397b", "gemma4:31b"],
2929
3072
  defaultModel: "glm-5.3",
2930
3073
  // Owner-audited exact outage fallback: these current Ollama Cloud GLM-5.3 rows have
2931
3074
  // 1,048,576-token context windows. Live discovery and successful /api/show enrichment keep
@@ -2937,7 +3080,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
2937
3080
  "glm-5.3", "glm-5.2", "glm-5.1", "glm-5", "glm-4.7",
2938
3081
  "minimax-m2.7", "minimax-m2.5", "minimax-m2.1",
2939
3082
  "nemotron-3-ultra", "nemotron-3-super",
2940
- "deepseek-v4-pro", "deepseek-v4-flash",
3083
+ "deepseek-v4-flash",
2941
3084
  "gpt-oss", "qwen3-coder:480b",
2942
3085
  ],
2943
3086
  // Ollama's native chat API has no `text.verbosity` equivalent and the ollama-native adapter
@@ -3021,12 +3164,12 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
3021
3164
  // Zen DeepSeek thinking models — never serialize a bare tool-call turn.
3022
3165
  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.",
3023
3166
  modelReasoningEfforts: Object.fromEntries(
3024
- [...DEEPSEEK_THINKING_MODELS, ...OPENCODE_FREE_DEEPSEEK_MODELS].map(id => [id, deepseekThinkingEffortsFor(id)]),
3167
+ [...DEEPSEEK_GATEWAY_THINKING_MODELS, ...OPENCODE_FREE_DEEPSEEK_MODELS].map(id => [id, deepseekThinkingEffortsFor(id)]),
3025
3168
  ),
3026
3169
  modelReasoningEffortMap: Object.fromEntries(
3027
- [...DEEPSEEK_THINKING_MODELS, ...OPENCODE_FREE_DEEPSEEK_MODELS].map(id => [id, deepseekReasoningMapFor(id)]),
3170
+ [...DEEPSEEK_GATEWAY_THINKING_MODELS, ...OPENCODE_FREE_DEEPSEEK_MODELS].map(id => [id, deepseekReasoningMapFor(id)]),
3028
3171
  ),
3029
- preserveReasoningContentModels: [...DEEPSEEK_THINKING_MODELS, ...OPENCODE_FREE_DEEPSEEK_MODELS],
3172
+ preserveReasoningContentModels: [...DEEPSEEK_GATEWAY_THINKING_MODELS, ...OPENCODE_FREE_DEEPSEEK_MODELS],
3030
3173
  // Same Zen gateway as opencode-free: the DeepSeek vision preview id
3031
3174
  // (merges into deepseek-v4-flash later).
3032
3175
  modelContextWindows: {
@@ -3035,7 +3178,10 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
3035
3178
  modelInputModalities: {
3036
3179
  [DEEPSEEK_VISION_PREVIEW_MODEL]: ["text", "image"],
3037
3180
  },
3038
- noVisionModels: [...OPENCODE_ZEN_TEXT_ONLY_MODELS, ...DEEPSEEK_THINKING_MODELS],
3181
+ noVisionModels: [...OPENCODE_ZEN_TEXT_ONLY_MODELS, ...DEEPSEEK_GATEWAY_THINKING_MODELS],
3182
+ // Same DeepSeek routes as the Go preset above, behind the same vendor, so they carry
3183
+ // the same json_schema rejection (#1338 / #1415).
3184
+ noJsonSchemaModels: [...DEEPSEEK_GATEWAY_THINKING_MODELS, ...OPENCODE_FREE_DEEPSEEK_MODELS],
3039
3185
  },
3040
3186
  { 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" },
3041
3187
  {
@@ -3076,6 +3222,10 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
3076
3222
  // Same Zen roster behind the same base URL, so it carries the same measured
3077
3223
  // text-only list rather than only its DeepSeek member (#1043).
3078
3224
  noVisionModels: OPENCODE_ZEN_TEXT_ONLY_MODELS,
3225
+ // Same reasoning: the free tier is the same Zen roster, so its DeepSeek members get
3226
+ // the keyed tier's json_schema treatment and its reasoning contract rather than a
3227
+ // narrower table that silently falls behind whenever the keyed one is updated.
3228
+ noJsonSchemaModels: [...DEEPSEEK_GATEWAY_THINKING_MODELS, ...OPENCODE_FREE_DEEPSEEK_MODELS],
3079
3229
  },
3080
3230
  { id: "xiaomi", label: "Xiaomi MiMo", baseUrl: "https://api.xiaomimimo.com/anthropic", adapter: "anthropic", authKind: "key", dashboardUrl: "https://xiaomimimo.com", defaultModel: "mimo-v2.5-pro" },
3081
3231
  // Xiaomi's public OpenAI-compatible endpoint is a distinct transport from both the Anthropic
@@ -3417,12 +3567,22 @@ export function registryEntryForProviderDestination(
3417
3567
  if (typeof provider.baseUrl !== "string" || !provider.baseUrl) return undefined;
3418
3568
  if (provider.authMode !== undefined && provider.authMode !== "key") return undefined;
3419
3569
  const endpoint = normalizedProviderEndpoint(provider.baseUrl);
3420
- return PROVIDER_REGISTRY.find(entry =>
3570
+ const eligible = (entry: ProviderRegistryEntry): boolean =>
3421
3571
  entry.authKind === "key"
3422
3572
  && !entry.allowBaseUrlOverride
3423
- && !/\{[^}]*\}/.test(entry.baseUrl)
3573
+ && !/\{[^}]*\}/.test(entry.baseUrl);
3574
+ const direct = PROVIDER_REGISTRY.find(entry =>
3575
+ eligible(entry)
3424
3576
  && entry.adapter === provider.adapter
3425
3577
  && normalizedProviderEndpoint(entry.baseUrl) === endpoint);
3578
+ if (direct) return direct;
3579
+ // A row that moved keeps answering for the address it used to occupy, so an existing
3580
+ // custom provider written against the old endpoint does not silently lose its metadata.
3581
+ return PROVIDER_REGISTRY.find(entry =>
3582
+ eligible(entry)
3583
+ && (entry.destinationAliases ?? []).some(alias =>
3584
+ alias.adapter === provider.adapter
3585
+ && normalizedProviderEndpoint(alias.baseUrl) === endpoint));
3426
3586
  }
3427
3587
 
3428
3588
  /**