@bitkyc08/opencodex 2.33.0 → 2.34.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 (159) hide show
  1. package/README.md +3 -3
  2. package/gui/dist/assets/index-C4TMRloX.js +112 -0
  3. package/gui/dist/assets/index-DQ-Ie18T.css +1 -0
  4. package/gui/dist/index.html +2 -2
  5. package/package.json +1 -1
  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 +162 -0
  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-desktop.ts +23 -0
  17. package/src/adapters/cursor/native-exec-fs.ts +2 -2
  18. package/src/adapters/cursor/native-exec-network.ts +1 -1
  19. package/src/adapters/cursor/native-exec-shell.ts +5 -2
  20. package/src/adapters/cursor/native-exec.ts +20 -0
  21. package/src/adapters/cursor/protobuf-request.ts +68 -14
  22. package/src/adapters/cursor/request-builder.ts +29 -6
  23. package/src/adapters/cursor/tool-definitions.ts +11 -2
  24. package/src/adapters/cursor/tool-result-normalize.ts +32 -1
  25. package/src/adapters/cursor/types.ts +20 -0
  26. package/src/adapters/cursor.ts +189 -37
  27. package/src/adapters/google-antigravity-replay.ts +102 -23
  28. package/src/adapters/google-errors.ts +44 -12
  29. package/src/adapters/google.ts +115 -16
  30. package/src/adapters/image.ts +1 -1
  31. package/src/adapters/kiro-constants.ts +15 -0
  32. package/src/adapters/kiro-tools.ts +43 -15
  33. package/src/adapters/kiro.ts +54 -9
  34. package/src/adapters/openai-chat.ts +286 -242
  35. package/src/adapters/openai-responses.ts +334 -24
  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 -24
  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 +32 -4
  44. package/src/cli/account-extended.ts +1 -1
  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/init.ts +1 -0
  51. package/src/cli/models-runtime.ts +95 -0
  52. package/src/cli/models.ts +13 -7
  53. package/src/cli/provider-runtime.ts +16 -2
  54. package/src/cli/registry.ts +6 -1
  55. package/src/cli/v2.ts +34 -10
  56. package/src/codex/account-pause.ts +2 -1
  57. package/src/codex/account-priority.ts +3 -2
  58. package/src/codex/app-server-processes.ts +80 -6
  59. package/src/codex/auth-api.ts +46 -8
  60. package/src/codex/auth-context.ts +21 -18
  61. package/src/codex/catalog/aggregation.ts +6 -0
  62. package/src/codex/catalog/native-models.ts +5 -2
  63. package/src/codex/catalog/parsing.ts +16 -0
  64. package/src/codex/catalog/provider-fetch.ts +20 -3
  65. package/src/codex/catalog/sync.ts +127 -2
  66. package/src/codex/catalog.ts +1 -1
  67. package/src/codex/codex-write-lock.ts +3 -1
  68. package/src/codex/convergence-types.ts +1 -1
  69. package/src/codex/convergence.ts +22 -2
  70. package/src/codex/desired-state.ts +2 -2
  71. package/src/codex/desktop-app-restart.ts +18 -5
  72. package/src/codex/inject-coordination.ts +83 -0
  73. package/src/codex/inject.ts +14 -1
  74. package/src/codex/log-guard/inspect.ts +22 -4
  75. package/src/codex/model-entitlements.ts +9 -2
  76. package/src/codex/prompt-layers.ts +371 -25
  77. package/src/codex/prompt-text-probe.ts +238 -0
  78. package/src/codex/quota.ts +91 -10
  79. package/src/codex/routing.ts +9 -0
  80. package/src/codex/subagent-model-fallback.ts +104 -25
  81. package/src/codex/transition-state.ts +107 -8
  82. package/src/combos/types.ts +10 -0
  83. package/src/compatibility/openai-responses.ts +33 -1
  84. package/src/config/rebase-provenance.ts +68 -0
  85. package/src/config.ts +107 -9
  86. package/src/generated/compatibility-version.json +191 -139
  87. package/src/images/loop.ts +5 -4
  88. package/src/lab/conformance/fixtures/protocol-v1-cases.json +1 -1
  89. package/src/lab/fabric/producer-child.ts +1 -1
  90. package/src/lib/config-ownership.ts +20 -0
  91. package/src/lib/errors.ts +11 -2
  92. package/src/lib/package-tree-integrity.ts +101 -0
  93. package/src/oauth/generic-account-failover.ts +231 -0
  94. package/src/oauth/index.ts +57 -5
  95. package/src/oauth/key-providers.ts +10 -1
  96. package/src/oauth/kiro.ts +45 -0
  97. package/src/oauth/types.ts +15 -0
  98. package/src/providers/command-code-efforts.ts +38 -6
  99. package/src/providers/context-cap.ts +4 -3
  100. package/src/providers/default-aliases.ts +65 -0
  101. package/src/providers/derive.ts +28 -0
  102. package/src/providers/fastwire.ts +7 -1
  103. package/src/providers/model-presets.ts +119 -0
  104. package/src/providers/new-model-policy.ts +146 -0
  105. package/src/providers/provider-id-rewrite.ts +2 -1
  106. package/src/providers/quota.ts +65 -7
  107. package/src/providers/registry.ts +160 -68
  108. package/src/providers/slug-codec.ts +52 -0
  109. package/src/responses/code-mode-helper-compat.ts +50 -0
  110. package/src/responses/custom-tool-compat.ts +34 -10
  111. package/src/responses/parser.ts +4 -0
  112. package/src/responses/schema.ts +5 -1
  113. package/src/responses/thought-signature-replay.ts +17 -0
  114. package/src/router.ts +40 -2
  115. package/src/routing/analytics.ts +1 -0
  116. package/src/routing/quota.ts +10 -0
  117. package/src/server/auth-cors.ts +4 -0
  118. package/src/server/chat-completions.ts +24 -16
  119. package/src/server/chat-native-sse.ts +3 -3
  120. package/src/server/chat-native.ts +30 -11
  121. package/src/server/claude-messages.ts +1 -1
  122. package/src/server/index.ts +77 -11
  123. package/src/server/lifecycle.ts +52 -1
  124. package/src/server/management/agent-settings-routes.ts +31 -15
  125. package/src/server/management/codex-prompt-routes.ts +570 -0
  126. package/src/server/management/combo-routes.ts +2 -1
  127. package/src/server/management/config-routes.ts +27 -9
  128. package/src/server/management/context.ts +9 -0
  129. package/src/server/management/model-routes.ts +266 -0
  130. package/src/server/management/provider-routes.ts +6 -0
  131. package/src/server/management/routing-profile-routes.ts +2 -2
  132. package/src/server/management-api.ts +2 -0
  133. package/src/server/relay-eager.ts +147 -20
  134. package/src/server/relay.ts +251 -19
  135. package/src/server/request-log-conversation.ts +21 -0
  136. package/src/server/request-log.ts +41 -15
  137. package/src/server/responses/collaboration.ts +37 -3
  138. package/src/server/responses/combo-stream-preflight.ts +10 -3
  139. package/src/server/responses/core.ts +494 -114
  140. package/src/server/responses/empty-completion-guard.ts +35 -0
  141. package/src/server/responses/fetch-helpers.ts +2 -1
  142. package/src/server/responses/input-admission.ts +3 -1
  143. package/src/server/responses/passthrough-error.ts +33 -9
  144. package/src/server/responses/responses-field-backfill.ts +105 -13
  145. package/src/server/responses-custom-tool-repair.ts +52 -7
  146. package/src/server/responses-terminal-repair.ts +25 -4
  147. package/src/server/sse-frame-buffer.ts +31 -4
  148. package/src/server/ws-bridge.ts +14 -2
  149. package/src/types/config.ts +53 -0
  150. package/src/types/provider.ts +64 -0
  151. package/src/types/request.ts +11 -3
  152. package/src/types/tools.ts +7 -5
  153. package/src/types.ts +1 -0
  154. package/src/usage/expected-prices.ts +43 -24
  155. package/src/usage/log.ts +3 -0
  156. package/src/web-search/exa-executor.ts +40 -9
  157. package/src/web-search/loop.ts +5 -4
  158. package/gui/dist/assets/index-23-Lf7jR.js +0 -102
  159. package/gui/dist/assets/index-DxJMDyOr.css +0 -1
@@ -250,6 +250,18 @@ export interface ProviderRegistryEntry {
250
250
  preserveResponsesReasoningContent?: boolean;
251
251
  /** Registry defaults for per-model Codex reasoning propagation; explicit user keys win during enrichment. */
252
252
  modelSupportsReasoningSummaries?: Record<string, boolean>;
253
+ /** Registry defaults for per-model Codex Responses verbosity support. */
254
+ modelSupportsVerbosity?: Record<string, boolean>;
255
+ /**
256
+ * Registry default applied to EVERY model of this provider, including ids that arrive from
257
+ * live discovery after this table was written.
258
+ *
259
+ * `modelSupportsVerbosity` only covers the ids enumerated here, so a newly discovered model
260
+ * fell through and re-advertised a control the upstream accepts and ignores. Where the opt-out
261
+ * is a property of the provider's API rather than of one model, declare it here; a per-model
262
+ * entry still wins over it.
263
+ */
264
+ supportsVerbosity?: boolean;
253
265
  modelDiscovery?: ProviderModelDiscoverySpec;
254
266
  contextWindow?: number;
255
267
  modelContextWindows?: Record<string, number>;
@@ -328,9 +340,31 @@ const ANTHROPIC_MODEL_CONTEXT_WINDOWS: Record<string, number> = { "claude-sonnet
328
340
  // The non-Z.AI providers below are speculative on purpose: they carry 5.2 today and are
329
341
  // expected to pick 5.3 up on their usual lag. Providers whose live /v1/models discovery is
330
342
  // enabled self-correct on the next successful fetch; static ones need a follow-up refresh.
331
- const ZAI_GLM_53_MODELS = ["glm-5.3", "glm-5.3[1m]"];
343
+ // Every 5.3 family member, so the effort ladder, the default effort and the output
344
+ // cap are derived in ONE place. `glm-5.3-flash` was seeded into the model list and
345
+ // the context map by hand and left out of this constant, which meant it advertised
346
+ // a 1M context with a null effort ladder, no default effort and no output cap while
347
+ // its siblings carried three tiers, a `max` default and 131072 tokens. A member
348
+ // added to the list but not to the family is a model whose metadata silently
349
+ // disappears.
350
+ const ZAI_GLM_53_MODELS = ["glm-5.3", "glm-5.3[1m]", "glm-5.3-flash"];
332
351
  const ZAI_GLM_52_MODELS = ["glm-5.2", "glm-5.2[1m]"];
333
352
  const ZAI_GLM_5X_MODELS = [...ZAI_GLM_53_MODELS, ...ZAI_GLM_52_MODELS];
353
+ /**
354
+ * The 5.x rows whose images the PROXY has to describe, which is NOT the same set as
355
+ * the 5.x rows themselves.
356
+ *
357
+ * `glm-5.3-flash` is a native VLM (docs.z.ai/guides/vlm/glm-5.3-flash), so listing it
358
+ * in `noVisionModels` sent an image through the vision sidecar and handed the model a
359
+ * text description of a picture it could have read itself - no error, worse answer,
360
+ * extra call. The correction commit fixed the Alibaba entries and left the eight
361
+ * providers that reach this constant behind.
362
+ *
363
+ * Kept separate from ZAI_GLM_5X_MODELS rather than filtered at each use site: that
364
+ * constant also drives `modelSupportsReasoningSummaries` and
365
+ * `preserveReasoningContentModels`, where flash DOES belong.
366
+ */
367
+ const ZAI_GLM_5X_SIDECAR_VISION_MODELS = ZAI_GLM_5X_MODELS.filter(id => id !== "glm-5.3-flash");
334
368
  const ZAI_GLM_52_REASONING_EFFORTS = ["low", "medium", "high", "xhigh", "max"];
335
369
  /**
336
370
  * GLM-5.3 does NOT share 5.2's five-tier ladder. docs.z.ai/devpack/latest-model folds every
@@ -416,6 +450,15 @@ const OPENAI_DAYBREAK_REASONING_EFFORTS: Record<string, string[]> = Object.fromE
416
450
  OPENAI_DAYBREAK_MODELS.map(id => [id, [] as string[]]),
417
451
  );
418
452
  const OPENROUTER_GPT56_MODELS = OPENAI_GPT56_MODELS.map(id => `openai/${id}`);
453
+ const XAI_MODELS = [
454
+ "grok-4.6",
455
+ "grok-4.5",
456
+ "grok-4.3",
457
+ "grok-4.20-0309-reasoning",
458
+ "grok-4.20-0309-non-reasoning",
459
+ "grok-build-0.1",
460
+ "grok-composer-2.5-fast",
461
+ ];
419
462
  // OpenRouter's live /endpoints routes report 1,050,000; keep this separate from the
420
463
  // unverified OpenAI API-key seed. Evidence: devlog/_plan/260710_provider_hardening/003_research_aggregators.md.
421
464
  const OPENROUTER_GPT56_CONTEXT_WINDOW = 1_050_000;
@@ -450,13 +493,15 @@ const OPENCODE_GO_THINKING_TOGGLE_MODELS = [
450
493
  * images through the proxy's vision sidecar (src/codex/catalog/provider-fetch.ts), a claim nobody
451
494
  * has verified for BigModel-hosted GLM.
452
495
  */
496
+ // `glm-5.3-flash` is deliberately absent: it is a native VLM
497
+ // (docs.z.ai/guides/vlm/glm-5.3-flash), unlike glm-5.3 itself.
453
498
  const ZHIPU_BIGMODEL_TEXT_MODELS = ["glm-4.6", "glm-4.7", "glm-4.7-flash", "glm-5", "glm-5.1", "glm-5.2", "glm-5.3"];
454
499
  const ZHIPU_BIGMODEL_MODELS = [...ZHIPU_BIGMODEL_TEXT_MODELS, "glm-4.6v"];
455
500
  const ZHIPU_BIGMODEL_INPUT_MODALITIES: Record<string, string[]> = {
456
501
  ...Object.fromEntries(ZHIPU_BIGMODEL_TEXT_MODELS.map(id => [id, ["text"]])),
457
502
  "glm-4.6v": ["text", "image"],
458
503
  };
459
- const ZHIPU_BIGMODEL_THINKING_TOGGLE_MODELS = ["glm-4.6", "glm-4.7", "glm-5", "glm-5.1", "glm-5.2", "glm-5.3"];
504
+ const ZHIPU_BIGMODEL_THINKING_TOGGLE_MODELS = ["glm-4.6", "glm-4.7", "glm-5", "glm-5.1", "glm-5.2", "glm-5.3", "glm-5.3-flash"];
460
505
  const THINKING_BUDGET_EFFORTS = ["low", "medium", "high", "xhigh", "max"];
461
506
  // Qwen3.8-Max is the first Qwen3.x model with official direct `reasoning_effort` support.
462
507
  // Evidence: https://qwen.ai/blog?id=qwen3.8
@@ -474,16 +519,27 @@ const DEEPSEEK_THINKING_MODELS = ["deepseek-v4-pro", "deepseek-v4-flash"];
474
519
  * at which point this id retires the same way deepseek-chat/reasoner did.
475
520
  */
476
521
  const DEEPSEEK_VISION_PREVIEW_MODEL = "deepseek-v4-flash-vision-exp";
477
- const OPENCODE_FREE_DEEPSEEK_MODELS = ["deepseek-v4-flash-free"];
478
- /*
479
- * OpenCode Zen's free slug for the OpenRouter stealth model "Ox Alpha"
480
- * (openrouter.ai/stealth/ox-alpha): 1,048,576-token context, multimodal
481
- * (text+image+video upstream; Zen serves text+image), mandatory reasoning,
482
- * free during the stealth window. Zen displays it as "Ox Alpha Free" under
483
- * this exact id (opencode.ai/docs/zen, verified 2026-08-21).
522
+ /**
523
+ * CommandCode routes verified to accept image input end-to-end (#2406).
524
+ *
525
+ * Verified-negative and therefore deliberately ABSENT: deepseek/deepseek-v4-flash,
526
+ * deepseek/deepseek-v4-pro, zai-org/GLM-5.2, zai-org/GLM-5.3, xai/grok-4.6. Those
527
+ * routes accept the request and drop the image, which is worse than declining it — the
528
+ * model answers about an image it never saw. Do not add an id here on family resemblance;
529
+ * capability intersection trusts this map.
484
530
  */
485
- const OPENCODE_OX_ALPHA_FREE_MODEL = "x-preview-f-free";
486
- const OX_ALPHA_CONTEXT_WINDOW = 1_048_576;
531
+ const COMMAND_CODE_IMAGE_MODELS = [
532
+ `deepseek/${DEEPSEEK_VISION_PREVIEW_MODEL}`,
533
+ "gpt-5.6-luna",
534
+ "gpt-5.6-sol",
535
+ "MiniMaxAI/MiniMax-M3",
536
+ "moonshotai/Kimi-K3",
537
+ "meta/muse-spark-1.2",
538
+ "meta/muse-spark-1.2-contributor",
539
+ ] as const;
540
+ const COMMAND_CODE_MODEL_INPUT_MODALITIES: Record<string, ["text", "image"]> =
541
+ Object.fromEntries(COMMAND_CODE_IMAGE_MODELS.map(id => [id, ["text", "image"]]));
542
+ const OPENCODE_FREE_DEEPSEEK_MODELS = ["deepseek-v4-flash-free"];
487
543
  /*
488
544
  * Zen free models that reject `image_url` upstream (#1043, and the reproducible
489
545
  * half of #1024).
@@ -570,7 +626,7 @@ const deepseekReasoningMapFor = (modelId: string): Record<string, string> =>
570
626
  // https://help.aliyun.com/en/model-studio/token-plan-quickstart
571
627
  const ALIBABA_TOKEN_PLAN_MODELS = [
572
628
  "qwen3.8-max", "qwen3.7-max", "qwen3.7-plus", "qwen3.6-flash",
573
- "glm-5.3", "glm-5.2", "deepseek-v4-pro",
629
+ "glm-5.3", "glm-5.3-flash", "glm-5.2", "deepseek-v4-pro",
574
630
  ];
575
631
  const ALIBABA_TOKEN_PLAN_QWEN_MODELS = [
576
632
  "qwen3.8-max", "qwen3.7-max", "qwen3.7-plus", "qwen3.6-flash",
@@ -581,6 +637,7 @@ const ALIBABA_TOKEN_PLAN_INPUT_MODALITIES: Record<string, string[]> = {
581
637
  "qwen3.7-plus": ["text", "image"],
582
638
  "qwen3.6-flash": ["text", "image"],
583
639
  "glm-5.3": ["text"],
640
+ "glm-5.3-flash": ["text", "image"],
584
641
  "glm-5.2": ["text"],
585
642
  "deepseek-v4-pro": ["text"],
586
643
  };
@@ -593,7 +650,7 @@ const ALIBABA_INTL_TOKEN_PLAN_MODELS = [
593
650
  "qwen3.8-max", "qwen3.7-max", "qwen3.7-plus", "qwen3.6-plus", "qwen3.6-flash",
594
651
  "deepseek-v4-pro", "deepseek-v4-flash", "deepseek-v3.2",
595
652
  "kimi-k2.7-code", "kimi-k2.6", "kimi-k2.5",
596
- "glm-5.3", "glm-5.2", "glm-5.1", "glm-5",
653
+ "glm-5.3", "glm-5.3-flash", "glm-5.2", "glm-5.1", "glm-5",
597
654
  "MiniMax-M2.5",
598
655
  ];
599
656
  const ALIBABA_INTL_TOKEN_PLAN_QWEN_MODELS = [
@@ -643,6 +700,7 @@ const VOLCENGINE_CODING_PLAN_MODELS = [
643
700
  "deepseek-v4-pro",
644
701
  "deepseek-v4-flash",
645
702
  "glm-5.3",
703
+ "glm-5.3-flash",
646
704
  "glm-5.2",
647
705
  "kimi-k2.6",
648
706
  "minimax-m3",
@@ -651,6 +709,7 @@ const VOLCENGINE_AGENT_PLAN_MODELS = [
651
709
  "deepseek-v4-pro",
652
710
  "deepseek-v4-flash",
653
711
  "glm-5.3",
712
+ "glm-5.3-flash",
654
713
  "glm-5.2",
655
714
  "kimi-k2.6",
656
715
  "minimax-m3",
@@ -659,6 +718,9 @@ const VOLCENGINE_AGENT_PLAN_MODELS = [
659
718
  const VOLCENGINE_PLAN_INPUT_MODALITIES: Record<string, string[]> = {
660
719
  "kimi-k2.6": ["text", "image"],
661
720
  "minimax-m3": ["text", "image"],
721
+ // Native VLM (docs.z.ai/guides/vlm/glm-5.3-flash), so it is declared here and left
722
+ // out of the text-only list below.
723
+ "glm-5.3-flash": ["text", "image"],
662
724
  };
663
725
  // Every other Plan model is text-only. Declaring this explicitly keeps the vision
664
726
  // sidecar from advertising image input for models that cannot accept it — the same
@@ -685,6 +747,7 @@ const ALIBABA_INTL_TOKEN_PLAN_INPUT_MODALITIES: Record<string, string[]> = {
685
747
  "kimi-k2.6": ["text", "image"],
686
748
  "kimi-k2.5": ["text", "image"],
687
749
  "glm-5.3": ["text"],
750
+ "glm-5.3-flash": ["text", "image"],
688
751
  "glm-5.2": ["text"],
689
752
  "glm-5.1": ["text"],
690
753
  "glm-5": ["text"],
@@ -775,6 +838,7 @@ const NVIDIA_NIM_VISION_MODELS = [
775
838
  "minimaxai/minimax-m3", "moonshotai/kimi-k2.6", "moonshotai/kimi-k2.5",
776
839
  "stepfun-ai/step-3.7-flash", "thinkingmachines/inkling",
777
840
  "mistralai/mistral-medium-3.5-128b",
841
+ "z-ai/glm-5.3-flash",
778
842
  ];
779
843
  /**
780
844
  * The catalog advertises image input only for `noVisionModels` members, so a natively
@@ -808,6 +872,10 @@ const NVIDIA_NIM_NO_VISION_MODELS = [
808
872
  "nvidia/nemotron-3-ultra-550b-a55b", "nvidia/nemotron-mini-4b-instruct",
809
873
  "nvidia/nvidia-nemotron-nano-9b-v2",
810
874
  "openai/gpt-oss-120b", "openai/gpt-oss-20b",
875
+ // z-ai/glm-5.3-flash belongs in NVIDIA_NIM_VISION_MODELS, not here: Z.AI documents
876
+ // it under docs.z.ai/guides/vlm/. The header above says an id must be classified
877
+ // deliberately rather than assumed from its name, and inheriting glm-5.3's
878
+ // text-only verdict because of the shared prefix is exactly that mistake.
811
879
  "poolside/laguna-xs-2.1", "z-ai/glm-5.3", "z-ai/glm-5.2",
812
880
  ];
813
881
  const KIMI_CODING_MODEL_CONTEXT_WINDOWS: Record<string, number> = Object.fromEntries(
@@ -817,7 +885,7 @@ const KIMI_CODING_MODEL_INPUT_MODALITIES = Object.fromEntries(
817
885
  KIMI_CODING_K3_MODELS.map(id => [id, ["text", "image"]]),
818
886
  );
819
887
  const NEURALWATT_REASONING_HISTORY_MODELS = [
820
- "glm-5.3", "glm-5.3-short",
888
+ "glm-5.3", "glm-5.3-short", "glm-5.3-flash",
821
889
  "glm-5.2", "glm-5.2-short",
822
890
  "kimi-k2.6", "kimi-k2.7-code",
823
891
  "qwen3.5-397b", "qwen3.6-35b",
@@ -898,6 +966,7 @@ const DIGITALOCEAN_CHAT_COMPLETION_MODELS = [
898
966
  "nemotron-nano-12b-v2-vl",
899
967
  "mimo-v2.5-pro",
900
968
  "glm-5.3",
969
+ "glm-5.3-flash",
901
970
  "glm-5.2",
902
971
  "glm-5.1",
903
972
  "glm-5",
@@ -906,6 +975,7 @@ const DIGITALOCEAN_CHAT_COMPLETION_MODELS = [
906
975
  ] as const;
907
976
  const SCALEWAY_SERVERLESS_CHAT_MODELS = [
908
977
  "glm-5.3",
978
+ "glm-5.3-flash",
909
979
  "glm-5.2",
910
980
  // gpt-oss-120b is intentionally omitted: Scaleway requires Responses API for tool calling,
911
981
  // while this preset routes Codex agent tools through Chat Completions.
@@ -927,6 +997,7 @@ const UMANS_MODELS = [
927
997
  "umans-kimi-k2.7",
928
998
  "umans-flash",
929
999
  "umans-glm-5.3",
1000
+ "umans-glm-5.3-flash",
930
1001
  "umans-glm-5.2",
931
1002
  "umans-glm-5.1",
932
1003
  "umans-qwen3.6-35b-a3b",
@@ -936,12 +1007,19 @@ const UMANS_GLM_REASONING_EFFORTS = ["high", "xhigh", "max"];
936
1007
  // 260814: Z.AI folds GLM-5.3 efforts into low/high/max, so `low` is a real tier here and
937
1008
  // `xhigh` is not distinct from `max` (docs.z.ai/devpack/latest-model).
938
1009
  const UMANS_GLM_53_REASONING_EFFORTS = ["low", "high", "max"];
1010
+ // `umans-glm-5.3-flash` is NOT here: Z.AI documents glm-5.3-flash under
1011
+ // docs.z.ai/guides/vlm/, so it takes images natively and does not need the proxy's
1012
+ // vision sidecar. The seeding pass classified it from the family name and a later
1013
+ // pass corrected only some of the providers; this is one it missed.
939
1014
  const UMANS_TEXT_ONLY_MODELS = ["umans-glm-5.3", "umans-glm-5.2", "umans-glm-5.1"];
940
1015
  const UMANS_MODEL_CONTEXT_WINDOWS: Record<string, number> = {
941
1016
  "umans-coder": 262_144,
942
1017
  "umans-kimi-k2.7": 262_144,
943
1018
  "umans-flash": 262_144,
944
1019
  "umans-glm-5.3": 405_504,
1020
+ // Mirrors the sibling this provider already carries. Umans has not published a
1021
+ // separate window for the flash tier; asserting a different number would be a guess.
1022
+ "umans-glm-5.3-flash": 405_504,
945
1023
  "umans-glm-5.2": 405_504,
946
1024
  "umans-glm-5.1": 202_752,
947
1025
  "umans-qwen3.6-35b-a3b": 262_144,
@@ -951,6 +1029,7 @@ const UMANS_MODEL_INPUT_MODALITIES: Record<string, string[]> = Object.fromEntrie
951
1029
  );
952
1030
  const CLINE_PASS_MODELS = [
953
1031
  "cline-pass/glm-5.3",
1032
+ "cline-pass/glm-5.3-flash",
954
1033
  "cline-pass/glm-5.2",
955
1034
  "cline-pass/kimi-k3",
956
1035
  "cline-pass/kimi-k2.7-code",
@@ -966,6 +1045,7 @@ const CLINE_PASS_MODELS = [
966
1045
  ];
967
1046
  const CLINE_PASS_MODEL_CONTEXT_WINDOWS: Record<string, number> = {
968
1047
  "cline-pass/glm-5.3": 1_048_576,
1048
+ "cline-pass/glm-5.3-flash": 1_048_576,
969
1049
  "cline-pass/glm-5.2": 1_048_576,
970
1050
  "cline-pass/kimi-k3": 1_048_576,
971
1051
  "cline-pass/kimi-k2.7-code": 262_144,
@@ -985,6 +1065,11 @@ const CLINE_PASS_IMAGE_MODELS = new Set([
985
1065
  "cline-pass/mimo-v2.5",
986
1066
  "cline-pass/minimax-m3",
987
1067
  "cline-pass/qwen3.7-plus",
1068
+ // Native VLM (docs.z.ai/guides/vlm/), so its images do not go through the proxy's
1069
+ // sidecar. Adding it here moves it out of CLINE_PASS_TEXT_ONLY_MODELS and flips its
1070
+ // declared modalities to ["text", "image"] in one edit, because both are derived
1071
+ // from this set.
1072
+ "cline-pass/glm-5.3-flash",
988
1073
  ]);
989
1074
  const CLINE_PASS_MODALITY_KNOWN_MODELS = CLINE_PASS_MODELS.filter(id => id !== "cline-pass/qwen3.8-max");
990
1075
  const CLINE_PASS_TEXT_ONLY_MODELS = CLINE_PASS_MODALITY_KNOWN_MODELS.filter(id => !CLINE_PASS_IMAGE_MODELS.has(id));
@@ -1067,7 +1152,18 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1067
1152
  // transport returns 400 ("Multi Agent requests are not allowed on chat completions").
1068
1153
  // 260813: grok-4.6 added per docs.x.ai/developers/grok-4-6. Context/vision still match
1069
1154
  // grok-4.5; the reasoning ladder does not — 4.6 adds the documented xhigh rung.
1070
- models: ["grok-4.6", "grok-4.5", "grok-4.3", "grok-4.20-0309-reasoning", "grok-4.20-0309-non-reasoning", "grok-build-0.1", "grok-composer-2.5-fast"],
1155
+ models: XAI_MODELS,
1156
+ // Measured only on grok-4.6 against cli-chat-proxy.grok.com: even an invalid
1157
+ // `text.verbosity` value is accepted and low/high/omitted output length is non-monotonic.
1158
+ // Apply the resulting opt-out to the whole xAI lineup because `text.verbosity` is an OpenAI
1159
+ // Responses parameter absent from xAI's documented API, not because every model was probed.
1160
+ // Keep this separate from reasoning-summary support: that bit gates Codex's
1161
+ // entire Responses reasoning object, including reasoning.effort.
1162
+ modelSupportsVerbosity: Object.fromEntries(XAI_MODELS.map(id => [id, false])),
1163
+ // Provider-wide, not merely per-model: `text.verbosity` is an OpenAI Responses parameter
1164
+ // absent from xAI's documented API, so a model discovered later has no more support for it
1165
+ // than the seeded ones do.
1166
+ supportsVerbosity: false,
1071
1167
  defaultModel: "grok-4.5",
1072
1168
  // Keep Codex Responses callers on the compatibility Chat wire until xAI can replay
1073
1169
  // opaque reasoning continuation and compaction state across later turns. The scoped
@@ -1142,17 +1238,12 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1142
1238
  // Unknown/new live models deliberately do not advertise a reasoning picker.
1143
1239
  reasoningEfforts: [],
1144
1240
  modelReasoningEfforts: COMMAND_CODE_MODEL_REASONING_EFFORTS,
1145
- // Ox Alpha (stealth preview, changelog v1.31.0): free 1M multimodal reasoning
1146
- // model on every plan. DeepSeek vision preview id is preemptive metadata —
1147
- // it is expected to merge into deepseek-v4-flash later.
1241
+ // The DeepSeek vision preview id is preemptive metadata it is expected to
1242
+ // merge into deepseek-v4-flash later.
1148
1243
  modelContextWindows: {
1149
- "stealth/ox-alpha": OX_ALPHA_CONTEXT_WINDOW,
1150
1244
  [`deepseek/${DEEPSEEK_VISION_PREVIEW_MODEL}`]: 1_048_576,
1151
1245
  },
1152
- modelInputModalities: {
1153
- "stealth/ox-alpha": ["text", "image"],
1154
- [`deepseek/${DEEPSEEK_VISION_PREVIEW_MODEL}`]: ["text", "image"],
1155
- },
1246
+ modelInputModalities: COMMAND_CODE_MODEL_INPUT_MODALITIES,
1156
1247
  defaultMaxOutputTokens: 64_000,
1157
1248
  // The proprietary generate wire has no verified per-request serialization flag.
1158
1249
  parallelToolCalls: false,
@@ -1237,6 +1328,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1237
1328
  // Per-model context metadata is maintained next to the Kiro model list.
1238
1329
  modelContextWindows: KIRO_MODEL_CONTEXT_WINDOWS,
1239
1330
  modelReasoningEfforts: KIRO_MODEL_REASONING_EFFORTS,
1331
+ modelSupportsVerbosity: Object.fromEntries(KIRO_MODELS.map(id => [id, false])),
1240
1332
  },
1241
1333
  {
1242
1334
  // Nous Portal — Nous Research subscription gateway (same backend Hermes Agent
@@ -1321,6 +1413,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1321
1413
  "umans-kimi-k2.7": UMANS_REASONING_EFFORTS,
1322
1414
  "umans-flash": UMANS_REASONING_EFFORTS,
1323
1415
  "umans-glm-5.3": UMANS_GLM_53_REASONING_EFFORTS,
1416
+ "umans-glm-5.3-flash": UMANS_GLM_53_REASONING_EFFORTS,
1324
1417
  "umans-glm-5.2": UMANS_GLM_REASONING_EFFORTS,
1325
1418
  "umans-glm-5.1": UMANS_GLM_REASONING_EFFORTS,
1326
1419
  "umans-qwen3.6-35b-a3b": UMANS_REASONING_EFFORTS,
@@ -1336,31 +1429,36 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1336
1429
  // finish_reason or [DONE] (#2260). The adapter still rejects incomplete argument JSON.
1337
1430
  openaiChatEofTolerance: true,
1338
1431
  /* [Decision Log]
1339
- - 목적과 의도: Route GPT 5.6 Luna to the Responses endpoint that OpenCode Go documents for that exact model.
1432
+ - 목적과 의도: Route the exact models OpenCode Go documents on the Responses endpoint GPT 5.6 Luna, and Muse Spark 1.2 Contributor (#2617).
1340
1433
  - 기존 구현 및 제약 조건: The provider is mixed-wire but its provider-wide `openai-chat` adapter sent Luna to `/chat/completions`; explicit user `modelAdapters` entries must remain authoritative.
1341
1434
  - 검토한 주요 대안: Change the whole provider to Responses; infer the wire from model-family names; add one registry-only exact-model default.
1342
- - 선택한 방식: Declare only `gpt-5.6-luna` as `openai-responses` through the existing registry default mechanism.
1435
+ - 선택한 방식: Declare only the named models as `openai-responses` through the existing registry default mechanism; the map stays an exact-model allowlist rather than a family or provider-wide rule.
1343
1436
  - 다른 대안 대신 이 방식을 선택한 이유: OpenCode Go documents sibling models on Chat or Anthropic endpoints, and an exact registry default preserves both those routes and explicit opt-out precedence.
1344
- - 장점, 단점 및 영향: Luna reaches `/responses` from every inbound surface without changing siblings; a future upstream endpoint change requires an evidence-backed registry update.
1437
+ - 장점, 단점 및 영향: Each listed model reaches `/responses` from every inbound surface without changing siblings; a future upstream endpoint change requires an evidence-backed registry update.
1345
1438
  */
1346
- modelWireDefaults: { "gpt-5.6-luna": "openai-responses" },
1439
+ modelWireDefaults: { "gpt-5.6-luna": "openai-responses", "muse-spark-1.2-contributor": "openai-responses" },
1347
1440
  modelContextWindows: {
1348
1441
  "kimi-k3": KIMI_K3_STANDARD_CONTEXT_WINDOW,
1349
- // Ox Alpha (stealth 1M multimodal) and the DeepSeek vision preview are
1350
- // metadata-only here: the Go roster is discovered live, so these apply
1351
- // the moment the gateway starts serving the ids.
1352
- [OPENCODE_OX_ALPHA_FREE_MODEL]: OX_ALPHA_CONTEXT_WINDOW,
1442
+ // The DeepSeek vision preview id is metadata-only here: the Go roster is
1443
+ // discovered live, so it applies the moment the gateway serves the id.
1353
1444
  [DEEPSEEK_VISION_PREVIEW_MODEL]: 1_048_576,
1354
1445
  },
1355
1446
  modelInputModalities: {
1356
1447
  "kimi-k3": ["text", "image"],
1357
- [OPENCODE_OX_ALPHA_FREE_MODEL]: ["text", "image"],
1358
1448
  // Experimental DeepSeek vision preview — expected to merge into deepseek-v4-flash later.
1359
1449
  [DEEPSEEK_VISION_PREVIEW_MODEL]: ["text", "image"],
1450
+ // Muse Spark 1.2 Contributor is natively multimodal on Zen Go: it accepts input_image
1451
+ // parts over /responses (probed 2026-08-26). Without this declaration the catalog
1452
+ // advertises it text-only and the Codex app blocks image attachments client-side with
1453
+ // "This model does not support image inputs" before the request ever reaches the proxy.
1454
+ "muse-spark-1.2-contributor": ["text", "image"],
1360
1455
  },
1361
1456
  modelReasoningEfforts: {
1457
+ "gpt-5.6-luna": OPENAI_API_GPT56_REASONING_EFFORTS,
1362
1458
  "glm-5.3": ZAI_GLM_53_REASONING_EFFORTS,
1459
+ "glm-5.3-flash": ZAI_GLM_53_REASONING_EFFORTS,
1363
1460
  "glm-5.2": ZAI_GLM_52_REASONING_EFFORTS,
1461
+ "qwen3.8-max": QWEN38_REASONING_EFFORTS,
1364
1462
  "kimi-k3": KIMI_CODING_K3_REASONING_EFFORTS,
1365
1463
  "kimi-k2.7-code": [],
1366
1464
  "kimi-k2.7-code-highspeed": [],
@@ -1378,6 +1476,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1378
1476
  },
1379
1477
  modelSupportsReasoningSummaries: {
1380
1478
  "glm-5.3": true,
1479
+ "glm-5.3-flash": true,
1381
1480
  "glm-5.2": true,
1382
1481
  "glm-5.1": true,
1383
1482
  "glm-5": true,
@@ -1401,7 +1500,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1401
1500
  noPenaltyModels: ["kimi-k3", "kimi-k2.7-code", "kimi-k2.7-code-highspeed"],
1402
1501
  autoToolChoiceOnlyModels: ["kimi-k2.7-code", "kimi-k2.7-code-highspeed"],
1403
1502
  // Issue #78: DeepSeek V4 thinking mode requires reasoning_content replay on tool-call turns.
1404
- preserveReasoningContentModels: ["glm-5.3", "glm-5.2", "kimi-k3", "kimi-k2.7-code", "kimi-k2.7-code-highspeed", ...DEEPSEEK_THINKING_MODELS],
1503
+ preserveReasoningContentModels: ["glm-5.3", "glm-5.3-flash", "glm-5.2", "kimi-k3", "kimi-k2.7-code", "kimi-k2.7-code-highspeed", ...DEEPSEEK_THINKING_MODELS],
1405
1504
  },
1406
1505
  {
1407
1506
  id: "neuralwatt",
@@ -1417,6 +1516,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1417
1516
  // Evidence: devlog/_plan/260710_provider_hardening/003_research_aggregators.md and https://api.neuralwatt.com/v1/models.
1418
1517
  models: [
1419
1518
  "glm-5.3", "glm-5.3-fast", "glm-5.3-short", "glm-5.3-short-fast",
1519
+ "glm-5.3-flash",
1420
1520
  "glm-5.2", "glm-5.2-fast", "glm-5.2-short", "glm-5.2-short-fast",
1421
1521
  "kimi-k2.6", "kimi-k2.6-fast",
1422
1522
  "kimi-k2.7-code",
@@ -1428,6 +1528,9 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1428
1528
  "glm-5.3-fast": [],
1429
1529
  "glm-5.3-short": ZAI_GLM_53_REASONING_EFFORTS,
1430
1530
  "glm-5.3-short-fast": [],
1531
+ // No `-fast`/`-short` variants are asserted for the flash tier: those suffixes
1532
+ // encode routing Neuralwatt documents per model, and this seed has no source for them.
1533
+ "glm-5.3-flash": ZAI_GLM_53_REASONING_EFFORTS,
1431
1534
  "glm-5.2": ZAI_GLM_52_REASONING_EFFORTS,
1432
1535
  "glm-5.2-fast": [],
1433
1536
  "glm-5.2-short": ZAI_GLM_52_REASONING_EFFORTS,
@@ -1460,16 +1563,11 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1460
1563
  featured: true,
1461
1564
  dashboardUrl: "https://openrouter.ai/keys",
1462
1565
  jawcodeBundle: "openrouter",
1463
- // stealth/ox-alpha: free stealth-window frontier model (launched 2026-08-20).
1464
- // /api/v1/models reports 1,048,576 context, 131,072 max output, text+image+video
1465
- // input, $0 pricing, mandatory reasoning. Single provider slug: `stealth`.
1466
- models: ["anthropic/claude-sonnet-5", "stealth/ox-alpha", ...OPENROUTER_GPT56_MODELS],
1566
+ models: ["anthropic/claude-sonnet-5", ...OPENROUTER_GPT56_MODELS],
1467
1567
  modelContextWindows: {
1468
1568
  "anthropic/claude-sonnet-5": 1_000_000,
1469
- "stealth/ox-alpha": OX_ALPHA_CONTEXT_WINDOW,
1470
1569
  ...OPENROUTER_GPT56_CONTEXT_WINDOWS,
1471
1570
  },
1472
- modelInputModalities: { "stealth/ox-alpha": ["text", "image"] },
1473
1571
  // OpenRouter documents priority support for OpenAI endpoints, but not Anthropic. Keep the
1474
1572
  // provider unclassified and opt in only the exact OpenAI-backed slugs we ship. These facts
1475
1573
  // belong only to the canonical destination; a same-named custom gateway is unknown to us.
@@ -1880,17 +1978,12 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1880
1978
  // slash ids — so a Codex-facing slug like `commandcode/deepseek-deepseek-v4-pro`
1881
1979
  // is sent upstream verbatim and rejected with `unsupported_model`.
1882
1980
  modelReasoningEfforts: COMMAND_CODE_MODEL_REASONING_EFFORTS,
1883
- // Ox Alpha (stealth preview, Command Code changelog v1.31.0) ships with a
1884
- // 1.05M-token multimodal context; the DeepSeek vision preview id is
1885
- // preemptive for when the catalog serves it (merges into v4-flash later).
1981
+ // The DeepSeek vision preview id is preemptive for when the catalog serves it
1982
+ // (merges into v4-flash later).
1886
1983
  modelContextWindows: {
1887
- "stealth/ox-alpha": OX_ALPHA_CONTEXT_WINDOW,
1888
1984
  [`deepseek/${DEEPSEEK_VISION_PREVIEW_MODEL}`]: 1_048_576,
1889
1985
  },
1890
- modelInputModalities: {
1891
- "stealth/ox-alpha": ["text", "image"],
1892
- [`deepseek/${DEEPSEEK_VISION_PREVIEW_MODEL}`]: ["text", "image"],
1893
- },
1986
+ modelInputModalities: COMMAND_CODE_MODEL_INPUT_MODALITIES,
1894
1987
  modelDiscovery: {
1895
1988
  path: "models",
1896
1989
  maxResponseBytes: 256 * 1024,
@@ -2147,11 +2240,11 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
2147
2240
  id: "zai", label: "Z.AI — GLM Coding Plan", baseUrl: "https://api.z.ai/api/coding/paas/v4", adapter: "openai-chat", authKind: "key",
2148
2241
  dashboardUrl: "https://z.ai/manage-apikey/apikey-list", defaultModel: "glm-5.3",
2149
2242
  note: "GLM-5.3 coding subscription",
2150
- models: ["glm-5.3", "glm-5.3[1m]", "glm-5.2", "glm-5.2[1m]", "glm-5.1", "glm-5", "glm-4.6"],
2151
- modelContextWindows: { "glm-5.3": 1_000_000, "glm-5.3[1m]": 1_000_000, "glm-5.2": 1_000_000, "glm-5.2[1m]": 1_000_000 },
2243
+ 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"],
2244
+ 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 },
2152
2245
  // Z.AI's OpenAI path returns 400 code 1211 for bracketed model ids.
2153
2246
  modelSuffixBracketStrip: true,
2154
- noVisionModels: ZAI_GLM_5X_MODELS,
2247
+ noVisionModels: ZAI_GLM_5X_SIDECAR_VISION_MODELS,
2155
2248
  modelReasoningEfforts: ZAI_GLM_5X_REASONING_EFFORTS,
2156
2249
  modelDefaultReasoningEfforts: Object.fromEntries(ZAI_GLM_53_MODELS.map(id => [id, "max"])),
2157
2250
  modelMaxOutputTokens: Object.fromEntries(ZAI_GLM_53_MODELS.map(id => [id, 131_072])),
@@ -2228,11 +2321,11 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
2228
2321
  authKind: "key",
2229
2322
  dashboardUrl: "https://bigmodel.cn/console/usercenter/apikeys",
2230
2323
  defaultModel: "glm-5.3",
2231
- models: ["glm-5.3", "glm-5.3[1m]", "glm-5.2", "glm-5.2[1m]", "glm-5.1", "glm-5", "glm-4.6"],
2324
+ 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"],
2232
2325
  jawcodeBundle: "zai",
2233
- modelContextWindows: { "glm-5.3": 1_000_000, "glm-5.3[1m]": 1_000_000, "glm-5.2": 1_000_000, "glm-5.2[1m]": 1_000_000 },
2326
+ 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 },
2234
2327
  modelSuffixBracketStrip: true,
2235
- noVisionModels: ZAI_GLM_5X_MODELS,
2328
+ noVisionModels: ZAI_GLM_5X_SIDECAR_VISION_MODELS,
2236
2329
  modelReasoningEfforts: ZAI_GLM_5X_REASONING_EFFORTS,
2237
2330
  modelSupportsReasoningSummaries: Object.fromEntries(ZAI_GLM_5X_MODELS.map(id => [id, true])),
2238
2331
  preserveReasoningContentModels: ZAI_GLM_5X_MODELS,
@@ -2374,12 +2467,13 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
2374
2467
  modelInputModalities: ALIBABA_TOKEN_PLAN_INPUT_MODALITIES,
2375
2468
  modelContextWindows: {
2376
2469
  "qwen3.8-max": 983_616, "qwen3.7-max": 1_000_000, "qwen3.7-plus": 1_000_000,
2377
- "qwen3.6-flash": 1_000_000, "glm-5.3": 1_000_000, "glm-5.2": 1_000_000, "deepseek-v4-pro": 1_000_000,
2470
+ "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,
2378
2471
  },
2379
2472
  modelReasoningEfforts: {
2380
2473
  ...Object.fromEntries(ALIBABA_TOKEN_PLAN_QWEN_MODELS.map(id => [id, THINKING_BUDGET_EFFORTS])),
2381
2474
  "qwen3.8-max": QWEN38_REASONING_EFFORTS,
2382
2475
  "glm-5.3": ZAI_GLM_53_REASONING_EFFORTS,
2476
+ "glm-5.3-flash": ZAI_GLM_53_REASONING_EFFORTS,
2383
2477
  "glm-5.2": ZAI_GLM_52_REASONING_EFFORTS,
2384
2478
  "deepseek-v4-pro": deepseekThinkingEffortsFor("deepseek-v4-pro"),
2385
2479
  },
@@ -2387,7 +2481,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
2387
2481
  modelReasoningEffortMap: { "deepseek-v4-pro": deepseekReasoningMapFor("deepseek-v4-pro") },
2388
2482
  directReasoningEffortModels: ["qwen3.8-max"],
2389
2483
  thinkingBudgetModels: ALIBABA_TOKEN_PLAN_QWEN_MODELS.filter(id => id !== "qwen3.8-max"),
2390
- preserveReasoningContentModels: ["glm-5.3", "glm-5.2", "deepseek-v4-pro", "qwen3.8-max", "qwen3.7-max", "qwen3.7-plus", "qwen3.6-flash"],
2484
+ 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"],
2391
2485
  noVisionModels: ["glm-5.3", "glm-5.2", "deepseek-v4-pro"],
2392
2486
  },
2393
2487
  {
@@ -2410,13 +2504,14 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
2410
2504
  "qwen3.7-max": 1_000_000, "qwen3.7-plus": 1_000_000, "qwen3.6-plus": 1_000_000, "qwen3.6-flash": 1_000_000,
2411
2505
  "deepseek-v4-pro": 1_000_000, "deepseek-v4-flash": 1_000_000, "deepseek-v3.2": 131_072,
2412
2506
  "kimi-k2.7-code": 262_144, "kimi-k2.6": 262_144, "kimi-k2.5": 262_144,
2413
- "glm-5.3": 1_000_000, "glm-5.2": 1_000_000, "glm-5.1": 1_000_000, "glm-5": 1_000_000,
2507
+ "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,
2414
2508
  "MiniMax-M2.5": 204_800,
2415
2509
  },
2416
2510
  modelReasoningEfforts: {
2417
2511
  ...Object.fromEntries(ALIBABA_INTL_TOKEN_PLAN_QWEN_MODELS.map(id => [id, THINKING_BUDGET_EFFORTS])),
2418
2512
  "qwen3.8-max": QWEN38_REASONING_EFFORTS,
2419
2513
  "glm-5.3": ZAI_GLM_53_REASONING_EFFORTS,
2514
+ "glm-5.3-flash": ZAI_GLM_53_REASONING_EFFORTS,
2420
2515
  "glm-5.2": ZAI_GLM_52_REASONING_EFFORTS,
2421
2516
  "deepseek-v4-pro": deepseekThinkingEffortsFor("deepseek-v4-pro"),
2422
2517
  "deepseek-v4-flash": deepseekThinkingEffortsFor("deepseek-v4-flash"),
@@ -2427,7 +2522,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
2427
2522
  },
2428
2523
  directReasoningEffortModels: ["qwen3.8-max"],
2429
2524
  thinkingBudgetModels: ALIBABA_INTL_TOKEN_PLAN_QWEN_MODELS.filter(id => id !== "qwen3.8-max"),
2430
- preserveReasoningContentModels: ["glm-5.3", "glm-5.2", "deepseek-v4-pro", "deepseek-v4-flash", "qwen3.8-max", "qwen3.7-max", "qwen3.7-plus", "qwen3.6-plus", "qwen3.6-flash"],
2525
+ 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"],
2431
2526
  noVisionModels: ["deepseek-v4-pro", "deepseek-v4-flash", "deepseek-v3.2", "glm-5.3", "glm-5.2", "glm-5.1", "glm-5", "MiniMax-M2.5"],
2432
2527
  noReasoningModels: ["kimi-k2.7-code", "kimi-k2.6", "kimi-k2.5", "deepseek-v3.2", "glm-5.1", "glm-5", "MiniMax-M2.5"],
2433
2528
  modelDefaultReasoningEfforts: { "qwen3.8-max": "xhigh" },
@@ -2460,9 +2555,11 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
2460
2555
  authKind: "key",
2461
2556
  dashboardUrl: "https://ollama.com/settings/keys",
2462
2557
  // Live IDs verified 2026-07-10; qwen3-coder:480b retires 2026-07-15.
2463
- models: ["glm-5.3", "glm-5.2", "deepseek-v4-pro", "qwen3-coder:480b", "gpt-oss:120b", "kimi-k2.6", "minimax-m3", "qwen3.5:397b", "gemma4:31b"],
2558
+ 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"],
2464
2559
  defaultModel: "glm-5.3",
2465
2560
  noVisionModels: [
2561
+ // glm-5.3-flash is absent on purpose: native VLM
2562
+ // (docs.z.ai/guides/vlm/glm-5.3-flash), so its images skip the sidecar.
2466
2563
  "glm-5.3", "glm-5.2", "glm-5.1", "glm-5", "glm-4.7",
2467
2564
  "minimax-m2.7", "minimax-m2.5", "minimax-m2.1",
2468
2565
  "nemotron-3-ultra", "nemotron-3-super",
@@ -2535,14 +2632,12 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
2535
2632
  [...DEEPSEEK_THINKING_MODELS, ...OPENCODE_FREE_DEEPSEEK_MODELS].map(id => [id, deepseekReasoningMapFor(id)]),
2536
2633
  ),
2537
2634
  preserveReasoningContentModels: [...DEEPSEEK_THINKING_MODELS, ...OPENCODE_FREE_DEEPSEEK_MODELS],
2538
- // Same Zen gateway as opencode-free: Ox Alpha Free (1M multimodal stealth model)
2539
- // and the DeepSeek vision preview (merges into deepseek-v4-flash later).
2635
+ // Same Zen gateway as opencode-free: the DeepSeek vision preview id
2636
+ // (merges into deepseek-v4-flash later).
2540
2637
  modelContextWindows: {
2541
- [OPENCODE_OX_ALPHA_FREE_MODEL]: OX_ALPHA_CONTEXT_WINDOW,
2542
2638
  [DEEPSEEK_VISION_PREVIEW_MODEL]: 1_048_576,
2543
2639
  },
2544
2640
  modelInputModalities: {
2545
- [OPENCODE_OX_ALPHA_FREE_MODEL]: ["text", "image"],
2546
2641
  [DEEPSEEK_VISION_PREVIEW_MODEL]: ["text", "image"],
2547
2642
  },
2548
2643
  noVisionModels: [...OPENCODE_ZEN_TEXT_ONLY_MODELS, ...DEEPSEEK_THINKING_MODELS],
@@ -2575,16 +2670,12 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
2575
2670
  modelReasoningEfforts: Object.fromEntries(OPENCODE_FREE_DEEPSEEK_MODELS.map(id => [id, deepseekThinkingEffortsFor(id)])),
2576
2671
  modelReasoningEffortMap: Object.fromEntries(OPENCODE_FREE_DEEPSEEK_MODELS.map(id => [id, deepseekReasoningMapFor(id)])),
2577
2672
  preserveReasoningContentModels: OPENCODE_FREE_DEEPSEEK_MODELS,
2578
- // Ox Alpha Free (`x-preview-f-free`): the OpenRouter stealth model on Zen's
2579
- // free tier 1,048,576 context, text+image input. Deliberately NOT in the
2580
- // text-only list below. The DeepSeek vision preview id is preemptive
2581
- // metadata for when Zen starts serving it (merges into v4-flash later).
2673
+ // The DeepSeek vision preview id is preemptive metadata for when Zen starts
2674
+ // serving it (merges into v4-flash later).
2582
2675
  modelContextWindows: {
2583
- [OPENCODE_OX_ALPHA_FREE_MODEL]: OX_ALPHA_CONTEXT_WINDOW,
2584
2676
  [DEEPSEEK_VISION_PREVIEW_MODEL]: 1_048_576,
2585
2677
  },
2586
2678
  modelInputModalities: {
2587
- [OPENCODE_OX_ALPHA_FREE_MODEL]: ["text", "image"],
2588
2679
  [DEEPSEEK_VISION_PREVIEW_MODEL]: ["text", "image"],
2589
2680
  },
2590
2681
  // Same Zen roster behind the same base URL, so it carries the same measured
@@ -2676,6 +2767,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
2676
2767
  "@cf/deepseek-ai/deepseek-r1-distill-qwen-32b",
2677
2768
  "@cf/moonshotai/kimi-k2.7-code",
2678
2769
  "@cf/zai-org/glm-5.3",
2770
+ "@cf/zai-org/glm-5.3-flash",
2679
2771
  "@cf/zai-org/glm-5.2",
2680
2772
  "@cf/mistralai/mistral-small-3.1-24b-instruct",
2681
2773
  ],
@@ -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
+ }