@yansigit/opencodex 2.33.1 → 2.35.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. package/README.md +3 -3
  2. package/gui/dist/assets/index-BjCaHxdz.js +112 -0
  3. package/gui/dist/assets/index-DLkXOXLC.css +1 -0
  4. package/gui/dist/index.html +2 -2
  5. package/package.json +1 -3
  6. package/src/adapters/anthropic.ts +79 -2
  7. package/src/adapters/command-code.ts +40 -3
  8. package/src/adapters/cursor/call-id.ts +44 -0
  9. package/src/adapters/cursor/checkpoint-store.ts +15 -10
  10. package/src/adapters/cursor/discovery.ts +60 -2
  11. package/src/adapters/cursor/effort-map.ts +79 -1
  12. package/src/adapters/cursor/envelope-echo.ts +9 -9
  13. package/src/adapters/cursor/live-models.ts +7 -2
  14. package/src/adapters/cursor/live-transport.ts +14 -0
  15. package/src/adapters/cursor/message-mapper.ts +4 -1
  16. package/src/adapters/cursor/native-exec-shell.ts +103 -6
  17. package/src/adapters/cursor/native-exec.ts +20 -0
  18. package/src/adapters/cursor/protobuf-request.ts +74 -39
  19. package/src/adapters/cursor/request-builder.ts +32 -10
  20. package/src/adapters/cursor/tool-definitions.ts +13 -10
  21. package/src/adapters/cursor/tool-result-normalize.ts +35 -3
  22. package/src/adapters/cursor/types.ts +16 -4
  23. package/src/adapters/cursor.ts +97 -70
  24. package/src/adapters/google-antigravity-replay.ts +105 -25
  25. package/src/adapters/google-antigravity-wire.ts +5 -0
  26. package/src/adapters/google-errors.ts +41 -12
  27. package/src/adapters/google-http.ts +12 -11
  28. package/src/adapters/google.ts +156 -65
  29. package/src/adapters/image.ts +1 -1
  30. package/src/adapters/kiro-constants.ts +15 -0
  31. package/src/adapters/kiro-tools.ts +43 -15
  32. package/src/adapters/kiro.ts +54 -9
  33. package/src/adapters/openai-chat.ts +286 -242
  34. package/src/adapters/openai-responses.ts +334 -24
  35. package/src/adapters/run-turn-queue.ts +36 -1
  36. package/src/adapters/tool-catalog-nudge.ts +2 -2
  37. package/src/adapters/xai-tool-schema.ts +436 -0
  38. package/src/bridge.ts +67 -26
  39. package/src/chat/inbound.ts +14 -1
  40. package/src/chat/outbound.ts +15 -7
  41. package/src/claude/agents-inject.ts +8 -1
  42. package/src/claude/outbound.ts +10 -8
  43. package/src/cli/account-api.ts +27 -7
  44. package/src/cli/account-extended.ts +10 -3
  45. package/src/cli/account.ts +29 -5
  46. package/src/cli/alias.ts +66 -0
  47. package/src/cli/claude.ts +26 -1
  48. package/src/cli/dispatch.ts +13 -1
  49. package/src/cli/help.ts +1 -0
  50. package/src/cli/index.ts +5 -0
  51. package/src/cli/init.ts +1 -0
  52. package/src/cli/models-runtime.ts +95 -0
  53. package/src/cli/models.ts +13 -7
  54. package/src/cli/provider-runtime.ts +16 -2
  55. package/src/cli/registry.ts +6 -1
  56. package/src/cli/telemetry-commands.ts +25 -0
  57. package/src/cli/v2.ts +34 -10
  58. package/src/codex/account-pause.ts +2 -1
  59. package/src/codex/account-priority.ts +3 -2
  60. package/src/codex/app-server-processes.ts +80 -6
  61. package/src/codex/auth-api.ts +48 -8
  62. package/src/codex/auth-context.ts +21 -18
  63. package/src/codex/catalog/aggregation.ts +6 -0
  64. package/src/codex/catalog/model-metadata.ts +13 -1
  65. package/src/codex/catalog/native-models.ts +5 -2
  66. package/src/codex/catalog/parsing.ts +16 -0
  67. package/src/codex/catalog/provider-fetch.ts +20 -37
  68. package/src/codex/catalog/sync.ts +127 -2
  69. package/src/codex/catalog.ts +1 -1
  70. package/src/codex/codex-write-lock.ts +3 -1
  71. package/src/codex/convergence-types.ts +1 -1
  72. package/src/codex/convergence.ts +22 -2
  73. package/src/codex/desired-state.ts +2 -2
  74. package/src/codex/desktop-app-restart.ts +18 -5
  75. package/src/codex/inject-coordination.ts +83 -0
  76. package/src/codex/inject.ts +14 -1
  77. package/src/codex/log-guard/inspect.ts +22 -4
  78. package/src/codex/model-entitlements.ts +9 -2
  79. package/src/codex/prompt-layers.ts +371 -25
  80. package/src/codex/prompt-text-probe.ts +238 -0
  81. package/src/codex/quota.ts +123 -18
  82. package/src/codex/routing.ts +9 -0
  83. package/src/codex/subagent-model-fallback.ts +198 -27
  84. package/src/codex/transition-state.ts +107 -8
  85. package/src/combos/types.ts +10 -0
  86. package/src/compatibility/openai-responses.ts +33 -1
  87. package/src/config/autonomous-remediation.ts +21 -0
  88. package/src/config/provider-validation.ts +14 -0
  89. package/src/config/rebase-provenance.ts +68 -0
  90. package/src/config.ts +191 -17
  91. package/src/generated/compatibility-version.json +229 -153
  92. package/src/images/loop.ts +5 -4
  93. package/src/lab/conformance/fixtures/protocol-v1-cases.json +1 -1
  94. package/src/lab/fabric/producer-child.ts +1 -1
  95. package/src/lib/config-ownership.ts +20 -0
  96. package/src/lib/errors.ts +11 -2
  97. package/src/lib/package-tree-integrity.ts +101 -0
  98. package/src/oauth/aistudio-credentials.ts +65 -0
  99. package/src/oauth/aistudio-native-daemon.ts +54 -0
  100. package/src/oauth/generic-account-failover.ts +231 -0
  101. package/src/oauth/index.ts +57 -5
  102. package/src/oauth/key-providers.ts +10 -1
  103. package/src/oauth/kiro.ts +45 -0
  104. package/src/oauth/login-cli.ts +19 -20
  105. package/src/oauth/types.ts +15 -0
  106. package/src/providers/codex-capacity.ts +5 -2
  107. package/src/providers/command-code-efforts.ts +38 -6
  108. package/src/providers/context-cap.ts +4 -3
  109. package/src/providers/default-aliases.ts +65 -0
  110. package/src/providers/derive.ts +28 -0
  111. package/src/providers/fastwire.ts +7 -1
  112. package/src/providers/model-presets.ts +119 -0
  113. package/src/providers/new-model-policy.ts +146 -0
  114. package/src/providers/provider-id-rewrite.ts +2 -1
  115. package/src/providers/quota.ts +73 -14
  116. package/src/providers/registry.ts +160 -68
  117. package/src/providers/slug-codec.ts +52 -0
  118. package/src/responses/code-mode-helper-compat.ts +50 -0
  119. package/src/responses/custom-tool-compat.ts +34 -10
  120. package/src/responses/parser.ts +4 -0
  121. package/src/responses/schema.ts +5 -1
  122. package/src/responses/thought-signature-replay.ts +17 -0
  123. package/src/router.ts +40 -2
  124. package/src/routing/analytics.ts +1 -0
  125. package/src/routing/quota.ts +10 -0
  126. package/src/server/auth-cors.ts +23 -0
  127. package/src/server/chat-completions.ts +24 -16
  128. package/src/server/chat-native-sse.ts +3 -3
  129. package/src/server/chat-native.ts +30 -11
  130. package/src/server/claude-messages.ts +1 -1
  131. package/src/server/effort-policy.ts +16 -0
  132. package/src/server/index.ts +152 -80
  133. package/src/server/lifecycle.ts +52 -1
  134. package/src/server/management/agent-settings-routes.ts +31 -15
  135. package/src/server/management/codex-prompt-routes.ts +570 -0
  136. package/src/server/management/combo-routes.ts +2 -1
  137. package/src/server/management/config-routes.ts +27 -9
  138. package/src/server/management/context.ts +9 -0
  139. package/src/server/management/logs-usage-routes.ts +1 -1
  140. package/src/server/management/model-routes.ts +266 -0
  141. package/src/server/management/provider-routes.ts +137 -3
  142. package/src/server/management/routing-profile-routes.ts +2 -2
  143. package/src/server/management-api.ts +2 -0
  144. package/src/server/relay-eager.ts +147 -20
  145. package/src/server/relay.ts +251 -19
  146. package/src/server/request-log-conversation.ts +21 -0
  147. package/src/server/request-log.ts +46 -20
  148. package/src/server/responses/collaboration.ts +42 -5
  149. package/src/server/responses/combo-stream-preflight.ts +10 -3
  150. package/src/server/responses/core.ts +571 -137
  151. package/src/server/responses/empty-completion-guard.ts +35 -0
  152. package/src/server/responses/fetch-helpers.ts +14 -6
  153. package/src/server/responses/input-admission.ts +3 -1
  154. package/src/server/responses/passthrough-error.ts +33 -9
  155. package/src/server/responses/responses-field-backfill.ts +105 -13
  156. package/src/server/responses/ws-upstream.ts +35 -5
  157. package/src/server/responses-custom-tool-repair.ts +52 -7
  158. package/src/server/responses-terminal-repair.ts +25 -4
  159. package/src/server/sse-frame-buffer.ts +31 -4
  160. package/src/server/ws-bridge.ts +15 -4
  161. package/src/telemetry/dispatcher.ts +44 -0
  162. package/src/telemetry/fingerprint.ts +24 -0
  163. package/src/telemetry/hook.ts +43 -0
  164. package/src/telemetry/ledger.ts +54 -0
  165. package/src/telemetry/types.ts +23 -0
  166. package/src/types/config.ts +66 -14
  167. package/src/types/provider.ts +77 -0
  168. package/src/types/request.ts +16 -10
  169. package/src/types/tools.ts +20 -25
  170. package/src/types.ts +1 -0
  171. package/src/usage/expected-prices.ts +43 -24
  172. package/src/usage/log.ts +17 -7
  173. package/src/web-search/exa-executor.ts +40 -9
  174. package/src/web-search/loop.ts +5 -4
  175. package/gui/dist/assets/index-CIDo4y4k.js +0 -102
  176. package/gui/dist/assets/index-DrSQdTRd.css +0 -1
  177. package/src/server/aistudio-ws-hub.ts +0 -295
@@ -251,6 +251,18 @@ export interface ProviderRegistryEntry {
251
251
  preserveResponsesReasoningContent?: boolean;
252
252
  /** Registry defaults for per-model Codex reasoning propagation; explicit user keys win during enrichment. */
253
253
  modelSupportsReasoningSummaries?: Record<string, boolean>;
254
+ /** Registry defaults for per-model Codex Responses verbosity support. */
255
+ modelSupportsVerbosity?: Record<string, boolean>;
256
+ /**
257
+ * Registry default applied to EVERY model of this provider, including ids that arrive from
258
+ * live discovery after this table was written.
259
+ *
260
+ * `modelSupportsVerbosity` only covers the ids enumerated here, so a newly discovered model
261
+ * fell through and re-advertised a control the upstream accepts and ignores. Where the opt-out
262
+ * is a property of the provider's API rather than of one model, declare it here; a per-model
263
+ * entry still wins over it.
264
+ */
265
+ supportsVerbosity?: boolean;
254
266
  modelDiscovery?: ProviderModelDiscoverySpec;
255
267
  contextWindow?: number;
256
268
  modelContextWindows?: Record<string, number>;
@@ -329,9 +341,31 @@ const ANTHROPIC_MODEL_CONTEXT_WINDOWS: Record<string, number> = { "claude-sonnet
329
341
  // The non-Z.AI providers below are speculative on purpose: they carry 5.2 today and are
330
342
  // expected to pick 5.3 up on their usual lag. Providers whose live /v1/models discovery is
331
343
  // enabled self-correct on the next successful fetch; static ones need a follow-up refresh.
332
- const ZAI_GLM_53_MODELS = ["glm-5.3", "glm-5.3[1m]"];
344
+ // Every 5.3 family member, so the effort ladder, the default effort and the output
345
+ // cap are derived in ONE place. `glm-5.3-flash` was seeded into the model list and
346
+ // the context map by hand and left out of this constant, which meant it advertised
347
+ // a 1M context with a null effort ladder, no default effort and no output cap while
348
+ // its siblings carried three tiers, a `max` default and 131072 tokens. A member
349
+ // added to the list but not to the family is a model whose metadata silently
350
+ // disappears.
351
+ const ZAI_GLM_53_MODELS = ["glm-5.3", "glm-5.3[1m]", "glm-5.3-flash"];
333
352
  const ZAI_GLM_52_MODELS = ["glm-5.2", "glm-5.2[1m]"];
334
353
  const ZAI_GLM_5X_MODELS = [...ZAI_GLM_53_MODELS, ...ZAI_GLM_52_MODELS];
354
+ /**
355
+ * The 5.x rows whose images the PROXY has to describe, which is NOT the same set as
356
+ * the 5.x rows themselves.
357
+ *
358
+ * `glm-5.3-flash` is a native VLM (docs.z.ai/guides/vlm/glm-5.3-flash), so listing it
359
+ * in `noVisionModels` sent an image through the vision sidecar and handed the model a
360
+ * text description of a picture it could have read itself - no error, worse answer,
361
+ * extra call. The correction commit fixed the Alibaba entries and left the eight
362
+ * providers that reach this constant behind.
363
+ *
364
+ * Kept separate from ZAI_GLM_5X_MODELS rather than filtered at each use site: that
365
+ * constant also drives `modelSupportsReasoningSummaries` and
366
+ * `preserveReasoningContentModels`, where flash DOES belong.
367
+ */
368
+ const ZAI_GLM_5X_SIDECAR_VISION_MODELS = ZAI_GLM_5X_MODELS.filter(id => id !== "glm-5.3-flash");
335
369
  const ZAI_GLM_52_REASONING_EFFORTS = ["low", "medium", "high", "xhigh", "max"];
336
370
  /**
337
371
  * GLM-5.3 does NOT share 5.2's five-tier ladder. docs.z.ai/devpack/latest-model folds every
@@ -417,6 +451,15 @@ const OPENAI_DAYBREAK_REASONING_EFFORTS: Record<string, string[]> = Object.fromE
417
451
  OPENAI_DAYBREAK_MODELS.map(id => [id, [] as string[]]),
418
452
  );
419
453
  const OPENROUTER_GPT56_MODELS = OPENAI_GPT56_MODELS.map(id => `openai/${id}`);
454
+ const XAI_MODELS = [
455
+ "grok-4.6",
456
+ "grok-4.5",
457
+ "grok-4.3",
458
+ "grok-4.20-0309-reasoning",
459
+ "grok-4.20-0309-non-reasoning",
460
+ "grok-build-0.1",
461
+ "grok-composer-2.5-fast",
462
+ ];
420
463
  // OpenRouter's live /endpoints routes report 1,050,000; keep this separate from the
421
464
  // unverified OpenAI API-key seed. Evidence: devlog/_plan/260710_provider_hardening/003_research_aggregators.md.
422
465
  const OPENROUTER_GPT56_CONTEXT_WINDOW = 1_050_000;
@@ -451,13 +494,15 @@ const OPENCODE_GO_THINKING_TOGGLE_MODELS = [
451
494
  * images through the proxy's vision sidecar (src/codex/catalog/provider-fetch.ts), a claim nobody
452
495
  * has verified for BigModel-hosted GLM.
453
496
  */
497
+ // `glm-5.3-flash` is deliberately absent: it is a native VLM
498
+ // (docs.z.ai/guides/vlm/glm-5.3-flash), unlike glm-5.3 itself.
454
499
  const ZHIPU_BIGMODEL_TEXT_MODELS = ["glm-4.6", "glm-4.7", "glm-4.7-flash", "glm-5", "glm-5.1", "glm-5.2", "glm-5.3"];
455
500
  const ZHIPU_BIGMODEL_MODELS = [...ZHIPU_BIGMODEL_TEXT_MODELS, "glm-4.6v"];
456
501
  const ZHIPU_BIGMODEL_INPUT_MODALITIES: Record<string, string[]> = {
457
502
  ...Object.fromEntries(ZHIPU_BIGMODEL_TEXT_MODELS.map(id => [id, ["text"]])),
458
503
  "glm-4.6v": ["text", "image"],
459
504
  };
460
- const ZHIPU_BIGMODEL_THINKING_TOGGLE_MODELS = ["glm-4.6", "glm-4.7", "glm-5", "glm-5.1", "glm-5.2", "glm-5.3"];
505
+ const ZHIPU_BIGMODEL_THINKING_TOGGLE_MODELS = ["glm-4.6", "glm-4.7", "glm-5", "glm-5.1", "glm-5.2", "glm-5.3", "glm-5.3-flash"];
461
506
  const THINKING_BUDGET_EFFORTS = ["low", "medium", "high", "xhigh", "max"];
462
507
  // Qwen3.8-Max is the first Qwen3.x model with official direct `reasoning_effort` support.
463
508
  // Evidence: https://qwen.ai/blog?id=qwen3.8
@@ -475,16 +520,27 @@ const DEEPSEEK_THINKING_MODELS = ["deepseek-v4-pro", "deepseek-v4-flash"];
475
520
  * at which point this id retires the same way deepseek-chat/reasoner did.
476
521
  */
477
522
  const DEEPSEEK_VISION_PREVIEW_MODEL = "deepseek-v4-flash-vision-exp";
478
- const OPENCODE_FREE_DEEPSEEK_MODELS = ["deepseek-v4-flash-free"];
479
- /*
480
- * OpenCode Zen's free slug for the OpenRouter stealth model "Ox Alpha"
481
- * (openrouter.ai/stealth/ox-alpha): 1,048,576-token context, multimodal
482
- * (text+image+video upstream; Zen serves text+image), mandatory reasoning,
483
- * free during the stealth window. Zen displays it as "Ox Alpha Free" under
484
- * this exact id (opencode.ai/docs/zen, verified 2026-08-21).
523
+ /**
524
+ * CommandCode routes verified to accept image input end-to-end (#2406).
525
+ *
526
+ * Verified-negative and therefore deliberately ABSENT: deepseek/deepseek-v4-flash,
527
+ * deepseek/deepseek-v4-pro, zai-org/GLM-5.2, zai-org/GLM-5.3, xai/grok-4.6. Those
528
+ * routes accept the request and drop the image, which is worse than declining it — the
529
+ * model answers about an image it never saw. Do not add an id here on family resemblance;
530
+ * capability intersection trusts this map.
485
531
  */
486
- const OPENCODE_OX_ALPHA_FREE_MODEL = "x-preview-f-free";
487
- const OX_ALPHA_CONTEXT_WINDOW = 1_048_576;
532
+ const COMMAND_CODE_IMAGE_MODELS = [
533
+ `deepseek/${DEEPSEEK_VISION_PREVIEW_MODEL}`,
534
+ "gpt-5.6-luna",
535
+ "gpt-5.6-sol",
536
+ "MiniMaxAI/MiniMax-M3",
537
+ "moonshotai/Kimi-K3",
538
+ "meta/muse-spark-1.2",
539
+ "meta/muse-spark-1.2-contributor",
540
+ ] as const;
541
+ const COMMAND_CODE_MODEL_INPUT_MODALITIES: Record<string, ["text", "image"]> =
542
+ Object.fromEntries(COMMAND_CODE_IMAGE_MODELS.map(id => [id, ["text", "image"]]));
543
+ const OPENCODE_FREE_DEEPSEEK_MODELS = ["deepseek-v4-flash-free"];
488
544
  /*
489
545
  * Zen free models that reject `image_url` upstream (#1043, and the reproducible
490
546
  * half of #1024).
@@ -571,7 +627,7 @@ const deepseekReasoningMapFor = (modelId: string): Record<string, string> =>
571
627
  // https://help.aliyun.com/en/model-studio/token-plan-quickstart
572
628
  const ALIBABA_TOKEN_PLAN_MODELS = [
573
629
  "qwen3.8-max", "qwen3.7-max", "qwen3.7-plus", "qwen3.6-flash",
574
- "glm-5.3", "glm-5.2", "deepseek-v4-pro",
630
+ "glm-5.3", "glm-5.3-flash", "glm-5.2", "deepseek-v4-pro",
575
631
  ];
576
632
  const ALIBABA_TOKEN_PLAN_QWEN_MODELS = [
577
633
  "qwen3.8-max", "qwen3.7-max", "qwen3.7-plus", "qwen3.6-flash",
@@ -582,6 +638,7 @@ const ALIBABA_TOKEN_PLAN_INPUT_MODALITIES: Record<string, string[]> = {
582
638
  "qwen3.7-plus": ["text", "image"],
583
639
  "qwen3.6-flash": ["text", "image"],
584
640
  "glm-5.3": ["text"],
641
+ "glm-5.3-flash": ["text", "image"],
585
642
  "glm-5.2": ["text"],
586
643
  "deepseek-v4-pro": ["text"],
587
644
  };
@@ -594,7 +651,7 @@ const ALIBABA_INTL_TOKEN_PLAN_MODELS = [
594
651
  "qwen3.8-max", "qwen3.7-max", "qwen3.7-plus", "qwen3.6-plus", "qwen3.6-flash",
595
652
  "deepseek-v4-pro", "deepseek-v4-flash", "deepseek-v3.2",
596
653
  "kimi-k2.7-code", "kimi-k2.6", "kimi-k2.5",
597
- "glm-5.3", "glm-5.2", "glm-5.1", "glm-5",
654
+ "glm-5.3", "glm-5.3-flash", "glm-5.2", "glm-5.1", "glm-5",
598
655
  "MiniMax-M2.5",
599
656
  ];
600
657
  const ALIBABA_INTL_TOKEN_PLAN_QWEN_MODELS = [
@@ -644,6 +701,7 @@ const VOLCENGINE_CODING_PLAN_MODELS = [
644
701
  "deepseek-v4-pro",
645
702
  "deepseek-v4-flash",
646
703
  "glm-5.3",
704
+ "glm-5.3-flash",
647
705
  "glm-5.2",
648
706
  "kimi-k2.6",
649
707
  "minimax-m3",
@@ -652,6 +710,7 @@ const VOLCENGINE_AGENT_PLAN_MODELS = [
652
710
  "deepseek-v4-pro",
653
711
  "deepseek-v4-flash",
654
712
  "glm-5.3",
713
+ "glm-5.3-flash",
655
714
  "glm-5.2",
656
715
  "kimi-k2.6",
657
716
  "minimax-m3",
@@ -660,6 +719,9 @@ const VOLCENGINE_AGENT_PLAN_MODELS = [
660
719
  const VOLCENGINE_PLAN_INPUT_MODALITIES: Record<string, string[]> = {
661
720
  "kimi-k2.6": ["text", "image"],
662
721
  "minimax-m3": ["text", "image"],
722
+ // Native VLM (docs.z.ai/guides/vlm/glm-5.3-flash), so it is declared here and left
723
+ // out of the text-only list below.
724
+ "glm-5.3-flash": ["text", "image"],
663
725
  };
664
726
  // Every other Plan model is text-only. Declaring this explicitly keeps the vision
665
727
  // sidecar from advertising image input for models that cannot accept it — the same
@@ -686,6 +748,7 @@ const ALIBABA_INTL_TOKEN_PLAN_INPUT_MODALITIES: Record<string, string[]> = {
686
748
  "kimi-k2.6": ["text", "image"],
687
749
  "kimi-k2.5": ["text", "image"],
688
750
  "glm-5.3": ["text"],
751
+ "glm-5.3-flash": ["text", "image"],
689
752
  "glm-5.2": ["text"],
690
753
  "glm-5.1": ["text"],
691
754
  "glm-5": ["text"],
@@ -776,6 +839,7 @@ const NVIDIA_NIM_VISION_MODELS = [
776
839
  "minimaxai/minimax-m3", "moonshotai/kimi-k2.6", "moonshotai/kimi-k2.5",
777
840
  "stepfun-ai/step-3.7-flash", "thinkingmachines/inkling",
778
841
  "mistralai/mistral-medium-3.5-128b",
842
+ "z-ai/glm-5.3-flash",
779
843
  ];
780
844
  /**
781
845
  * The catalog advertises image input only for `noVisionModels` members, so a natively
@@ -809,6 +873,10 @@ const NVIDIA_NIM_NO_VISION_MODELS = [
809
873
  "nvidia/nemotron-3-ultra-550b-a55b", "nvidia/nemotron-mini-4b-instruct",
810
874
  "nvidia/nvidia-nemotron-nano-9b-v2",
811
875
  "openai/gpt-oss-120b", "openai/gpt-oss-20b",
876
+ // z-ai/glm-5.3-flash belongs in NVIDIA_NIM_VISION_MODELS, not here: Z.AI documents
877
+ // it under docs.z.ai/guides/vlm/. The header above says an id must be classified
878
+ // deliberately rather than assumed from its name, and inheriting glm-5.3's
879
+ // text-only verdict because of the shared prefix is exactly that mistake.
812
880
  "poolside/laguna-xs-2.1", "z-ai/glm-5.3", "z-ai/glm-5.2",
813
881
  ];
814
882
  const KIMI_CODING_MODEL_CONTEXT_WINDOWS: Record<string, number> = Object.fromEntries(
@@ -818,7 +886,7 @@ const KIMI_CODING_MODEL_INPUT_MODALITIES = Object.fromEntries(
818
886
  KIMI_CODING_K3_MODELS.map(id => [id, ["text", "image"]]),
819
887
  );
820
888
  const NEURALWATT_REASONING_HISTORY_MODELS = [
821
- "glm-5.3", "glm-5.3-short",
889
+ "glm-5.3", "glm-5.3-short", "glm-5.3-flash",
822
890
  "glm-5.2", "glm-5.2-short",
823
891
  "kimi-k2.6", "kimi-k2.7-code",
824
892
  "qwen3.5-397b", "qwen3.6-35b",
@@ -899,6 +967,7 @@ const DIGITALOCEAN_CHAT_COMPLETION_MODELS = [
899
967
  "nemotron-nano-12b-v2-vl",
900
968
  "mimo-v2.5-pro",
901
969
  "glm-5.3",
970
+ "glm-5.3-flash",
902
971
  "glm-5.2",
903
972
  "glm-5.1",
904
973
  "glm-5",
@@ -907,6 +976,7 @@ const DIGITALOCEAN_CHAT_COMPLETION_MODELS = [
907
976
  ] as const;
908
977
  const SCALEWAY_SERVERLESS_CHAT_MODELS = [
909
978
  "glm-5.3",
979
+ "glm-5.3-flash",
910
980
  "glm-5.2",
911
981
  // gpt-oss-120b is intentionally omitted: Scaleway requires Responses API for tool calling,
912
982
  // while this preset routes Codex agent tools through Chat Completions.
@@ -928,6 +998,7 @@ const UMANS_MODELS = [
928
998
  "umans-kimi-k2.7",
929
999
  "umans-flash",
930
1000
  "umans-glm-5.3",
1001
+ "umans-glm-5.3-flash",
931
1002
  "umans-glm-5.2",
932
1003
  "umans-glm-5.1",
933
1004
  "umans-qwen3.6-35b-a3b",
@@ -937,12 +1008,19 @@ const UMANS_GLM_REASONING_EFFORTS = ["high", "xhigh", "max"];
937
1008
  // 260814: Z.AI folds GLM-5.3 efforts into low/high/max, so `low` is a real tier here and
938
1009
  // `xhigh` is not distinct from `max` (docs.z.ai/devpack/latest-model).
939
1010
  const UMANS_GLM_53_REASONING_EFFORTS = ["low", "high", "max"];
1011
+ // `umans-glm-5.3-flash` is NOT here: Z.AI documents glm-5.3-flash under
1012
+ // docs.z.ai/guides/vlm/, so it takes images natively and does not need the proxy's
1013
+ // vision sidecar. The seeding pass classified it from the family name and a later
1014
+ // pass corrected only some of the providers; this is one it missed.
940
1015
  const UMANS_TEXT_ONLY_MODELS = ["umans-glm-5.3", "umans-glm-5.2", "umans-glm-5.1"];
941
1016
  const UMANS_MODEL_CONTEXT_WINDOWS: Record<string, number> = {
942
1017
  "umans-coder": 262_144,
943
1018
  "umans-kimi-k2.7": 262_144,
944
1019
  "umans-flash": 262_144,
945
1020
  "umans-glm-5.3": 405_504,
1021
+ // Mirrors the sibling this provider already carries. Umans has not published a
1022
+ // separate window for the flash tier; asserting a different number would be a guess.
1023
+ "umans-glm-5.3-flash": 405_504,
946
1024
  "umans-glm-5.2": 405_504,
947
1025
  "umans-glm-5.1": 202_752,
948
1026
  "umans-qwen3.6-35b-a3b": 262_144,
@@ -952,6 +1030,7 @@ const UMANS_MODEL_INPUT_MODALITIES: Record<string, string[]> = Object.fromEntrie
952
1030
  );
953
1031
  const CLINE_PASS_MODELS = [
954
1032
  "cline-pass/glm-5.3",
1033
+ "cline-pass/glm-5.3-flash",
955
1034
  "cline-pass/glm-5.2",
956
1035
  "cline-pass/kimi-k3",
957
1036
  "cline-pass/kimi-k2.7-code",
@@ -967,6 +1046,7 @@ const CLINE_PASS_MODELS = [
967
1046
  ];
968
1047
  const CLINE_PASS_MODEL_CONTEXT_WINDOWS: Record<string, number> = {
969
1048
  "cline-pass/glm-5.3": 1_048_576,
1049
+ "cline-pass/glm-5.3-flash": 1_048_576,
970
1050
  "cline-pass/glm-5.2": 1_048_576,
971
1051
  "cline-pass/kimi-k3": 1_048_576,
972
1052
  "cline-pass/kimi-k2.7-code": 262_144,
@@ -986,6 +1066,11 @@ const CLINE_PASS_IMAGE_MODELS = new Set([
986
1066
  "cline-pass/mimo-v2.5",
987
1067
  "cline-pass/minimax-m3",
988
1068
  "cline-pass/qwen3.7-plus",
1069
+ // Native VLM (docs.z.ai/guides/vlm/), so its images do not go through the proxy's
1070
+ // sidecar. Adding it here moves it out of CLINE_PASS_TEXT_ONLY_MODELS and flips its
1071
+ // declared modalities to ["text", "image"] in one edit, because both are derived
1072
+ // from this set.
1073
+ "cline-pass/glm-5.3-flash",
989
1074
  ]);
990
1075
  const CLINE_PASS_MODALITY_KNOWN_MODELS = CLINE_PASS_MODELS.filter(id => id !== "cline-pass/qwen3.8-max");
991
1076
  const CLINE_PASS_TEXT_ONLY_MODELS = CLINE_PASS_MODALITY_KNOWN_MODELS.filter(id => !CLINE_PASS_IMAGE_MODELS.has(id));
@@ -1068,7 +1153,18 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1068
1153
  // transport returns 400 ("Multi Agent requests are not allowed on chat completions").
1069
1154
  // 260813: grok-4.6 added per docs.x.ai/developers/grok-4-6. Context/vision still match
1070
1155
  // grok-4.5; the reasoning ladder does not — 4.6 adds the documented xhigh rung.
1071
- models: ["grok-4.6", "grok-4.5", "grok-4.3", "grok-4.20-0309-reasoning", "grok-4.20-0309-non-reasoning", "grok-build-0.1", "grok-composer-2.5-fast"],
1156
+ models: XAI_MODELS,
1157
+ // Measured only on grok-4.6 against cli-chat-proxy.grok.com: even an invalid
1158
+ // `text.verbosity` value is accepted and low/high/omitted output length is non-monotonic.
1159
+ // Apply the resulting opt-out to the whole xAI lineup because `text.verbosity` is an OpenAI
1160
+ // Responses parameter absent from xAI's documented API, not because every model was probed.
1161
+ // Keep this separate from reasoning-summary support: that bit gates Codex's
1162
+ // entire Responses reasoning object, including reasoning.effort.
1163
+ modelSupportsVerbosity: Object.fromEntries(XAI_MODELS.map(id => [id, false])),
1164
+ // Provider-wide, not merely per-model: `text.verbosity` is an OpenAI Responses parameter
1165
+ // absent from xAI's documented API, so a model discovered later has no more support for it
1166
+ // than the seeded ones do.
1167
+ supportsVerbosity: false,
1072
1168
  defaultModel: "grok-4.5",
1073
1169
  // Keep Codex Responses callers on the compatibility Chat wire until xAI can replay
1074
1170
  // opaque reasoning continuation and compaction state across later turns. The scoped
@@ -1143,17 +1239,12 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1143
1239
  // Unknown/new live models deliberately do not advertise a reasoning picker.
1144
1240
  reasoningEfforts: [],
1145
1241
  modelReasoningEfforts: COMMAND_CODE_MODEL_REASONING_EFFORTS,
1146
- // Ox Alpha (stealth preview, changelog v1.31.0): free 1M multimodal reasoning
1147
- // model on every plan. DeepSeek vision preview id is preemptive metadata —
1148
- // it is expected to merge into deepseek-v4-flash later.
1242
+ // The DeepSeek vision preview id is preemptive metadata it is expected to
1243
+ // merge into deepseek-v4-flash later.
1149
1244
  modelContextWindows: {
1150
- "stealth/ox-alpha": OX_ALPHA_CONTEXT_WINDOW,
1151
1245
  [`deepseek/${DEEPSEEK_VISION_PREVIEW_MODEL}`]: 1_048_576,
1152
1246
  },
1153
- modelInputModalities: {
1154
- "stealth/ox-alpha": ["text", "image"],
1155
- [`deepseek/${DEEPSEEK_VISION_PREVIEW_MODEL}`]: ["text", "image"],
1156
- },
1247
+ modelInputModalities: COMMAND_CODE_MODEL_INPUT_MODALITIES,
1157
1248
  defaultMaxOutputTokens: 64_000,
1158
1249
  // The proprietary generate wire has no verified per-request serialization flag.
1159
1250
  parallelToolCalls: false,
@@ -1238,6 +1329,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1238
1329
  // Per-model context metadata is maintained next to the Kiro model list.
1239
1330
  modelContextWindows: KIRO_MODEL_CONTEXT_WINDOWS,
1240
1331
  modelReasoningEfforts: KIRO_MODEL_REASONING_EFFORTS,
1332
+ modelSupportsVerbosity: Object.fromEntries(KIRO_MODELS.map(id => [id, false])),
1241
1333
  },
1242
1334
  {
1243
1335
  // Nous Portal — Nous Research subscription gateway (same backend Hermes Agent
@@ -1322,6 +1414,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1322
1414
  "umans-kimi-k2.7": UMANS_REASONING_EFFORTS,
1323
1415
  "umans-flash": UMANS_REASONING_EFFORTS,
1324
1416
  "umans-glm-5.3": UMANS_GLM_53_REASONING_EFFORTS,
1417
+ "umans-glm-5.3-flash": UMANS_GLM_53_REASONING_EFFORTS,
1325
1418
  "umans-glm-5.2": UMANS_GLM_REASONING_EFFORTS,
1326
1419
  "umans-glm-5.1": UMANS_GLM_REASONING_EFFORTS,
1327
1420
  "umans-qwen3.6-35b-a3b": UMANS_REASONING_EFFORTS,
@@ -1337,31 +1430,36 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1337
1430
  // finish_reason or [DONE] (#2260). The adapter still rejects incomplete argument JSON.
1338
1431
  openaiChatEofTolerance: true,
1339
1432
  /* [Decision Log]
1340
- - 목적과 의도: Route GPT 5.6 Luna to the Responses endpoint that OpenCode Go documents for that exact model.
1433
+ - 목적과 의도: Route the exact models OpenCode Go documents on the Responses endpoint GPT 5.6 Luna, and Muse Spark 1.2 Contributor (#2617).
1341
1434
  - 기존 구현 및 제약 조건: The provider is mixed-wire but its provider-wide `openai-chat` adapter sent Luna to `/chat/completions`; explicit user `modelAdapters` entries must remain authoritative.
1342
1435
  - 검토한 주요 대안: Change the whole provider to Responses; infer the wire from model-family names; add one registry-only exact-model default.
1343
- - 선택한 방식: Declare only `gpt-5.6-luna` as `openai-responses` through the existing registry default mechanism.
1436
+ - 선택한 방식: Declare only the named models as `openai-responses` through the existing registry default mechanism; the map stays an exact-model allowlist rather than a family or provider-wide rule.
1344
1437
  - 다른 대안 대신 이 방식을 선택한 이유: OpenCode Go documents sibling models on Chat or Anthropic endpoints, and an exact registry default preserves both those routes and explicit opt-out precedence.
1345
- - 장점, 단점 및 영향: Luna reaches `/responses` from every inbound surface without changing siblings; a future upstream endpoint change requires an evidence-backed registry update.
1438
+ - 장점, 단점 및 영향: Each listed model reaches `/responses` from every inbound surface without changing siblings; a future upstream endpoint change requires an evidence-backed registry update.
1346
1439
  */
1347
- modelWireDefaults: { "gpt-5.6-luna": "openai-responses" },
1440
+ modelWireDefaults: { "gpt-5.6-luna": "openai-responses", "muse-spark-1.2-contributor": "openai-responses" },
1348
1441
  modelContextWindows: {
1349
1442
  "kimi-k3": KIMI_K3_STANDARD_CONTEXT_WINDOW,
1350
- // Ox Alpha (stealth 1M multimodal) and the DeepSeek vision preview are
1351
- // metadata-only here: the Go roster is discovered live, so these apply
1352
- // the moment the gateway starts serving the ids.
1353
- [OPENCODE_OX_ALPHA_FREE_MODEL]: OX_ALPHA_CONTEXT_WINDOW,
1443
+ // The DeepSeek vision preview id is metadata-only here: the Go roster is
1444
+ // discovered live, so it applies the moment the gateway serves the id.
1354
1445
  [DEEPSEEK_VISION_PREVIEW_MODEL]: 1_048_576,
1355
1446
  },
1356
1447
  modelInputModalities: {
1357
1448
  "kimi-k3": ["text", "image"],
1358
- [OPENCODE_OX_ALPHA_FREE_MODEL]: ["text", "image"],
1359
1449
  // Experimental DeepSeek vision preview — expected to merge into deepseek-v4-flash later.
1360
1450
  [DEEPSEEK_VISION_PREVIEW_MODEL]: ["text", "image"],
1451
+ // Muse Spark 1.2 Contributor is natively multimodal on Zen Go: it accepts input_image
1452
+ // parts over /responses (probed 2026-08-26). Without this declaration the catalog
1453
+ // advertises it text-only and the Codex app blocks image attachments client-side with
1454
+ // "This model does not support image inputs" before the request ever reaches the proxy.
1455
+ "muse-spark-1.2-contributor": ["text", "image"],
1361
1456
  },
1362
1457
  modelReasoningEfforts: {
1458
+ "gpt-5.6-luna": OPENAI_API_GPT56_REASONING_EFFORTS,
1363
1459
  "glm-5.3": ZAI_GLM_53_REASONING_EFFORTS,
1460
+ "glm-5.3-flash": ZAI_GLM_53_REASONING_EFFORTS,
1364
1461
  "glm-5.2": ZAI_GLM_52_REASONING_EFFORTS,
1462
+ "qwen3.8-max": QWEN38_REASONING_EFFORTS,
1365
1463
  "kimi-k3": KIMI_CODING_K3_REASONING_EFFORTS,
1366
1464
  "kimi-k2.7-code": [],
1367
1465
  "kimi-k2.7-code-highspeed": [],
@@ -1379,6 +1477,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1379
1477
  },
1380
1478
  modelSupportsReasoningSummaries: {
1381
1479
  "glm-5.3": true,
1480
+ "glm-5.3-flash": true,
1382
1481
  "glm-5.2": true,
1383
1482
  "glm-5.1": true,
1384
1483
  "glm-5": true,
@@ -1402,7 +1501,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1402
1501
  noPenaltyModels: ["kimi-k3", "kimi-k2.7-code", "kimi-k2.7-code-highspeed"],
1403
1502
  autoToolChoiceOnlyModels: ["kimi-k2.7-code", "kimi-k2.7-code-highspeed"],
1404
1503
  // Issue #78: DeepSeek V4 thinking mode requires reasoning_content replay on tool-call turns.
1405
- preserveReasoningContentModels: ["glm-5.3", "glm-5.2", "kimi-k3", "kimi-k2.7-code", "kimi-k2.7-code-highspeed", ...DEEPSEEK_THINKING_MODELS],
1504
+ 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],
1406
1505
  },
1407
1506
  {
1408
1507
  id: "neuralwatt",
@@ -1418,6 +1517,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1418
1517
  // Evidence: devlog/_plan/260710_provider_hardening/003_research_aggregators.md and https://api.neuralwatt.com/v1/models.
1419
1518
  models: [
1420
1519
  "glm-5.3", "glm-5.3-fast", "glm-5.3-short", "glm-5.3-short-fast",
1520
+ "glm-5.3-flash",
1421
1521
  "glm-5.2", "glm-5.2-fast", "glm-5.2-short", "glm-5.2-short-fast",
1422
1522
  "kimi-k2.6", "kimi-k2.6-fast",
1423
1523
  "kimi-k2.7-code",
@@ -1429,6 +1529,9 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1429
1529
  "glm-5.3-fast": [],
1430
1530
  "glm-5.3-short": ZAI_GLM_53_REASONING_EFFORTS,
1431
1531
  "glm-5.3-short-fast": [],
1532
+ // No `-fast`/`-short` variants are asserted for the flash tier: those suffixes
1533
+ // encode routing Neuralwatt documents per model, and this seed has no source for them.
1534
+ "glm-5.3-flash": ZAI_GLM_53_REASONING_EFFORTS,
1432
1535
  "glm-5.2": ZAI_GLM_52_REASONING_EFFORTS,
1433
1536
  "glm-5.2-fast": [],
1434
1537
  "glm-5.2-short": ZAI_GLM_52_REASONING_EFFORTS,
@@ -1461,16 +1564,11 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1461
1564
  featured: true,
1462
1565
  dashboardUrl: "https://openrouter.ai/keys",
1463
1566
  jawcodeBundle: "openrouter",
1464
- // stealth/ox-alpha: free stealth-window frontier model (launched 2026-08-20).
1465
- // /api/v1/models reports 1,048,576 context, 131,072 max output, text+image+video
1466
- // input, $0 pricing, mandatory reasoning. Single provider slug: `stealth`.
1467
- models: ["anthropic/claude-sonnet-5", "stealth/ox-alpha", ...OPENROUTER_GPT56_MODELS],
1567
+ models: ["anthropic/claude-sonnet-5", ...OPENROUTER_GPT56_MODELS],
1468
1568
  modelContextWindows: {
1469
1569
  "anthropic/claude-sonnet-5": 1_000_000,
1470
- "stealth/ox-alpha": OX_ALPHA_CONTEXT_WINDOW,
1471
1570
  ...OPENROUTER_GPT56_CONTEXT_WINDOWS,
1472
1571
  },
1473
- modelInputModalities: { "stealth/ox-alpha": ["text", "image"] },
1474
1572
  // OpenRouter documents priority support for OpenAI endpoints, but not Anthropic. Keep the
1475
1573
  // provider unclassified and opt in only the exact OpenAI-backed slugs we ship. These facts
1476
1574
  // belong only to the canonical destination; a same-named custom gateway is unknown to us.
@@ -1903,17 +2001,12 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1903
2001
  // slash ids — so a Codex-facing slug like `commandcode/deepseek-deepseek-v4-pro`
1904
2002
  // is sent upstream verbatim and rejected with `unsupported_model`.
1905
2003
  modelReasoningEfforts: COMMAND_CODE_MODEL_REASONING_EFFORTS,
1906
- // Ox Alpha (stealth preview, Command Code changelog v1.31.0) ships with a
1907
- // 1.05M-token multimodal context; the DeepSeek vision preview id is
1908
- // preemptive for when the catalog serves it (merges into v4-flash later).
2004
+ // The DeepSeek vision preview id is preemptive for when the catalog serves it
2005
+ // (merges into v4-flash later).
1909
2006
  modelContextWindows: {
1910
- "stealth/ox-alpha": OX_ALPHA_CONTEXT_WINDOW,
1911
2007
  [`deepseek/${DEEPSEEK_VISION_PREVIEW_MODEL}`]: 1_048_576,
1912
2008
  },
1913
- modelInputModalities: {
1914
- "stealth/ox-alpha": ["text", "image"],
1915
- [`deepseek/${DEEPSEEK_VISION_PREVIEW_MODEL}`]: ["text", "image"],
1916
- },
2009
+ modelInputModalities: COMMAND_CODE_MODEL_INPUT_MODALITIES,
1917
2010
  modelDiscovery: {
1918
2011
  path: "models",
1919
2012
  maxResponseBytes: 256 * 1024,
@@ -2170,11 +2263,11 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
2170
2263
  id: "zai", label: "Z.AI — GLM Coding Plan", baseUrl: "https://api.z.ai/api/coding/paas/v4", adapter: "openai-chat", authKind: "key",
2171
2264
  dashboardUrl: "https://z.ai/manage-apikey/apikey-list", defaultModel: "glm-5.3",
2172
2265
  note: "GLM-5.3 coding subscription",
2173
- models: ["glm-5.3", "glm-5.3[1m]", "glm-5.2", "glm-5.2[1m]", "glm-5.1", "glm-5", "glm-4.6"],
2174
- modelContextWindows: { "glm-5.3": 1_000_000, "glm-5.3[1m]": 1_000_000, "glm-5.2": 1_000_000, "glm-5.2[1m]": 1_000_000 },
2266
+ models: ["glm-5.3", "glm-5.3[1m]", "glm-5.3-flash", "glm-5.2", "glm-5.2[1m]", "glm-5.1", "glm-5", "glm-4.6"],
2267
+ modelContextWindows: { "glm-5.3": 1_000_000, "glm-5.3[1m]": 1_000_000, "glm-5.3-flash": 1_000_000, "glm-5.2": 1_000_000, "glm-5.2[1m]": 1_000_000 },
2175
2268
  // Z.AI's OpenAI path returns 400 code 1211 for bracketed model ids.
2176
2269
  modelSuffixBracketStrip: true,
2177
- noVisionModels: ZAI_GLM_5X_MODELS,
2270
+ noVisionModels: ZAI_GLM_5X_SIDECAR_VISION_MODELS,
2178
2271
  modelReasoningEfforts: ZAI_GLM_5X_REASONING_EFFORTS,
2179
2272
  modelDefaultReasoningEfforts: Object.fromEntries(ZAI_GLM_53_MODELS.map(id => [id, "max"])),
2180
2273
  modelMaxOutputTokens: Object.fromEntries(ZAI_GLM_53_MODELS.map(id => [id, 131_072])),
@@ -2251,11 +2344,11 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
2251
2344
  authKind: "key",
2252
2345
  dashboardUrl: "https://bigmodel.cn/console/usercenter/apikeys",
2253
2346
  defaultModel: "glm-5.3",
2254
- models: ["glm-5.3", "glm-5.3[1m]", "glm-5.2", "glm-5.2[1m]", "glm-5.1", "glm-5", "glm-4.6"],
2347
+ models: ["glm-5.3", "glm-5.3[1m]", "glm-5.3-flash", "glm-5.2", "glm-5.2[1m]", "glm-5.1", "glm-5", "glm-4.6"],
2255
2348
  jawcodeBundle: "zai",
2256
- modelContextWindows: { "glm-5.3": 1_000_000, "glm-5.3[1m]": 1_000_000, "glm-5.2": 1_000_000, "glm-5.2[1m]": 1_000_000 },
2349
+ modelContextWindows: { "glm-5.3": 1_000_000, "glm-5.3[1m]": 1_000_000, "glm-5.3-flash": 1_000_000, "glm-5.2": 1_000_000, "glm-5.2[1m]": 1_000_000 },
2257
2350
  modelSuffixBracketStrip: true,
2258
- noVisionModels: ZAI_GLM_5X_MODELS,
2351
+ noVisionModels: ZAI_GLM_5X_SIDECAR_VISION_MODELS,
2259
2352
  modelReasoningEfforts: ZAI_GLM_5X_REASONING_EFFORTS,
2260
2353
  modelSupportsReasoningSummaries: Object.fromEntries(ZAI_GLM_5X_MODELS.map(id => [id, true])),
2261
2354
  preserveReasoningContentModels: ZAI_GLM_5X_MODELS,
@@ -2397,12 +2490,13 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
2397
2490
  modelInputModalities: ALIBABA_TOKEN_PLAN_INPUT_MODALITIES,
2398
2491
  modelContextWindows: {
2399
2492
  "qwen3.8-max": 983_616, "qwen3.7-max": 1_000_000, "qwen3.7-plus": 1_000_000,
2400
- "qwen3.6-flash": 1_000_000, "glm-5.3": 1_000_000, "glm-5.2": 1_000_000, "deepseek-v4-pro": 1_000_000,
2493
+ "qwen3.6-flash": 1_000_000, "glm-5.3": 1_000_000, "glm-5.3-flash": 1_000_000, "glm-5.2": 1_000_000, "deepseek-v4-pro": 1_000_000,
2401
2494
  },
2402
2495
  modelReasoningEfforts: {
2403
2496
  ...Object.fromEntries(ALIBABA_TOKEN_PLAN_QWEN_MODELS.map(id => [id, THINKING_BUDGET_EFFORTS])),
2404
2497
  "qwen3.8-max": QWEN38_REASONING_EFFORTS,
2405
2498
  "glm-5.3": ZAI_GLM_53_REASONING_EFFORTS,
2499
+ "glm-5.3-flash": ZAI_GLM_53_REASONING_EFFORTS,
2406
2500
  "glm-5.2": ZAI_GLM_52_REASONING_EFFORTS,
2407
2501
  "deepseek-v4-pro": deepseekThinkingEffortsFor("deepseek-v4-pro"),
2408
2502
  },
@@ -2410,7 +2504,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
2410
2504
  modelReasoningEffortMap: { "deepseek-v4-pro": deepseekReasoningMapFor("deepseek-v4-pro") },
2411
2505
  directReasoningEffortModels: ["qwen3.8-max"],
2412
2506
  thinkingBudgetModels: ALIBABA_TOKEN_PLAN_QWEN_MODELS.filter(id => id !== "qwen3.8-max"),
2413
- preserveReasoningContentModels: ["glm-5.3", "glm-5.2", "deepseek-v4-pro", "qwen3.8-max", "qwen3.7-max", "qwen3.7-plus", "qwen3.6-flash"],
2507
+ preserveReasoningContentModels: ["glm-5.3", "glm-5.3-flash", "glm-5.2", "deepseek-v4-pro", "qwen3.8-max", "qwen3.7-max", "qwen3.7-plus", "qwen3.6-flash"],
2414
2508
  noVisionModels: ["glm-5.3", "glm-5.2", "deepseek-v4-pro"],
2415
2509
  },
2416
2510
  {
@@ -2433,13 +2527,14 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
2433
2527
  "qwen3.7-max": 1_000_000, "qwen3.7-plus": 1_000_000, "qwen3.6-plus": 1_000_000, "qwen3.6-flash": 1_000_000,
2434
2528
  "deepseek-v4-pro": 1_000_000, "deepseek-v4-flash": 1_000_000, "deepseek-v3.2": 131_072,
2435
2529
  "kimi-k2.7-code": 262_144, "kimi-k2.6": 262_144, "kimi-k2.5": 262_144,
2436
- "glm-5.3": 1_000_000, "glm-5.2": 1_000_000, "glm-5.1": 1_000_000, "glm-5": 1_000_000,
2530
+ "glm-5.3": 1_000_000, "glm-5.3-flash": 1_000_000, "glm-5.2": 1_000_000, "glm-5.1": 1_000_000, "glm-5": 1_000_000,
2437
2531
  "MiniMax-M2.5": 204_800,
2438
2532
  },
2439
2533
  modelReasoningEfforts: {
2440
2534
  ...Object.fromEntries(ALIBABA_INTL_TOKEN_PLAN_QWEN_MODELS.map(id => [id, THINKING_BUDGET_EFFORTS])),
2441
2535
  "qwen3.8-max": QWEN38_REASONING_EFFORTS,
2442
2536
  "glm-5.3": ZAI_GLM_53_REASONING_EFFORTS,
2537
+ "glm-5.3-flash": ZAI_GLM_53_REASONING_EFFORTS,
2443
2538
  "glm-5.2": ZAI_GLM_52_REASONING_EFFORTS,
2444
2539
  "deepseek-v4-pro": deepseekThinkingEffortsFor("deepseek-v4-pro"),
2445
2540
  "deepseek-v4-flash": deepseekThinkingEffortsFor("deepseek-v4-flash"),
@@ -2450,7 +2545,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
2450
2545
  },
2451
2546
  directReasoningEffortModels: ["qwen3.8-max"],
2452
2547
  thinkingBudgetModels: ALIBABA_INTL_TOKEN_PLAN_QWEN_MODELS.filter(id => id !== "qwen3.8-max"),
2453
- preserveReasoningContentModels: ["glm-5.3", "glm-5.2", "deepseek-v4-pro", "deepseek-v4-flash", "qwen3.8-max", "qwen3.7-max", "qwen3.7-plus", "qwen3.6-plus", "qwen3.6-flash"],
2548
+ preserveReasoningContentModels: ["glm-5.3", "glm-5.3-flash", "glm-5.2", "deepseek-v4-pro", "deepseek-v4-flash", "qwen3.8-max", "qwen3.7-max", "qwen3.7-plus", "qwen3.6-plus", "qwen3.6-flash"],
2454
2549
  noVisionModels: ["deepseek-v4-pro", "deepseek-v4-flash", "deepseek-v3.2", "glm-5.3", "glm-5.2", "glm-5.1", "glm-5", "MiniMax-M2.5"],
2455
2550
  noReasoningModels: ["kimi-k2.7-code", "kimi-k2.6", "kimi-k2.5", "deepseek-v3.2", "glm-5.1", "glm-5", "MiniMax-M2.5"],
2456
2551
  modelDefaultReasoningEfforts: { "qwen3.8-max": "xhigh" },
@@ -2483,9 +2578,11 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
2483
2578
  authKind: "key",
2484
2579
  dashboardUrl: "https://ollama.com/settings/keys",
2485
2580
  // Live IDs verified 2026-07-10; qwen3-coder:480b retires 2026-07-15.
2486
- models: ["glm-5.3", "glm-5.2", "deepseek-v4-pro", "qwen3-coder:480b", "gpt-oss:120b", "kimi-k2.6", "minimax-m3", "qwen3.5:397b", "gemma4:31b"],
2581
+ models: ["glm-5.3", "glm-5.3-flash", "glm-5.2", "deepseek-v4-pro", "qwen3-coder:480b", "gpt-oss:120b", "kimi-k2.6", "minimax-m3", "qwen3.5:397b", "gemma4:31b"],
2487
2582
  defaultModel: "glm-5.3",
2488
2583
  noVisionModels: [
2584
+ // glm-5.3-flash is absent on purpose: native VLM
2585
+ // (docs.z.ai/guides/vlm/glm-5.3-flash), so its images skip the sidecar.
2489
2586
  "glm-5.3", "glm-5.2", "glm-5.1", "glm-5", "glm-4.7",
2490
2587
  "minimax-m2.7", "minimax-m2.5", "minimax-m2.1",
2491
2588
  "nemotron-3-ultra", "nemotron-3-super",
@@ -2558,14 +2655,12 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
2558
2655
  [...DEEPSEEK_THINKING_MODELS, ...OPENCODE_FREE_DEEPSEEK_MODELS].map(id => [id, deepseekReasoningMapFor(id)]),
2559
2656
  ),
2560
2657
  preserveReasoningContentModels: [...DEEPSEEK_THINKING_MODELS, ...OPENCODE_FREE_DEEPSEEK_MODELS],
2561
- // Same Zen gateway as opencode-free: Ox Alpha Free (1M multimodal stealth model)
2562
- // and the DeepSeek vision preview (merges into deepseek-v4-flash later).
2658
+ // Same Zen gateway as opencode-free: the DeepSeek vision preview id
2659
+ // (merges into deepseek-v4-flash later).
2563
2660
  modelContextWindows: {
2564
- [OPENCODE_OX_ALPHA_FREE_MODEL]: OX_ALPHA_CONTEXT_WINDOW,
2565
2661
  [DEEPSEEK_VISION_PREVIEW_MODEL]: 1_048_576,
2566
2662
  },
2567
2663
  modelInputModalities: {
2568
- [OPENCODE_OX_ALPHA_FREE_MODEL]: ["text", "image"],
2569
2664
  [DEEPSEEK_VISION_PREVIEW_MODEL]: ["text", "image"],
2570
2665
  },
2571
2666
  noVisionModels: [...OPENCODE_ZEN_TEXT_ONLY_MODELS, ...DEEPSEEK_THINKING_MODELS],
@@ -2598,16 +2693,12 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
2598
2693
  modelReasoningEfforts: Object.fromEntries(OPENCODE_FREE_DEEPSEEK_MODELS.map(id => [id, deepseekThinkingEffortsFor(id)])),
2599
2694
  modelReasoningEffortMap: Object.fromEntries(OPENCODE_FREE_DEEPSEEK_MODELS.map(id => [id, deepseekReasoningMapFor(id)])),
2600
2695
  preserveReasoningContentModels: OPENCODE_FREE_DEEPSEEK_MODELS,
2601
- // Ox Alpha Free (`x-preview-f-free`): the OpenRouter stealth model on Zen's
2602
- // free tier 1,048,576 context, text+image input. Deliberately NOT in the
2603
- // text-only list below. The DeepSeek vision preview id is preemptive
2604
- // metadata for when Zen starts serving it (merges into v4-flash later).
2696
+ // The DeepSeek vision preview id is preemptive metadata for when Zen starts
2697
+ // serving it (merges into v4-flash later).
2605
2698
  modelContextWindows: {
2606
- [OPENCODE_OX_ALPHA_FREE_MODEL]: OX_ALPHA_CONTEXT_WINDOW,
2607
2699
  [DEEPSEEK_VISION_PREVIEW_MODEL]: 1_048_576,
2608
2700
  },
2609
2701
  modelInputModalities: {
2610
- [OPENCODE_OX_ALPHA_FREE_MODEL]: ["text", "image"],
2611
2702
  [DEEPSEEK_VISION_PREVIEW_MODEL]: ["text", "image"],
2612
2703
  },
2613
2704
  // Same Zen roster behind the same base URL, so it carries the same measured
@@ -2699,6 +2790,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
2699
2790
  "@cf/deepseek-ai/deepseek-r1-distill-qwen-32b",
2700
2791
  "@cf/moonshotai/kimi-k2.7-code",
2701
2792
  "@cf/zai-org/glm-5.3",
2793
+ "@cf/zai-org/glm-5.3-flash",
2702
2794
  "@cf/zai-org/glm-5.2",
2703
2795
  "@cf/mistralai/mistral-small-3.1-24b-instruct",
2704
2796
  ],
@@ -101,3 +101,55 @@ export function slugEquivalenceKey(slug: string): string {
101
101
  export function slugsEquivalent(a: string, b: string): boolean {
102
102
  return a === b || slugEquivalenceKey(a) === slugEquivalenceKey(b);
103
103
  }
104
+
105
+ /**
106
+ * Resolve one config selection against a provider's known native ids (#2491).
107
+ *
108
+ * `slugEquivalenceKey` is deliberately lossy — the Codex one-slash rule forces `a/b` and
109
+ * `a-b` onto the same encoded form — so a selection written in either spelling matches BOTH
110
+ * when a provider publishes both. Filtering and persisted sync share that key, which keeps
111
+ * them consistent with each other but silently over-grants.
112
+ *
113
+ * This resolver keeps the tolerant behaviour (a selection still matches through either
114
+ * spelling, and an id absent from an incomplete live roster still resolves) while reporting
115
+ * whether the match was EXACT or merely equivalent. A caller that can afford to be strict —
116
+ * one holding a complete known-id set — can then prefer the exact row instead of granting the
117
+ * whole collision class.
118
+ *
119
+ * Returning the ambiguity rather than resolving it is deliberate: the roster is an incomplete
120
+ * dictionary, so silently narrowing to the exact spelling would hide a published id whenever
121
+ * discovery omitted it. The caller owns that tradeoff because only the caller knows whether
122
+ * its id set is complete.
123
+ */
124
+ export interface SlugSelectionMatch {
125
+ /** Native ids this selection admits. */
126
+ readonly matched: readonly string[];
127
+ /** The id whose raw form the selection names exactly, when one exists. */
128
+ readonly exact: string | undefined;
129
+ /** True when more than one known id shares the selection's encoded form. */
130
+ readonly ambiguous: boolean;
131
+ }
132
+
133
+ export function resolveSlugSelection(
134
+ provider: string,
135
+ selection: string,
136
+ knownIds: Iterable<string>,
137
+ ): SlugSelectionMatch {
138
+ // A slash in the selection is ambiguous on its own: `p/a-b` is provider-qualified, while
139
+ // `a/b` is a bare NATIVE id that happens to contain a slash. Treating every slash-bearing
140
+ // selection as provider-qualified made `a/b` resolve against provider "a", so the same
141
+ // collision reported ambiguous through the dash spelling and unambiguous through the slash
142
+ // spelling — the exact asymmetry this resolver exists to remove.
143
+ const qualified = selection.startsWith(`${provider}/`)
144
+ ? selection
145
+ : routedSlug(provider, selection);
146
+ const selectionKey = slugEquivalenceKey(qualified);
147
+ const matched: string[] = [];
148
+ let exact: string | undefined;
149
+ for (const id of knownIds) {
150
+ if (slugEquivalenceKey(routedSlug(provider, id)) !== selectionKey) continue;
151
+ matched.push(id);
152
+ if (id === selection || `${provider}/${id}` === selection) exact = id;
153
+ }
154
+ return { matched, exact, ambiguous: matched.length > 1 };
155
+ }