@bitkyc08/opencodex 2.63.0 → 2.64.0-preview.20260923

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 (262) hide show
  1. package/AGENTS_INSTALL.md +4 -3
  2. package/README.md +46 -33
  3. package/assets/download-linux.svg +10 -0
  4. package/assets/download-macos.svg +10 -0
  5. package/assets/download-windows.svg +10 -0
  6. package/bin/ocx.mjs +12 -4
  7. package/gui/dist/assets/App-D5eN1TID.js +50 -0
  8. package/gui/dist/assets/App-I5AnaSLh.css +1 -0
  9. package/gui/dist/assets/{Tray-CncKDBTp.js → Tray-Bh_ErQDh.js} +1 -1
  10. package/gui/dist/assets/index-BEq4OCOz.js +86 -0
  11. package/gui/dist/assets/index-DiBRuK-d.css +1 -0
  12. package/gui/dist/assets/usage-companion-chart-DBG_37kQ.js +1 -0
  13. package/gui/dist/index.html +2 -2
  14. package/native/remote-workspace-helper/src/protocol.rs +5 -0
  15. package/package.json +4 -1
  16. package/src/adapters/anthropic.ts +66 -7
  17. package/src/adapters/codebuddy/adapter.ts +112 -16
  18. package/src/adapters/codebuddy/mcp-server.ts +180 -0
  19. package/src/adapters/codebuddy/scaffold-guard.ts +25 -8
  20. package/src/adapters/codebuddy/tool-bridge.ts +597 -0
  21. package/src/adapters/coding-agent/protocol.ts +123 -10
  22. package/src/adapters/coding-agent/turn.ts +324 -2
  23. package/src/adapters/command-code-restored-schema.ts +112 -0
  24. package/src/adapters/command-code-tool-text.ts +589 -0
  25. package/src/adapters/command-code.ts +80 -6
  26. package/src/adapters/cursor/catalog.ts +12 -0
  27. package/src/adapters/cursor/current-request.ts +46 -0
  28. package/src/adapters/cursor/discovery.ts +1 -1
  29. package/src/adapters/cursor/effort-map.ts +4 -0
  30. package/src/adapters/cursor/native-exec.ts +15 -0
  31. package/src/adapters/cursor/protobuf-events.ts +40 -12
  32. package/src/adapters/cursor/protobuf-request.ts +83 -18
  33. package/src/adapters/cursor/request-builder.ts +5 -4
  34. package/src/adapters/cursor/tool-guidance.ts +1 -1
  35. package/src/adapters/devin/live-models.ts +7 -0
  36. package/src/adapters/inline-think-tags.ts +251 -0
  37. package/src/adapters/kiro/adapter.ts +1 -0
  38. package/src/adapters/kiro/stream.ts +5 -3
  39. package/src/adapters/kiro/usage.ts +4 -3
  40. package/src/adapters/mimo-free.ts +37 -18
  41. package/src/adapters/openai-chat/messages.ts +5 -5
  42. package/src/adapters/openai-chat/tool-schema.ts +124 -2
  43. package/src/adapters/openai-chat.ts +13 -3
  44. package/src/adapters/openai-responses/passthrough.ts +15 -2
  45. package/src/adapters/openai-responses/reasoning.ts +14 -1
  46. package/src/adapters/openai-responses/request-strips.ts +29 -3
  47. package/src/adapters/openai-responses/tool-output-recovery.ts +9 -3
  48. package/src/adapters/qoder/adapter.ts +15 -12
  49. package/src/adapters/qoder/scaffold-guard.ts +45 -13
  50. package/src/bridge/internal.ts +20 -0
  51. package/src/bridge/sse.ts +14 -5
  52. package/src/claude/agents-inject.ts +2 -1
  53. package/src/claude/desktop-applied-marker.ts +44 -0
  54. package/src/claude/desktop-profile.ts +22 -12
  55. package/src/claude/inbound.ts +8 -3
  56. package/src/cli/access.ts +22 -5
  57. package/src/cli/account-api.ts +6 -0
  58. package/src/cli/account-auth.ts +5 -2
  59. package/src/cli/account.ts +5 -6
  60. package/src/cli/aside-profiles.ts +61 -1
  61. package/src/cli/claude-desktop.ts +3 -2
  62. package/src/cli/claude.ts +3 -1
  63. package/src/cli/codex-cli-update.ts +2 -2
  64. package/src/cli/codex-shim-autorestore.ts +3 -0
  65. package/src/cli/dispatch.ts +12 -4
  66. package/src/cli/index.ts +98 -10
  67. package/src/cli/registry.ts +1 -1
  68. package/src/cli/resolve.ts +113 -1
  69. package/src/cli/root.ts +5 -0
  70. package/src/cli/stop-approval.ts +186 -0
  71. package/src/cli/stop-report.ts +1 -1
  72. package/src/cli/system-restart-client.ts +19 -4
  73. package/src/client/hub-client.ts +25 -18
  74. package/src/clients/config-export.ts +12 -4
  75. package/src/codex/account-auto-switch.ts +59 -0
  76. package/src/codex/account-lifecycle.ts +7 -0
  77. package/src/codex/account-priority.ts +10 -0
  78. package/src/codex/account-usability.ts +9 -0
  79. package/src/codex/auth-api/account-list.ts +5 -0
  80. package/src/codex/auth-api/login-flow.ts +15 -5
  81. package/src/codex/auth-api/login-state.ts +5 -16
  82. package/src/codex/auth-api/pool-mode-gate.ts +30 -7
  83. package/src/codex/auth-api/routes.ts +39 -3
  84. package/src/codex/auth-context.ts +145 -17
  85. package/src/codex/catalog/build-entries.ts +2 -1
  86. package/src/codex/catalog/effort.ts +8 -2
  87. package/src/codex/catalog/metadata.ts +33 -7
  88. package/src/codex/catalog/native-models.ts +102 -5
  89. package/src/codex/catalog/parsing.ts +2 -0
  90. package/src/codex/catalog/provider-models.ts +19 -6
  91. package/src/codex/cli-installation-targets.ts +44 -40
  92. package/src/codex/codex-write-lock.ts +32 -2
  93. package/src/codex/inject/multi-agent-v2.ts +90 -0
  94. package/src/codex/inject/plan.ts +365 -0
  95. package/src/codex/inject.ts +237 -362
  96. package/src/codex/log-guard/maintenance.ts +30 -13
  97. package/src/codex/project-config-warnings.ts +47 -7
  98. package/src/codex/prompt-layers/encoding.ts +41 -0
  99. package/src/codex/prompt-layers/toml-read.ts +1 -1
  100. package/src/codex/prompt-layers.ts +17 -13
  101. package/src/codex/prompt-text-probe.ts +20 -10
  102. package/src/codex/quota-observation-freshness.ts +34 -0
  103. package/src/codex/quota-rejection.ts +8 -4
  104. package/src/codex/quota.ts +4 -2
  105. package/src/codex/routing/selection.ts +32 -9
  106. package/src/codex/routing.ts +53 -11
  107. package/src/codex/subagent-model-fallback.ts +4 -3
  108. package/src/codex/sync.ts +11 -0
  109. package/src/codex/windows-installation-files.ts +33 -11
  110. package/src/codex/write-coordination.ts +4 -0
  111. package/src/combos/request.ts +6 -2
  112. package/src/companion/settings.ts +6 -1
  113. package/src/config/atomic-write.ts +12 -1
  114. package/src/config/derived-registries.ts +29 -0
  115. package/src/config/diagnostics.ts +17 -0
  116. package/src/config/live-reconcile.ts +11 -2
  117. package/src/config/load-degrade.ts +9 -2
  118. package/src/config/persist-unlocked.ts +4 -3
  119. package/src/config/persisted-mutation.ts +94 -0
  120. package/src/config/provider-validation.ts +1 -1
  121. package/src/config/proxy-env.ts +62 -12
  122. package/src/config/rebase-provenance.ts +80 -1
  123. package/src/config/schema/config-schema.ts +5 -0
  124. package/src/config/schema/leaf-validators.ts +50 -1
  125. package/src/config/subagent-models.ts +41 -15
  126. package/src/config.ts +18 -95
  127. package/src/generated/compatibility-version.json +330 -218
  128. package/src/generated/model-metadata.ts +8 -5
  129. package/src/github/star-state.ts +46 -6
  130. package/src/grok/inject.ts +74 -40
  131. package/src/grok/status.ts +11 -4
  132. package/src/integrations/cursor-effort-table.ts +50 -13
  133. package/src/integrations/raycast-detect.ts +19 -4
  134. package/src/integrations/serialize.ts +11 -1
  135. package/src/lib/bounded-body.ts +30 -0
  136. package/src/lib/crash-guard.ts +52 -4
  137. package/src/lib/local-aside-sync-contract.ts +41 -0
  138. package/src/lib/package-tree-integrity.ts +181 -9
  139. package/src/lib/proxy-env.ts +18 -1
  140. package/src/lib/request-failure-attribution.ts +1 -0
  141. package/src/lib/request-failure-model.ts +3 -0
  142. package/src/lib/service-secrets.ts +99 -2
  143. package/src/lib/socks5-fetch.ts +43 -14
  144. package/src/lib/token-estimate.ts +17 -2
  145. package/src/oauth/command-code.ts +3 -2
  146. package/src/oauth/generic-account-failover.ts +29 -1
  147. package/src/oauth/index.ts +24 -16
  148. package/src/oauth/login-flow-state.ts +5 -5
  149. package/src/oauth/meta-muse-device.ts +49 -7
  150. package/src/oauth/store.ts +74 -9
  151. package/src/providers/alibaba-region-backup.ts +16 -1
  152. package/src/providers/anthropic-fast.ts +89 -0
  153. package/src/providers/anthropic-reset-grant-ledger.ts +288 -0
  154. package/src/providers/anthropic-reset-grants.ts +329 -0
  155. package/src/providers/claude-cli-identity.ts +12 -0
  156. package/src/providers/command-code-efforts.ts +184 -102
  157. package/src/providers/derive.ts +11 -3
  158. package/src/providers/fastwire.ts +16 -3
  159. package/src/providers/label.ts +8 -3
  160. package/src/providers/model-rename-fields.ts +1 -0
  161. package/src/providers/openai-virtual-models.ts +1 -0
  162. package/src/providers/quota/vendor-probes-oauth.ts +2 -1
  163. package/src/providers/reasoning-metadata.ts +38 -15
  164. package/src/providers/registry/entries-core.ts +95 -7
  165. package/src/providers/registry/entries-extended.ts +21 -10
  166. package/src/providers/registry/model-ids.ts +1 -0
  167. package/src/providers/registry/model-seeds.ts +31 -3
  168. package/src/providers/registry/types.ts +3 -1
  169. package/src/providers/resolved-model-policy-merge.ts +38 -8
  170. package/src/providers/resolved-model-policy.ts +4 -2
  171. package/src/providers/service-tier.ts +3 -1
  172. package/src/reasoning-effort.ts +6 -5
  173. package/src/responses/code-mode-helper-compat.ts +14 -4
  174. package/src/responses/code-mode-shell-input.ts +54 -0
  175. package/src/responses/custom-tool-compat.ts +173 -5
  176. package/src/responses/parser.ts +38 -2
  177. package/src/responses/reasoning-replay-cache.ts +26 -0
  178. package/src/router.ts +11 -1
  179. package/src/routing/history/indexer.ts +7 -2
  180. package/src/routing/history/schema.ts +3 -1
  181. package/src/server/adapter-resolve.ts +2 -2
  182. package/src/server/auth-cors.ts +3 -0
  183. package/src/server/chat-completions.ts +14 -1
  184. package/src/server/chat-native.ts +17 -0
  185. package/src/server/claude-messages.ts +4 -3
  186. package/src/server/direct-local-http.ts +45 -19
  187. package/src/server/gui-session.ts +6 -15
  188. package/src/server/index/package-tree-guard.ts +53 -0
  189. package/src/server/index/serve-options.ts +17 -3
  190. package/src/server/index/startup-warnings.ts +17 -0
  191. package/src/server/index.ts +15 -17
  192. package/src/server/lifecycle.ts +8 -0
  193. package/src/server/live.ts +54 -4
  194. package/src/server/management/agent-settings-routes.ts +44 -8
  195. package/src/server/management/anthropic-reset-grant-routes.ts +252 -0
  196. package/src/server/management/codex-prompt-routes.ts +5 -1
  197. package/src/server/management/config-routes.ts +14 -4
  198. package/src/server/management/oauth-account-routes.ts +17 -1
  199. package/src/server/management/provider-overwrite-carry.ts +164 -0
  200. package/src/server/management/provider-routes.ts +36 -6
  201. package/src/server/management/remote-workspace-routes.ts +2 -2
  202. package/src/server/management/route-registry.ts +2 -0
  203. package/src/server/management/shadow-call-validation.ts +39 -0
  204. package/src/server/management/system-restart.ts +72 -6
  205. package/src/server/management-api.ts +16 -2
  206. package/src/server/management-auth.ts +52 -8
  207. package/src/server/proxy-liveness.ts +110 -4
  208. package/src/server/request-log.ts +26 -9
  209. package/src/server/request-metrics.ts +5 -0
  210. package/src/server/responses/adapter-continuation.ts +6 -3
  211. package/src/server/responses/adapter-dispatch.ts +65 -1
  212. package/src/server/responses/compact.ts +31 -1
  213. package/src/server/responses/compaction-routing.ts +28 -3
  214. package/src/server/responses/core-codex-account.ts +85 -26
  215. package/src/server/responses/core-combo-failure.ts +16 -15
  216. package/src/server/responses/core-normalize.ts +5 -1
  217. package/src/server/responses/core-opaque-recovery.ts +50 -2
  218. package/src/server/responses/core-options.ts +2 -0
  219. package/src/server/responses/core-replay.ts +7 -0
  220. package/src/server/responses/core.ts +1 -1
  221. package/src/server/responses/encrypted-payload.ts +23 -9
  222. package/src/server/responses/passthrough-delivery.ts +55 -35
  223. package/src/server/responses/passthrough-dispatch.ts +12 -13
  224. package/src/server/responses/policy-fallback.ts +12 -1
  225. package/src/server/responses/request-prepare.ts +46 -7
  226. package/src/server/responses/request-spend.ts +31 -15
  227. package/src/server/responses/run-turn-execution.ts +6 -5
  228. package/src/server/responses/shadow-target-availability.ts +61 -0
  229. package/src/server/responses-custom-tool-repair.ts +2 -0
  230. package/src/service/claim.ts +185 -0
  231. package/src/service/cli.ts +10 -1
  232. package/src/service/guarded-manager-target.ts +151 -0
  233. package/src/service/guards.ts +47 -48
  234. package/src/service/managing-cli.ts +170 -0
  235. package/src/service/orchestration.ts +3 -1
  236. package/src/service/state.ts +4 -0
  237. package/src/service/systemd.ts +16 -1
  238. package/src/service.ts +1 -1
  239. package/src/types/config.ts +8 -0
  240. package/src/types/provider.ts +16 -0
  241. package/src/types/request.ts +10 -0
  242. package/src/types/tools.ts +9 -1
  243. package/src/types/wire.ts +68 -4
  244. package/src/types.ts +1 -0
  245. package/src/update/job.ts +10 -16
  246. package/src/update/npm-invocation.mjs +17 -16
  247. package/src/update/transactional-install.d.mts +22 -1
  248. package/src/update/transactional-install.mjs +176 -19
  249. package/src/update/update-failure-guidance.d.mts +8 -0
  250. package/src/update/update-failure-guidance.mjs +47 -0
  251. package/src/usage/expected-prices.ts +54 -12
  252. package/src/usage/log.ts +117 -3
  253. package/src/usage/telemetry-contract.ts +1 -0
  254. package/src/usage/timeline.ts +34 -6
  255. package/src/usage/user-cost-overlay-reconciler.ts +3 -3
  256. package/src/vision/reasoning.ts +2 -4
  257. package/src/web-search/xai-executor.ts +14 -4
  258. package/gui/dist/assets/App-BqrsSrIR.js +0 -50
  259. package/gui/dist/assets/index-C6SJrh0N.js +0 -86
  260. package/gui/dist/assets/index-DdDunwDb.css +0 -1
  261. package/gui/dist/assets/usage-companion-chart-CzAAAB1o.js +0 -1
  262. package/src/adapters/kiro-thinking.ts +0 -112
@@ -1,41 +1,58 @@
1
1
  import { readBoundedResponseBody } from "../lib/bounded-body";
2
2
 
3
+ /*
4
+ * Keys must match the EXACT upstream /provider/v1/models ids (GLM ships as `zai-org/GLM-5.3`, not
5
+ * `zai-org/glm-5.3`). The table doubles as the router's known-ids decode source (via
6
+ * `knownModelIdsForProvider`), so a case mismatch makes a Codex-facing slug such as
7
+ * `commandcode/zai-org-GLM-5.3` pass through undecoded and upstream rejects it with
8
+ * `unsupported_model`.
9
+ *
10
+ * PROVENANCE. commandcode.ai renders each /models/<slug> profile client-side, but the delivered HTML
11
+ * carries the loader data as a React Router payload: one `streamController.enqueue("<json>")` call
12
+ * whose JSON is an indexed value table. A model record's `reasoningEfforts` field points at an array
13
+ * of indices that resolve to effort names (for example the 2026-08-29 glm-5-3-flash page resolved
14
+ * 224=low, 225=medium, 226=high, 227=xhigh, 569=max, cross-checked six for six against committed
15
+ * rows). Rows marked "captured 2026-09-23" were read from that payload. On the same date a live
16
+ * `refreshCommandCodeReasoningEfforts` pass reproduced every other row except GLM-5, GLM-5.1 and
17
+ * GLM-5.2-Fast (their payload ladders are empty, so the static rows stay) and the Muse 1.x rows.
18
+ *
19
+ * The profile is Command Code's per-model statement, but /alpha/generate validates effort against
20
+ * one global enum (low..max) and accepts rungs a profile omits: `max` on muse-spark-1.2 and
21
+ * 1.3-contributor, and `high` and `max` on Qwen3.8-Flash, all returned 200 on 2026-09-23 while
22
+ * `ultra` returned 400. A correction therefore adds the rungs a profile newly lists and keeps the
23
+ * rungs the upstream measurably accepts; dropping them would strip an effort that works today,
24
+ * including Codex's default `high`. The refresh path decodes the same payload, so it narrows a row
25
+ * only after the upstream actually rejects a rung.
26
+ */
3
27
  const COMMAND_CODE_MODEL_EFFORTS = {
28
+ // Captured profile payload 2026-09-23: claude-fable-5-1.html.
29
+ "claude-fable-5-1": {
30
+ efforts: ["low", "medium", "high", "xhigh", "max"],
31
+ profileUrl: "https://commandcode.ai/models/claude-fable-5-1",
32
+ },
33
+ // Captured profile payload 2026-09-23: claude-opus-5-5.html.
34
+ "claude-opus-5-5": {
35
+ efforts: ["low", "medium", "high", "xhigh", "max"],
36
+ profileUrl: "https://commandcode.ai/models/claude-opus-5-5",
37
+ },
4
38
  "deepseek/deepseek-v4-flash": {
5
39
  efforts: ["high", "max"],
6
40
  profileUrl: "https://commandcode.ai/models/deepseek-v4-flash",
7
41
  },
8
- /*
9
- * Three live routes that reached the catalog without an effort ladder (#2647).
10
- * Without a row here the model advertises no efforts at all, so a client that
11
- * sends one gets it stripped or rejected rather than honored.
12
- *
13
- * PROVENANCE, stated plainly: these three ladders are the reporter's
14
- * (darwintree, #2647), recorded as reported and NOT independently verified.
15
- * All three profileUrls return HTTP 200, but commandcode.ai renders these
16
- * pages client-side and ships the ladder inside a serialized React payload
17
- * whose `reasoningEfforts` array is EMPTY in the delivered HTML. There is no
18
- * fetchable statement of these ladders to check them against.
19
- *
20
- * Do not assume the refresh path launders this. It does not:
21
- * `parsedProfileEfforts` below matches prose of the form
22
- * "Reasoning efforts ... are supported;", and `grep -c -i 'reasoning efforts'`
23
- * against the live pages returns 0 — for these three AND for the older rows
24
- * (deepseek-v4-pro, GLM-5.3, muse-spark-1.2 all measured 0 on 2026-08-27).
25
- * So `refreshCommandCodeReasoningEfforts` returns undefined and the caller
26
- * keeps whatever is written here, indefinitely. The self-correction mechanism
27
- * is currently dead for EVERY row in this table, which is a pre-existing
28
- * defect worth its own fix (teach the parser to read the embedded payload),
29
- * not something these three rows introduced.
30
- *
31
- * The practical consequence: a wrong ladder here stays wrong until a human
32
- * changes it. It degrades safely — an effort the upstream rejects surfaces as
33
- * an error rather than silent corruption — but it does not self-heal.
34
- */
35
42
  "deepseek/deepseek-v4-flash-vision-exp": {
36
43
  efforts: ["high", "max"],
37
44
  profileUrl: "https://commandcode.ai/models/deepseek-v4-flash-vision-exp",
38
45
  },
46
+ // Captured profile payload 2026-09-23: deepseek-v4-flash-fast.html.
47
+ "deepseek/deepseek-v4-flash-fast": {
48
+ efforts: ["low", "high", "max"],
49
+ profileUrl: "https://commandcode.ai/models/deepseek-v4-flash-fast",
50
+ },
51
+ // Captured profile payload 2026-09-23: deepseek-v4-1-flash.html.
52
+ "deepseek/deepseek-v4.1-flash": {
53
+ efforts: ["low", "high", "max"],
54
+ profileUrl: "https://commandcode.ai/models/deepseek-v4-1-flash",
55
+ },
39
56
  "gpt-5.6-luna": {
40
57
  efforts: ["low", "medium", "high", "xhigh", "max"],
41
58
  profileUrl: "https://commandcode.ai/models/gpt-5-6-luna",
@@ -44,11 +61,11 @@ const COMMAND_CODE_MODEL_EFFORTS = {
44
61
  efforts: ["low", "medium", "high"],
45
62
  profileUrl: "https://commandcode.ai/models/gemini-3-7-flash",
46
63
  },
47
- // Keys must match the EXACT upstream /provider/v1/models ids (GLM ships as
48
- // `zai-org/GLM-5.3`, not `zai-org/glm-5.3`). The table doubles as the router's
49
- // known-ids decode source (via `knownModelIdsForProvider`), so a case mismatch
50
- // makes the Codex-facing slug `commandcode/zai-org-GLM-5.3` pass through
51
- // undecoded and upstream rejects it with `unsupported_model`.
64
+ // Captured profile payload 2026-09-23: gemini-3-8-flash.html.
65
+ "google/gemini-3.8-flash": {
66
+ efforts: ["low", "medium", "high"],
67
+ profileUrl: "https://commandcode.ai/models/gemini-3-8-flash",
68
+ },
52
69
  "zai-org/GLM-5": {
53
70
  efforts: ["high", "max"],
54
71
  profileUrl: "https://commandcode.ai/models/glm-5",
@@ -69,86 +86,71 @@ const COMMAND_CODE_MODEL_EFFORTS = {
69
86
  efforts: ["low", "high", "max"],
70
87
  profileUrl: "https://commandcode.ai/models/glm-5-3",
71
88
  },
72
- /*
73
- * GLM-5.3-Flash (#2883). Reported as advertising NO efforts at all: the live
74
- * route is `z-ai/glm-5.3-flash`, which shares neither vendor prefix nor model
75
- * id with `zai-org/GLM-5.3` above, so `modelRecordValue` cannot bridge them
76
- * (exact / colon-family / case-folded only — by design; a substring match here
77
- * would merge two genuinely different models across two vendor namespaces).
78
- *
79
- * PROVENANCE: unlike the #2647 rows above, this ladder is MEASURED, not
80
- * reported. commandcode.ai renders the profile client-side, but the delivered
81
- * HTML ships a serialized React payload whose string table can be read
82
- * directly: in the 2026-08-29 fetch of /models/glm-5-3-flash (HTTP 200,
83
- * 228749 bytes) the indices resolve as 224=low, 225=medium, 226=high,
84
- * 227=xhigh, 569=max, and this model's array is [224,226,569].
85
- *
86
- * The index map was cross-validated against every row in this table that the
87
- * same page carries: deepseek-v4-pro and -flash [226,569], gpt-5.6-luna
88
- * [224,225,226,227,569], gemini-3.7-flash [224,225,226], GLM-5.2 [226,569],
89
- * GLM-5.3 [224,226,569] — six for six against the values already committed
90
- * here. No authenticated upstream generate probe was performed.
91
- */
92
89
  "z-ai/glm-5.3-flash": {
93
90
  efforts: ["low", "high", "max"],
94
91
  profileUrl: "https://commandcode.ai/models/glm-5-3-flash",
95
92
  },
96
- // Muse Spark: CLI currently prints "has no adjustable reasoning effort" and
97
- // blocks --effort locally, but the upstream /alpha/generate endpoint accepts
98
- // reasoning_effort low..max for meta/muse-spark-1.2-contributor (verified
99
- // 2026-08-13: direct upstream POST with low/medium/high/xhigh/max all 200,
100
- // ultra 400; reasoningTokens differentiated 114..253; proxy previously stripped
101
- // the field so effort changes had no effect).
102
- //
103
- // 1.3 shipped 2026-09-02 as the same-shaped successor to 1.2 (Command Code
104
- // publishes meta/muse-spark-1.3 and meta/muse-spark-1.3-contributor alongside
105
- // the 1.2 pair, and Zen serves muse-spark-1.3-contributor over the same
106
- // /responses wire). It carries the 1.2 ladder because it IS the 1.2 spec: the
107
- // upstream ladder statement is per-family, and a narrower guess here would
108
- // strip an effort the gateway accepts. Additive — 1.2 and 1.1 stay live.
93
+ // Captured profile payload 2026-09-23: glm-5-3-flashx.html.
94
+ "z-ai/glm-5.3-flashx": {
95
+ efforts: ["low", "high", "max"],
96
+ profileUrl: "https://commandcode.ai/models/glm-5-3-flashx",
97
+ },
98
+ // Captured profile payload 2026-09-23: muse-spark-1-3.html.
99
+ // Muse Spark: the Command Code CLI prints "has no adjustable reasoning effort", but /alpha/generate
100
+ // accepts reasoning_effort for these routes (verified 2026-08-13 on 1.2-contributor: low..max 200,
101
+ // ultra 400), so the table, not the CLI, is the ladder authority.
109
102
  "meta/muse-spark-1.3": {
110
103
  efforts: ["low", "medium", "high", "xhigh", "max"],
111
- profileUrl: "https://commandcode.ai/models/meta-muse-spark-1.3",
104
+ profileUrl: "https://commandcode.ai/models/muse-spark-1-3",
112
105
  },
106
+ // Captured profile payload 2026-09-23 lists low..xhigh; max stays because /alpha/generate accepts it.
113
107
  "meta/muse-spark-1.3-contributor": {
114
108
  efforts: ["low", "medium", "high", "xhigh", "max"],
115
- profileUrl: "https://commandcode.ai/models/meta-muse-spark-1.3-contributor",
109
+ profileUrl: "https://commandcode.ai/models/muse-spark-1-3-contributor",
116
110
  },
117
111
  "meta/muse-spark-1.2": {
118
112
  efforts: ["low", "medium", "high", "xhigh", "max"],
119
- profileUrl: "https://commandcode.ai/models/meta-muse-spark-1.2",
113
+ profileUrl: "https://commandcode.ai/models/muse-spark-1-2",
120
114
  },
121
115
  "meta/muse-spark-1.2-contributor": {
122
116
  efforts: ["low", "medium", "high", "xhigh", "max"],
123
- profileUrl: "https://commandcode.ai/models/meta-muse-spark-1.2-contributor",
117
+ profileUrl: "https://commandcode.ai/models/muse-spark-1-2-contributor",
124
118
  },
125
119
  "meta/muse-spark-1.1": {
126
120
  efforts: ["low", "medium", "high", "xhigh", "max"],
127
- profileUrl: "https://commandcode.ai/models/meta-muse-spark-1.1",
128
- },
129
- /*
130
- * Two live routes that never gained a row here, so the adapter dropped every
131
- * requested effort (a client's `max` left the wire as no reasoning parameter
132
- * at all) and the preset advertised no effort control for them.
133
- *
134
- * PROVENANCE, stated plainly: both ladders are inferred from the same-family
135
- * rows above — deepseek v4: high..max; the Qwen 3.8 family: low..max — NOT
136
- * read from the profile pages. commandcode.ai renders those client-side and
137
- * ships an empty reasoning payload, so the self-refresh below is as dead for
138
- * these rows as the #2647 block above already documents. Measured live
139
- * 2026-09-11: /alpha/generate accepts `reasoning_effort: "max"` on both
140
- * routes (HTTP 200). `ultra` is deliberately not offered: the adapter would
141
- * strip it, and no profile evidence backs an ultra→max alias the way it does
142
- * for v4-pro/v4-flash above.
143
- */
144
- "deepseek/deepseek-v4.1-flash": {
145
- efforts: ["high", "max"],
146
- profileUrl: "https://commandcode.ai/models/deepseek-v4-1-flash",
121
+ profileUrl: "https://commandcode.ai/models/muse-spark-1-1",
147
122
  },
123
+ // Captured profile payload 2026-09-23 lists low, medium, xhigh; high and max stay because
124
+ // /alpha/generate accepts both (measured 2026-09-11 and 2026-09-23).
148
125
  "Qwen/Qwen3.8-Flash": {
149
- efforts: ["low", "medium", "high", "max"],
126
+ efforts: ["low", "medium", "high", "xhigh", "max"],
150
127
  profileUrl: "https://commandcode.ai/models/qwen3-8-flash",
151
128
  },
129
+ // Captured profile payload 2026-09-23: qwen3-8-omni-flash.html.
130
+ "Qwen/Qwen3.8-Omni-Flash": {
131
+ efforts: ["low", "medium", "xhigh"],
132
+ profileUrl: "https://commandcode.ai/models/qwen3-8-omni-flash",
133
+ },
134
+ // Captured profile payload 2026-09-23: qwen3-8-max-0902.html.
135
+ "Qwen/Qwen3.8-Max-0902": {
136
+ efforts: ["low", "medium", "xhigh"],
137
+ profileUrl: "https://commandcode.ai/models/qwen3-8-max-0902",
138
+ },
139
+ // Captured profile payload 2026-09-23: step-5-preview.html.
140
+ "stepfun/Step-5-Preview": {
141
+ efforts: ["low", "medium", "high"],
142
+ profileUrl: "https://commandcode.ai/models/step-5-preview",
143
+ },
144
+ // Captured profile payload 2026-09-23: hy4-preview.html.
145
+ "tencent/hy4-preview": {
146
+ efforts: ["low", "medium", "high"],
147
+ profileUrl: "https://commandcode.ai/models/hy4-preview",
148
+ },
149
+ // Captured profile payload 2026-09-23: grok-4-7.html.
150
+ "xai/grok-4.7": {
151
+ efforts: ["low", "medium", "high", "xhigh"],
152
+ profileUrl: "https://commandcode.ai/models/grok-4-7",
153
+ },
152
154
  } as const;
153
155
 
154
156
  /**
@@ -160,24 +162,91 @@ export const COMMAND_CODE_MODEL_REASONING_EFFORTS: Record<string, string[]> = Ob
160
162
  );
161
163
 
162
164
  const refreshedEfforts = new Map<string, string[]>();
165
+ const rejectedEfforts = new Map<string, Set<string>>();
166
+ const DEFAULT_EFFORT_DESTINATION = "https://api.commandcode.ai";
163
167
 
164
168
  function keyFor(modelId: string): string {
165
169
  return modelId.trim().toLowerCase();
166
170
  }
167
171
 
168
- export function commandCodeReasoningEfforts(modelId: string): readonly string[] | undefined {
169
- const key = keyFor(modelId);
172
+ function cacheKey(modelId: string, destination: string): string {
173
+ let normalized = destination.trim().replace(/\/+$/, "");
174
+ try {
175
+ const url = new URL(destination);
176
+ normalized = `${url.origin}${url.pathname.replace(/\/+$/, "")}`;
177
+ } catch { /* Config validation owns malformed destinations. */ }
178
+ return JSON.stringify([normalized, keyFor(modelId)]);
179
+ }
180
+
181
+ export function commandCodeReasoningEfforts(
182
+ modelId: string,
183
+ destination = DEFAULT_EFFORT_DESTINATION,
184
+ ): readonly string[] | undefined {
185
+ const key = cacheKey(modelId, destination);
186
+ const rejected = rejectedEfforts.get(key);
170
187
  const refreshed = refreshedEfforts.get(key);
171
- if (refreshed !== undefined) return refreshed;
188
+ if (refreshed !== undefined) return rejected ? refreshed.filter(effort => !rejected.has(effort)) : refreshed;
172
189
  // Case-insensitive: the table keys match the EXACT upstream ids (e.g. `zai-org/GLM-5.3`),
173
190
  // but callers may pass either case.
174
191
  for (const [id, efforts] of Object.entries(COMMAND_CODE_MODEL_REASONING_EFFORTS)) {
175
- if (keyFor(id) === key) return efforts;
192
+ if (keyFor(id) === keyFor(modelId)) return rejected ? efforts.filter(effort => !rejected.has(effort)) : efforts;
176
193
  }
177
194
  return undefined;
178
195
  }
179
196
 
180
- function parsedProfileEfforts(page: string): string[] | undefined {
197
+ /** Upper bound for one fetched profile page. */
198
+ export const PROFILE_PAGE_MAX_BYTES = 512 * 1024;
199
+
200
+ const PROFILE_EFFORTS = new Set(["low", "medium", "high", "xhigh", "max"]);
201
+
202
+ function parsedRouterEfforts(page: string, modelId: string): string[] | undefined {
203
+ // React Router serializes the loader as a JSON string of an indexed value table.
204
+ // Object keys such as _125 and array elements are references into that table.
205
+ const flights = [...page.matchAll(/window\.__reactRouterContext\.streamController\.enqueue\(("(?:\\.|[^"\\])*")\);/g)];
206
+ if (flights.length !== 1) return undefined;
207
+ let values: unknown;
208
+ try {
209
+ values = JSON.parse(JSON.parse(flights[0]![1]!));
210
+ } catch {
211
+ return undefined;
212
+ }
213
+ if (!Array.isArray(values)) return undefined;
214
+ const table: unknown[] = values;
215
+ const dereference = (ref: unknown): unknown =>
216
+ Number.isInteger(ref) && (ref as number) >= 0 && (ref as number) < table.length
217
+ ? table[ref as number] : undefined;
218
+ let matched: string[] | undefined;
219
+ for (const value of table) {
220
+ if (!value || typeof value !== "object" || Array.isArray(value)) continue;
221
+ const fields = new Map<string, unknown>();
222
+ let conflicting = false;
223
+ for (const [key, ref] of Object.entries(value)) {
224
+ if (!/^_\d+$/.test(key)) continue;
225
+ const name = dereference(Number(key.slice(1)));
226
+ if (typeof name !== "string") continue;
227
+ // Two keys decoding to one field name is not a record this parser understands.
228
+ if (fields.has(name)) conflicting = true;
229
+ fields.set(name, ref);
230
+ }
231
+ if (conflicting && (fields.has("id") || fields.has("reasoningEfforts"))) return undefined;
232
+ const id = dereference(fields.get("id"));
233
+ if (typeof id !== "string" || keyFor(id) !== keyFor(modelId)) continue;
234
+ const refs = dereference(fields.get("reasoningEfforts"));
235
+ if (!Array.isArray(refs) || refs.length === 0) return undefined;
236
+ const efforts = refs.map(dereference);
237
+ if (efforts.some(effort => typeof effort !== "string" || !PROFILE_EFFORTS.has(effort)) ||
238
+ new Set(efforts).size !== efforts.length) return undefined;
239
+ const complete = efforts as string[];
240
+ if (matched && JSON.stringify(matched) !== JSON.stringify(complete)) return undefined;
241
+ matched = complete;
242
+ }
243
+ return matched;
244
+ }
245
+
246
+ function parsedProfileEfforts(page: string, modelId: string): string[] | undefined {
247
+ if (page.includes("window.__reactRouterContext.streamController.enqueue(")) {
248
+ return parsedRouterEfforts(page, modelId);
249
+ }
181
250
  const match = page.match(/Reasoning efforts\s+([^.;]+?)\s+are supported;\s*([^.]*)/i);
182
251
  if (!match) return undefined;
183
252
  const listed = match[1]!.toLowerCase().match(/\b(?:low|medium|high|xhigh|max)\b/g) ?? [];
@@ -200,16 +269,23 @@ function parsedProfileEfforts(page: string): string[] | undefined {
200
269
  export async function refreshCommandCodeReasoningEfforts(
201
270
  modelId: string,
202
271
  fetchFn: typeof globalThis.fetch = globalThis.fetch,
272
+ rejectedEffort?: string,
273
+ destination = DEFAULT_EFFORT_DESTINATION,
203
274
  ): Promise<readonly string[] | undefined> {
204
- const key = keyFor(modelId);
275
+ const key = cacheKey(modelId, destination);
205
276
  let profile: { efforts: readonly string[]; profileUrl: string } | undefined;
206
277
  for (const [id, row] of Object.entries(COMMAND_CODE_MODEL_EFFORTS)) {
207
- if (keyFor(id) === key) {
278
+ if (keyFor(id) === keyFor(modelId)) {
208
279
  profile = row;
209
280
  break;
210
281
  }
211
282
  }
212
283
  if (!profile) return undefined;
284
+ if (rejectedEffort) {
285
+ const rejected = rejectedEfforts.get(key) ?? new Set<string>();
286
+ rejected.add(rejectedEffort);
287
+ rejectedEfforts.set(key, rejected);
288
+ }
213
289
  try {
214
290
  const response = await fetchFn(profile.profileUrl, {
215
291
  headers: { Accept: "text/html" },
@@ -217,13 +293,18 @@ export async function refreshCommandCodeReasoningEfforts(
217
293
  });
218
294
  if (!response.ok) return undefined;
219
295
  // Bound the profile page before parsing: a large or malformed page must not
220
- // allocate unbounded memory on the request path.
221
- const observed = await readBoundedResponseBody(response, { maxBytes: 256 * 1024 });
296
+ // allocate unbounded memory on the request path. Live profiles measured 240-259 KB on
297
+ // 2026-09-23, so the cap leaves room for growth; a truncated page fails to parse and keeps
298
+ // the static row.
299
+ const observed = await readBoundedResponseBody(response, { maxBytes: PROFILE_PAGE_MAX_BYTES });
222
300
  if (!observed.displaySafe) return undefined;
223
- const efforts = parsedProfileEfforts(observed.text);
301
+ const efforts = parsedProfileEfforts(observed.text, modelId);
224
302
  if (efforts === undefined) return undefined;
225
- refreshedEfforts.set(key, efforts);
226
- return efforts;
303
+ const accepted = commandCodeReasoningEfforts(modelId, destination) ?? [];
304
+ const merged = [...new Set([...accepted, ...efforts])]
305
+ .filter(effort => !rejectedEfforts.get(key)?.has(effort));
306
+ refreshedEfforts.set(key, merged);
307
+ return merged;
227
308
  } catch {
228
309
  return undefined;
229
310
  }
@@ -231,4 +312,5 @@ export async function refreshCommandCodeReasoningEfforts(
231
312
 
232
313
  export function resetCommandCodeReasoningEffortsForTest(): void {
233
314
  refreshedEfforts.clear();
315
+ rejectedEfforts.clear();
234
316
  }
@@ -47,6 +47,7 @@ export interface DerivedKeyLoginProvider {
47
47
  requiresReasoningPlaceholderModels?: string[];
48
48
  showThinkingSummary?: boolean;
49
49
  reasoningSplitModels?: string[];
50
+ inlineThinkTagModels?: string[];
50
51
  reasoningDetailsModels?: string[];
51
52
  thinkingToggleModels?: string[];
52
53
  thinkingBudgetModels?: string[];
@@ -113,13 +114,17 @@ function cloneRecordOfArrays(input: Record<string, string[]>): Record<string, st
113
114
  * is how a partially customized `modelInputModalities` could leave a
114
115
  * vision-capable model advertising no image support, which in turn collapses any
115
116
  * combo containing it to text-only. Routing already merges these maps per key
116
- * (`mergeRecordFill` in src/router.ts); catalog enrichment now matches.
117
+ * (`mapFill` in src/providers/resolved-model-policy-merge.ts); catalog enrichment now matches.
117
118
  */
118
119
  function fillRecordOfArrays(
119
120
  seed: Record<string, string[]>,
120
121
  user: Record<string, string[]> | undefined,
121
122
  ): Record<string, string[]> {
122
- return { ...cloneRecordOfArrays(seed), ...(user ? cloneRecordOfArrays(user) : {}) };
123
+ const userKeys = new Set(Object.keys(user ?? {}).map(key => key.toLowerCase()));
124
+ const defaults = Object.fromEntries(
125
+ Object.entries(seed).filter(([key]) => !userKeys.has(key.toLowerCase())),
126
+ );
127
+ return { ...cloneRecordOfArrays(defaults), ...(user ? cloneRecordOfArrays(user) : {}) };
123
128
  }
124
129
 
125
130
  function cloneNestedRecord(input: Record<string, Record<string, string>>): Record<string, Record<string, string>> {
@@ -286,6 +291,7 @@ export function providerConfigSeed(entry: ProviderRegistryEntry): OcxProviderCon
286
291
  ...(entry.requiresReasoningPlaceholderModels ? { requiresReasoningPlaceholderModels: [...entry.requiresReasoningPlaceholderModels] } : {}),
287
292
  ...(entry.showThinkingSummary !== undefined ? { showThinkingSummary: entry.showThinkingSummary } : {}),
288
293
  ...(entry.reasoningSplitModels ? { reasoningSplitModels: [...entry.reasoningSplitModels] } : {}),
294
+ ...(entry.inlineThinkTagModels ? { inlineThinkTagModels: [...entry.inlineThinkTagModels] } : {}),
289
295
  ...(entry.reasoningDetailsModels ? { reasoningDetailsModels: [...entry.reasoningDetailsModels] } : {}),
290
296
  ...(entry.thinkingToggleModels ? { thinkingToggleModels: [...entry.thinkingToggleModels] } : {}),
291
297
  ...(entry.thinkingBudgetModels ? { thinkingBudgetModels: [...entry.thinkingBudgetModels] } : {}),
@@ -336,6 +342,7 @@ export function deriveKeyLoginMap(): Record<string, DerivedKeyLoginProvider> {
336
342
  ...(entry.requiresReasoningPlaceholderModels ? { requiresReasoningPlaceholderModels: [...entry.requiresReasoningPlaceholderModels] } : {}),
337
343
  ...(entry.showThinkingSummary !== undefined ? { showThinkingSummary: entry.showThinkingSummary } : {}),
338
344
  ...(entry.reasoningSplitModels ? { reasoningSplitModels: [...entry.reasoningSplitModels] } : {}),
345
+ ...(entry.inlineThinkTagModels ? { inlineThinkTagModels: [...entry.inlineThinkTagModels] } : {}),
339
346
  ...(entry.reasoningDetailsModels ? { reasoningDetailsModels: [...entry.reasoningDetailsModels] } : {}),
340
347
  ...(entry.thinkingToggleModels ? { thinkingToggleModels: [...entry.thinkingToggleModels] } : {}),
341
348
  ...(entry.thinkingBudgetModels ? { thinkingBudgetModels: [...entry.thinkingBudgetModels] } : {}),
@@ -534,7 +541,7 @@ export function enrichProviderFromRegistry(name: string, prov: OcxProviderConfig
534
541
  // Per-model fill for the same reason as modelInputModalities above: an all-or-nothing
535
542
  // copy let ONE customized model hide the registry's ladder for every other model on the
536
543
  // provider. That split the two planes apart — routing merges these maps per key
537
- // (mergeRecordFill in src/router.ts), so the wire honored the effort while /v1/models and
544
+ // (mapFill in src/providers/resolved-model-policy-merge.ts), so the wire honored the effort while /v1/models and
538
545
  // every client export showed no effort control at all.
539
546
  if (resolvedStatic.modelReasoningEfforts) prov.modelReasoningEfforts = cloneRecordOfArrays(resolvedStatic.modelReasoningEfforts);
540
547
  if (!prov.modelDefaultReasoningEfforts && seed.modelDefaultReasoningEfforts) prov.modelDefaultReasoningEfforts = { ...seed.modelDefaultReasoningEfforts };
@@ -602,6 +609,7 @@ export function enrichProviderFromRegistry(name: string, prov: OcxProviderConfig
602
609
  if (!prov.preserveReasoningContentModels && seed.preserveReasoningContentModels) prov.preserveReasoningContentModels = [...seed.preserveReasoningContentModels];
603
610
  if (!prov.requiresReasoningPlaceholderModels && seed.requiresReasoningPlaceholderModels) prov.requiresReasoningPlaceholderModels = [...seed.requiresReasoningPlaceholderModels];
604
611
  if (!prov.reasoningSplitModels && seed.reasoningSplitModels) prov.reasoningSplitModels = [...seed.reasoningSplitModels];
612
+ if (!prov.inlineThinkTagModels && seed.inlineThinkTagModels) prov.inlineThinkTagModels = [...seed.inlineThinkTagModels];
605
613
  if (!prov.reasoningDetailsModels && seed.reasoningDetailsModels) prov.reasoningDetailsModels = [...seed.reasoningDetailsModels];
606
614
  if (!prov.thinkingToggleModels && seed.thinkingToggleModels) prov.thinkingToggleModels = [...seed.thinkingToggleModels];
607
615
  if (!prov.thinkingBudgetModels && seed.thinkingBudgetModels) prov.thinkingBudgetModels = [...seed.thinkingBudgetModels];
@@ -12,8 +12,9 @@ import type { InboundWire, ModelWireDefault, ProviderAuthKind } from "./registry
12
12
  const SERVICE_TIER_ADAPTERS = new Set(["openai-chat", "openai-responses"]);
13
13
  const FAST_WIRE_ADAPTERS: Readonly<Record<FastWire["kind"], ReadonlySet<string>>> = {
14
14
  "service-tier": SERVICE_TIER_ADAPTERS,
15
- // A1 deliberately has no adapter implementation for Anthropic speed.
16
- "anthropic-speed": new Set(),
15
+ // Anthropic fast mode: top-level `speed: "fast"` plus the declared beta, serialized and
16
+ // observed (`usage.speed`) by the Anthropic adapter itself.
17
+ "anthropic-speed": new Set(["anthropic"]),
17
18
  // Cursor expresses Fast as a variant dimension of the picked model, resolved in the
18
19
  // request builder, so the adapter set is exactly the cursor adapter.
19
20
  "cursor-variant": new Set(["cursor"]),
@@ -46,6 +47,7 @@ export interface FastPolicyAuthority {
46
47
  };
47
48
  readonly modelAdapters: Readonly<Record<string, string>>;
48
49
  readonly hardPins: Readonly<Record<string, string>>;
50
+ readonly hardPinPrefixes?: Readonly<Record<string, string>>;
49
51
  readonly registryWireDefaults: Readonly<Record<string, ModelWireDefault>>;
50
52
  }
51
53
 
@@ -153,6 +155,12 @@ function resolvePolicyAdapter(
153
155
  ? authority.hardPins[modelId]
154
156
  : undefined;
155
157
  if (typeof hardPin === "string") return { adapter: hardPin, hardPinned: true };
158
+ const foldedModelId = modelId.toLowerCase();
159
+ for (const [prefix, adapter] of Object.entries(authority.hardPinPrefixes ?? {})) {
160
+ if (foldedModelId.startsWith(prefix.toLowerCase())) {
161
+ return { adapter, hardPinned: true };
162
+ }
163
+ }
156
164
  if (authority.modelWireOverrideAllowed) {
157
165
  const registryDefault = MODEL_ADAPTER_OVERRIDE_ALLOWED.has(authority.providerAdapter)
158
166
  ? registryDefaultForModel(
@@ -368,7 +376,12 @@ export function createAdapterTierMetadata(
368
376
  outcome.fastOutcome = "not-requested";
369
377
  } else if (!effectiveFastRequested || context.eligibility !== "eligible" || wireValue === null) {
370
378
  outcome.fastOutcome = "downgraded";
371
- outcome.fastDowngradeReason = downgradeReasonForUnavailable(context);
379
+ // An upstream that refused the fast wire earlier in this request is a decline, not a
380
+ // missing wire: the route is eligible and was asked, and the standard resend that follows
381
+ // must not read as if the proxy never tried.
382
+ outcome.fastDowngradeReason = context.upstreamDeclinedFast === true
383
+ ? "response-declined"
384
+ : downgradeReasonForUnavailable(context);
372
385
  outcome.confirmation = "downgraded";
373
386
  } else if (canonicalFromWire(context.fastWire, wireValue) === "priority") {
374
387
  outcome.canonical = "priority";
@@ -5,6 +5,8 @@ export function canonicalUsageProviderLabel(provider: string): string {
5
5
  return provider === "chatgpt" || provider === "openai-multi" ? "openai" : provider;
6
6
  }
7
7
 
8
+ const LEGACY_MAIN_ACCOUNT_PROVIDER_LABELS = new Set(["openai-main", "chatgpt-main", "openai-multi-main"]);
9
+
8
10
  export function usesApiKeyAccount(provider: Pick<OcxProviderConfig, "authMode" | "_apiKeyAttempt">): boolean {
9
11
  return provider.authMode === "key"
10
12
  || (provider.authMode === undefined && !!provider._apiKeyAttempt?.reference);
@@ -29,11 +31,14 @@ export function baseProviderLabel(provider: string): string {
29
31
  const cut = provider.lastIndexOf("-");
30
32
  if (cut <= 0) return canonicalUsageProviderLabel(provider);
31
33
  const suffix = provider.slice(cut + 1);
32
- // `-main` is the legacy log label for the main Codex account (MAIN_CODEX_ACCOUNT_ID). New entries
33
- // log under the base provider name, but historical `<provider>-main` entries must still collapse.
34
+ // `-main` was the legacy log label for the main Codex account (MAIN_CODEX_ACCOUNT_ID). Restrict
35
+ // that compatibility mapping to the known Codex provider labels so configured providers whose
36
+ // names naturally end in `-main` remain distinct.
34
37
  // ChatGPT auth-pool and OpenAI passthrough are the same Codex/OpenAI usage surface, so display
35
38
  // summaries normalize them to one `openai` row after recognized main/pool suffixes are removed.
36
- if (suffix === "main") return canonicalUsageProviderLabel(provider.slice(0, cut));
39
+ if (LEGACY_MAIN_ACCOUNT_PROVIDER_LABELS.has(provider)) {
40
+ return canonicalUsageProviderLabel(provider.slice(0, cut));
41
+ }
37
42
  return CODEX_ACCOUNT_LOG_LABEL_RE.test(suffix) ? canonicalUsageProviderLabel(provider.slice(0, cut)) : provider;
38
43
  }
39
44
 
@@ -121,6 +121,7 @@ export const PROVIDER_MODEL_RENAME_ROLES = {
121
121
  transientRetryOn5xx: "none",
122
122
  retryOnReset: "none",
123
123
  reasoningSplitModels: "list",
124
+ inlineThinkTagModels: "list",
124
125
  reasoningDetailsModels: "list",
125
126
  thinkingToggleModels: "list",
126
127
  thinkingBudgetModels: "list",
@@ -99,6 +99,7 @@ export function applyOpenAiVirtualModel(
99
99
 
100
100
  logCtx.model = resolution.selectedModelId;
101
101
  logCtx.resolvedModel = resolution.wireModelId;
102
+ logCtx.wireModel = resolution.wireModelId;
102
103
  route.modelId = resolution.wireModelId;
103
104
  captureOpenAiVirtualWirePolicy(route, resolution, inboundWire);
104
105
  parsed.modelId = resolution.wireModelId;
@@ -3,6 +3,7 @@ import { MAIN_CODEX_ACCOUNT_ID } from "../../codex/main-account";
3
3
  import { getValidAccessToken } from "../../oauth";
4
4
  import { getAccountCredential, getAccountSet } from "../../oauth/store";
5
5
  import { fetchMuseKeyQuotaSnapshot } from "../muse-key-quota";
6
+ import { CLAUDE_CLI_USER_AGENT } from "../claude-cli-identity";
6
7
  import { XAI_GROK_CLIENT_VERSION, XAI_GROK_COMPATIBILITY } from "../xai-transport";
7
8
  import {
8
9
  commitKiroAccountUsageState,
@@ -276,7 +277,7 @@ export async function fetchAnthropicUsageQuota(accessToken: string): Promise<Pro
276
277
  headers: {
277
278
  Accept: "application/json, text/plain, */*",
278
279
  "Content-Type": "application/json",
279
- "User-Agent": "claude-cli/2.1.63 (external, cli)",
280
+ "User-Agent": CLAUDE_CLI_USER_AGENT,
280
281
  "anthropic-beta": "claude-code-20250219,oauth-2025-04-20,interleaved-thinking-2025-05-14,context-management-2025-06-27,prompt-caching-scope-2026-01-05",
281
282
  Authorization: `Bearer ${accessToken}`,
282
283
  },