@bitkyc08/opencodex 2.86.0 → 2.87.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 (378) hide show
  1. package/gui/dist/assets/App-ZfYbLHkB.js +58 -0
  2. package/gui/dist/assets/App-wT3mXqFz.css +1 -0
  3. package/gui/dist/assets/{Tray-DyR9yBee.js → Tray-DCd-A1az.js} +1 -1
  4. package/gui/dist/assets/index-CfYDMj34.js +94 -0
  5. package/gui/dist/assets/index-DPeB9HpV.css +1 -0
  6. package/gui/dist/assets/quota-summary-ChvmKGOk.js +1 -0
  7. package/gui/dist/index.html +2 -2
  8. package/package.json +1 -1
  9. package/src/adapters/anthropic/beta-allowlist.ts +9 -8
  10. package/src/adapters/anthropic/beta-body-pairs.ts +93 -0
  11. package/src/adapters/anthropic/passthrough.ts +7 -23
  12. package/src/adapters/anthropic-progress.ts +84 -0
  13. package/src/adapters/anthropic.ts +108 -21
  14. package/src/adapters/base.ts +2 -0
  15. package/src/adapters/claude-cli/adapter.ts +111 -23
  16. package/src/adapters/claude-cli/profiles.ts +2 -1
  17. package/src/adapters/claude-cli/stable-replay.ts +108 -0
  18. package/src/adapters/claude-cli/usage-admission.ts +230 -0
  19. package/src/adapters/coding-agent/protocol.ts +1 -1
  20. package/src/adapters/coding-agent/turn.ts +18 -2
  21. package/src/adapters/dsh-account.ts +97 -0
  22. package/src/adapters/google.ts +35 -8
  23. package/src/adapters/openai-chat/messages.ts +4 -3
  24. package/src/adapters/openai-chat/passthrough.ts +8 -2
  25. package/src/adapters/openai-chat/reasoning-replay.ts +52 -0
  26. package/src/adapters/openai-responses/compaction-search-history.ts +14 -7
  27. package/src/adapters/openai-responses/passthrough.ts +57 -125
  28. package/src/adapters/openai-responses/request-strips.ts +25 -3
  29. package/src/adapters/openai-responses/transport.ts +81 -0
  30. package/src/adapters/qoder/adapter.ts +4 -2
  31. package/src/adapters/registry.ts +5 -0
  32. package/src/adapters/zed.ts +26 -1
  33. package/src/advisor/activity.ts +55 -0
  34. package/src/advisor/budget.ts +62 -0
  35. package/src/advisor/consult.ts +4 -1
  36. package/src/advisor/context.ts +16 -4
  37. package/src/advisor/evidence-tool.ts +21 -0
  38. package/src/advisor/evidence.ts +117 -0
  39. package/src/advisor/runtime.ts +79 -31
  40. package/src/advisor/settings.ts +11 -2
  41. package/src/bridge/errors.ts +19 -1
  42. package/src/bridge/internal.ts +3 -1
  43. package/src/bridge/response-json.ts +18 -3
  44. package/src/bridge/sse.ts +58 -8
  45. package/src/bridge/terminal-refusal.ts +59 -0
  46. package/src/claude/desktop-3p-library.ts +32 -7
  47. package/src/claude/desktop-3p.ts +55 -13
  48. package/src/claude/desktop-first-party.ts +1 -1
  49. package/src/claude/desktop-picker-platform.ts +4 -0
  50. package/src/claude/desktop-picker.ts +15 -8
  51. package/src/claude/desktop-system-proxy.ts +50 -4
  52. package/src/claude/inbound-content-options.ts +11 -0
  53. package/src/claude/inbound.ts +33 -0
  54. package/src/claude/intercept/picker-ca-startup.ts +2 -1
  55. package/src/claude/intercept/picker-runtime.ts +6 -5
  56. package/src/claude/intercept/picker-trust-windows.ts +338 -0
  57. package/src/claude/intercept/picker-trust.ts +19 -0
  58. package/src/claude/intercept/port-policy.ts +30 -0
  59. package/src/claude/intercept/runtime.ts +97 -94
  60. package/src/claude/outbound.ts +38 -13
  61. package/src/cli/account-api.ts +1 -0
  62. package/src/cli/account-policy-dto.ts +6 -0
  63. package/src/cli/account-policy.ts +34 -5
  64. package/src/cli/account-preservation.ts +44 -0
  65. package/src/cli/account.ts +8 -2
  66. package/src/cli/advisor.ts +17 -3
  67. package/src/cli/capabilities-accounts.ts +20 -1
  68. package/src/cli/capabilities-agents-routing.ts +5 -2
  69. package/src/cli/capabilities-base.ts +38 -10
  70. package/src/cli/capabilities-observe-system.ts +63 -3
  71. package/src/cli/capabilities-provider-models.ts +11 -0
  72. package/src/cli/codex-cli-update.ts +151 -41
  73. package/src/cli/codex-provider.ts +44 -0
  74. package/src/cli/combo-input.ts +10 -1
  75. package/src/cli/combo-result.ts +1 -1
  76. package/src/cli/combo.ts +2 -1
  77. package/src/cli/cross-home-owner.ts +17 -14
  78. package/src/cli/dispatch.ts +25 -4
  79. package/src/cli/doctor.ts +6 -1
  80. package/src/cli/ensure-desktop-cold-start.ts +60 -7
  81. package/src/cli/help.ts +3 -2
  82. package/src/cli/index.ts +1 -1
  83. package/src/cli/integration-plan-dto.ts +1 -0
  84. package/src/cli/log-history.ts +172 -0
  85. package/src/cli/models-runtime-subcommands.ts +1 -0
  86. package/src/cli/models-runtime.ts +72 -8
  87. package/src/cli/observe-stream.ts +2 -0
  88. package/src/cli/observe.ts +16 -4
  89. package/src/cli/provider.ts +27 -5
  90. package/src/cli/registry.ts +28 -8
  91. package/src/cli/runtime-api.ts +5 -3
  92. package/src/cli/system-command.ts +40 -8
  93. package/src/cli/uninstall-client-state.ts +32 -0
  94. package/src/cli/usage-compare.ts +309 -0
  95. package/src/cli/usage-model-search.ts +4 -4
  96. package/src/cli/usage-report.ts +2 -2
  97. package/src/cli/v2.ts +1 -1
  98. package/src/client/machine-listener.ts +1 -3
  99. package/src/client/runtime.ts +1 -0
  100. package/src/clients/client-path.ts +51 -0
  101. package/src/clients/config-export/contracts.ts +2 -1
  102. package/src/clients/config-export.ts +18 -44
  103. package/src/clients/gentle-shell-path.ts +87 -0
  104. package/src/codex/account-auto-switch.ts +2 -0
  105. package/src/codex/account-identity.ts +29 -0
  106. package/src/codex/account-label.ts +1 -1
  107. package/src/codex/account-lifecycle.ts +21 -5
  108. package/src/codex/account-preservation.ts +28 -0
  109. package/src/codex/account-use-remaining.ts +23 -0
  110. package/src/codex/app-server-processes.ts +97 -7
  111. package/src/codex/app-server-restart-service.ts +55 -4
  112. package/src/codex/auth-api/account-list.ts +4 -0
  113. package/src/codex/auth-api/account-pause-group.ts +6 -50
  114. package/src/codex/auth-api/account-preservation.ts +57 -0
  115. package/src/codex/auth-api/routes.ts +8 -0
  116. package/src/codex/auth-api/use-remaining-route.ts +36 -0
  117. package/src/codex/auth-api.ts +0 -1
  118. package/src/codex/auth-context.ts +2 -2
  119. package/src/codex/catalog/auto-review.ts +20 -3
  120. package/src/codex/catalog/build-entries.ts +9 -1
  121. package/src/codex/catalog/combo-member.ts +14 -5
  122. package/src/codex/catalog/configured-tiers.ts +1 -1
  123. package/src/codex/catalog/gather-capture.ts +2 -0
  124. package/src/codex/catalog/metadata.ts +11 -9
  125. package/src/codex/catalog/model-hints.ts +21 -16
  126. package/src/codex/catalog/model-visibility.ts +3 -1
  127. package/src/codex/catalog/parsing.ts +30 -86
  128. package/src/codex/catalog/provider-models.ts +36 -34
  129. package/src/codex/catalog/routed-gather.ts +12 -13
  130. package/src/codex/cli-install-provenance.ts +105 -17
  131. package/src/codex/cli-update-plan.ts +545 -0
  132. package/src/codex/config-write-lock.ts +23 -2
  133. package/src/codex/desktop-app/picker-freshness.ts +96 -0
  134. package/src/codex/desktop-app-restart.ts +21 -0
  135. package/src/codex/desktop-compatibility/installed-build.ts +24 -6
  136. package/src/codex/desktop-compatibility/routing-preflight.ts +1 -1
  137. package/src/codex/desktop-compatibility/runtime.ts +12 -3
  138. package/src/codex/desktop-compatibility/windows-key-protection.ts +115 -9
  139. package/src/codex/features.ts +85 -45
  140. package/src/codex/inject/history-failure.ts +27 -0
  141. package/src/codex/inject/restore.ts +24 -2
  142. package/src/codex/inject/routing-classify.ts +4 -3
  143. package/src/codex/inject.ts +18 -27
  144. package/src/codex/main-account-hard-lock.ts +30 -1
  145. package/src/codex/native-profile-startup.ts +1 -1
  146. package/src/codex/ocx-compaction-history.ts +12 -10
  147. package/src/codex/paths.ts +86 -1
  148. package/src/codex/provider-routing.ts +174 -0
  149. package/src/codex/refresh.ts +2 -2
  150. package/src/codex/routing/health-store.ts +1 -1
  151. package/src/codex/routing/selection.ts +4 -3
  152. package/src/codex/routing/thread-affinity.ts +1 -1
  153. package/src/codex/signed-in-identity.ts +43 -0
  154. package/src/codex/subagent-model-fallback.ts +1 -1
  155. package/src/codex/sync.ts +6 -1
  156. package/src/codex/user-identity.ts +16 -5
  157. package/src/codex/windows-local-appdata.ts +84 -0
  158. package/src/combos/failover.ts +6 -1
  159. package/src/combos/index.ts +1 -0
  160. package/src/combos/resolve.ts +46 -10
  161. package/src/combos/types.ts +15 -0
  162. package/src/config/diagnostics.ts +33 -6
  163. package/src/config/live-reconcile.ts +38 -2
  164. package/src/config/load-degrade.ts +17 -0
  165. package/src/config/opencode-wire-upgrade.ts +54 -0
  166. package/src/config/opencode-zen-upgrade.ts +52 -0
  167. package/src/config/owner-registry.ts +22 -17
  168. package/src/config/provider-validation.ts +45 -2
  169. package/src/config/schema/config-schema.ts +22 -0
  170. package/src/config/schema/leaf-validators.ts +38 -9
  171. package/src/config/schema/main-account-hard-lock-fallback.ts +26 -0
  172. package/src/config/schema/native-reasoning-retention.ts +26 -0
  173. package/src/config.ts +2 -0
  174. package/src/generated/compatibility-version.json +608 -284
  175. package/src/grok/sync.ts +1 -1
  176. package/src/images/loop.ts +46 -8
  177. package/src/integrations/catalog-refresh.ts +1 -1
  178. package/src/integrations/mutation-plan.ts +1 -0
  179. package/src/integrations/registry.ts +8 -0
  180. package/src/lib/codex-restart-contract.ts +15 -0
  181. package/src/lib/desktop-upstream-tunnel.ts +18 -53
  182. package/src/lib/empty-tool-catalog.ts +62 -0
  183. package/src/lib/errors.ts +19 -3
  184. package/src/lib/gcp-adc.ts +96 -9
  185. package/src/lib/gcp-credential-input.ts +12 -0
  186. package/src/lib/lab-live-route-production.ts +5 -0
  187. package/src/lib/opaque-proxy-tunnel.ts +217 -0
  188. package/src/lib/redact.ts +2 -2
  189. package/src/oauth/account-quota-rank.ts +69 -2
  190. package/src/oauth/anthropic-account-refusal.ts +20 -2
  191. package/src/oauth/anthropic-oauth-definitions.ts +3 -1
  192. package/src/oauth/anthropic-routing.ts +1 -21
  193. package/src/oauth/anthropic.ts +38 -7
  194. package/src/oauth/callback-server.ts +27 -1
  195. package/src/oauth/chatgpt-device.ts +1 -1
  196. package/src/oauth/chatgpt-shared.ts +83 -0
  197. package/src/oauth/chatgpt.ts +8 -72
  198. package/src/oauth/dsh.ts +203 -0
  199. package/src/oauth/generic-account-failover.ts +33 -7
  200. package/src/oauth/generic-session-affinity.ts +125 -0
  201. package/src/oauth/index.ts +184 -109
  202. package/src/oauth/local-token-detect.ts +21 -1
  203. package/src/oauth/login-cli-errors.ts +44 -0
  204. package/src/oauth/login-cli.ts +69 -7
  205. package/src/oauth/login-flow-state.ts +371 -23
  206. package/src/oauth/meta-muse-errors.ts +41 -0
  207. package/src/oauth/meta-muse-keychain.ts +52 -0
  208. package/src/oauth/meta-muse.ts +51 -84
  209. package/src/oauth/model-discovery-auth.ts +3 -0
  210. package/src/oauth/pool-settings-capability.ts +4 -2
  211. package/src/oauth/refresh-flight-retirement.ts +48 -0
  212. package/src/oauth/session-affinity-key.ts +23 -0
  213. package/src/oauth/store-anthropic-instance.ts +9 -2
  214. package/src/oauth/store.ts +12 -5
  215. package/src/protocols/encoders/adapter-events.ts +5 -4
  216. package/src/protocols/encoders/messages.ts +9 -6
  217. package/src/protocols/provider-summary.ts +2 -1
  218. package/src/providers/api-key-cooldowns.ts +49 -0
  219. package/src/providers/api-key-resolve.ts +11 -6
  220. package/src/providers/api-key-response-ownership.ts +57 -0
  221. package/src/providers/api-key-selection.ts +31 -6
  222. package/src/providers/api-keys.ts +214 -3
  223. package/src/providers/claude-cli-usage.ts +96 -0
  224. package/src/providers/dsh-account-destination.ts +31 -0
  225. package/src/providers/dsh-account-models.ts +41 -0
  226. package/src/providers/gcp-credential-cleanup.ts +59 -0
  227. package/src/providers/key-failover.ts +54 -50
  228. package/src/providers/key-store.ts +7 -2
  229. package/src/providers/model-rename-fields.ts +3 -0
  230. package/src/providers/model-rename-migration.ts +2 -2
  231. package/src/providers/openai-tiers-destination.ts +11 -2
  232. package/src/providers/openai-tiers.ts +5 -12
  233. package/src/providers/opencode-go-transport.ts +3 -1
  234. package/src/providers/quota/account-cache.ts +6 -5
  235. package/src/providers/quota/account-row.ts +34 -0
  236. package/src/providers/quota/antigravity.ts +28 -7
  237. package/src/providers/quota/dsh-account.ts +70 -0
  238. package/src/providers/quota/vendor-probes-oauth.ts +1 -93
  239. package/src/providers/quota-types.ts +2 -0
  240. package/src/providers/quota.ts +14 -20
  241. package/src/providers/registry/entries-core.ts +37 -8
  242. package/src/providers/registry/types.ts +1 -1
  243. package/src/providers/request-pacing.ts +77 -19
  244. package/src/providers/routed-config.ts +339 -0
  245. package/src/providers/service-tier.ts +2 -1
  246. package/src/quota/window-mapping.ts +5 -2
  247. package/src/responses/anthropic-progress.ts +34 -0
  248. package/src/responses/assistant-replay.ts +35 -0
  249. package/src/responses/compaction-history-framing.ts +65 -0
  250. package/src/responses/compaction.ts +6 -1
  251. package/src/responses/incomplete-reason.ts +31 -0
  252. package/src/responses/parser.ts +50 -5
  253. package/src/responses/reasoning-envelope.ts +4 -0
  254. package/src/responses/reasoning-replay-cache.ts +24 -2
  255. package/src/responses/reasoning-retention.ts +173 -0
  256. package/src/responses/retained-compaction.ts +46 -0
  257. package/src/responses/schema.ts +23 -0
  258. package/src/router.ts +8 -321
  259. package/src/routing/health.ts +37 -22
  260. package/src/routing/history/indexer.ts +6 -5
  261. package/src/routing/history/ingest.ts +6 -2
  262. package/src/routing/profile.ts +1 -1
  263. package/src/server/admission-model-scope.ts +17 -0
  264. package/src/server/auth-cors.ts +11 -0
  265. package/src/server/chat-completions.ts +7 -1
  266. package/src/server/chat-native-reasoning.ts +123 -0
  267. package/src/server/chat-native-sse.ts +110 -7
  268. package/src/server/chat-native.ts +76 -17
  269. package/src/server/claude-messages.ts +55 -414
  270. package/src/server/effort-policy.ts +1 -1
  271. package/src/server/images.ts +15 -2
  272. package/src/server/index/model-alias-id.ts +36 -0
  273. package/src/server/index/optional-listeners.ts +2 -0
  274. package/src/server/index/serve-options.ts +9 -1
  275. package/src/server/index/startup-warnings.ts +2 -0
  276. package/src/server/index.ts +1 -1
  277. package/src/server/inference/advisor-diagnostics.ts +31 -0
  278. package/src/server/management/advisor-routes.ts +15 -4
  279. package/src/server/management/claude-desktop-picker-routes.ts +1 -1
  280. package/src/server/management/combo-routes.ts +176 -50
  281. package/src/server/management/companion-routes.ts +1 -1
  282. package/src/server/management/config-routes.ts +75 -4
  283. package/src/server/management/cursor-integration-routes.ts +1 -1
  284. package/src/server/management/model-routes.ts +11 -6
  285. package/src/server/management/model-rows.ts +14 -0
  286. package/src/server/management/native-integration-routes.ts +2 -7
  287. package/src/server/management/native-reasoning-retention-routes.ts +41 -0
  288. package/src/server/management/oauth-account-routes.ts +87 -14
  289. package/src/server/management/provider-gcp-credential-routes.ts +29 -0
  290. package/src/server/management/provider-key-pacing-routes.ts +31 -0
  291. package/src/server/management/provider-overwrite-carry.ts +7 -0
  292. package/src/server/management/provider-routes.ts +25 -30
  293. package/src/server/management/route-registry.ts +10 -1
  294. package/src/server/management-api.ts +3 -1
  295. package/src/server/messages-native-tool-guard.ts +185 -0
  296. package/src/server/messages-native.ts +101 -21
  297. package/src/server/messages-shared.ts +431 -0
  298. package/src/server/relay.ts +1 -1
  299. package/src/server/request-log-terminal-status.ts +1 -1
  300. package/src/server/request-log.ts +6 -1
  301. package/src/server/responses/account-change-state.ts +8 -2
  302. package/src/server/responses/adapter-continuation.ts +13 -1
  303. package/src/server/responses/adapter-delivery.ts +19 -6
  304. package/src/server/responses/adapter-dispatch.ts +19 -2
  305. package/src/server/responses/advisor-retention.ts +117 -0
  306. package/src/server/responses/advisor-slot.ts +73 -41
  307. package/src/server/responses/codex-ws-wire.ts +15 -8
  308. package/src/server/responses/compact.ts +303 -125
  309. package/src/server/responses/compaction-recovery.ts +18 -10
  310. package/src/server/responses/compaction-retention.ts +61 -0
  311. package/src/server/responses/context-overflow.ts +11 -6
  312. package/src/server/responses/core-auth.ts +6 -0
  313. package/src/server/responses/core-codex-account.ts +11 -1
  314. package/src/server/responses/core-combo-failure.ts +2 -0
  315. package/src/server/responses/core-combo.ts +120 -36
  316. package/src/server/responses/core-errors.ts +3 -3
  317. package/src/server/responses/core-lifetime.ts +5 -2
  318. package/src/server/responses/core-opaque-recovery.ts +35 -0
  319. package/src/server/responses/core-options.ts +5 -0
  320. package/src/server/responses/core-pipeline.ts +141 -0
  321. package/src/server/responses/core-replay.ts +18 -2
  322. package/src/server/responses/core.ts +2 -115
  323. package/src/server/responses/fetch-helpers.ts +73 -19
  324. package/src/server/responses/main-hard-lock-routing.ts +109 -0
  325. package/src/server/responses/native-response-control.ts +4 -2
  326. package/src/server/responses/passthrough-delivery.ts +3 -1
  327. package/src/server/responses/passthrough-dispatch.ts +15 -13
  328. package/src/server/responses/request-prepare.ts +76 -12
  329. package/src/server/responses/request-sidecar-auth.ts +2 -2
  330. package/src/server/responses/request-transport.ts +104 -27
  331. package/src/server/responses/response-effects.ts +1 -0
  332. package/src/server/responses/run-turn-execution.ts +96 -30
  333. package/src/server/responses/sidecar-execution.ts +20 -2
  334. package/src/server/responses/terminal-guard.ts +2 -16
  335. package/src/service/auth-preflight.ts +71 -0
  336. package/src/service/cli.ts +4 -2
  337. package/src/service/definition.ts +97 -0
  338. package/src/service/guards.ts +1 -61
  339. package/src/service/health-sentinel-linux.ts +112 -0
  340. package/src/service/health-sentinel-main.ts +76 -57
  341. package/src/service/health.ts +5 -89
  342. package/src/service/launchd.ts +27 -7
  343. package/src/service/logging.ts +72 -9
  344. package/src/service/repair.ts +2 -1
  345. package/src/service/state.ts +7 -8
  346. package/src/service/systemd.ts +1 -1
  347. package/src/service/windows-guarded-manager.ts +2 -1
  348. package/src/service/windows-ops.ts +1 -1
  349. package/src/service/windows-scheduler.ts +2 -7
  350. package/src/service/windows-taskxml.ts +2 -2
  351. package/src/service/windows-wscript.ts +14 -0
  352. package/src/service.ts +4 -2
  353. package/src/sidecar/auth.ts +2 -0
  354. package/src/types/config.ts +64 -3
  355. package/src/types/opencode-go.ts +61 -0
  356. package/src/types/opencode-zen.ts +29 -0
  357. package/src/types/provider.ts +32 -6
  358. package/src/types/request.ts +23 -2
  359. package/src/types/wire.ts +29 -37
  360. package/src/types.ts +1 -0
  361. package/src/update/npm-invocation.mjs +12 -2
  362. package/src/usage/cost.ts +48 -12
  363. package/src/usage/expected-prices.ts +3 -0
  364. package/src/usage/ledger-retention.ts +2 -1
  365. package/src/usage/ledger-scanner.ts +6 -5
  366. package/src/usage/log.ts +17 -8
  367. package/src/usage/summary.ts +22 -9
  368. package/src/usage/user-cost-overlays.ts +14 -19
  369. package/src/usage/user-cost-validation.ts +38 -0
  370. package/src/vision/eligibility.ts +13 -0
  371. package/src/web-search/loop.ts +55 -5
  372. package/src/web-search/progress-stream.ts +4 -1
  373. package/src/web-search/run-turn-loop.ts +9 -1
  374. package/gui/dist/assets/App-BjlttVLy.js +0 -58
  375. package/gui/dist/assets/App-CSJvmjNs.css +0 -1
  376. package/gui/dist/assets/index-BB6uK6tU.css +0 -1
  377. package/gui/dist/assets/index-BMssP4k3.js +0 -94
  378. package/gui/dist/assets/quota-summary-D8M1bc_R.js +0 -1
@@ -2,10 +2,16 @@ import type { VendorContextInference } from "./vendor-context";
2
2
  import { isCodexControlPlaneModel } from "../control-plane-models";
3
3
  import { execFileSync } from "node:child_process";
4
4
  import { createHash } from "node:crypto";
5
- import { copyFileSync, existsSync, mkdirSync, readFileSync, realpathSync } from "node:fs";
5
+ import { copyFileSync, existsSync, mkdirSync, readFileSync } from "node:fs";
6
6
  import { delimiter, dirname, join, resolve } from "node:path";
7
- import { atomicWriteFile, expandUserPath, getConfigDir, loadConfig, ultraFastTierEnabled, websocketsEnabled } from "../../config";
8
- import { CODEX_CONFIG_PATH, CODEX_MODELS_CACHE_PATH, DEFAULT_CATALOG_PATH, readRootTomlString, resolveCodexConfigPath } from "../paths";
7
+ import { atomicWriteFile, getConfigDir, loadConfig, ultraFastTierEnabled, websocketsEnabled } from "../../config";
8
+ import {
9
+ activeCodexConfigPath,
10
+ activeCodexModelsCachePath,
11
+ isDefaultCatalogPath,
12
+ readCodexCatalogPath,
13
+ readRootTomlString,
14
+ } from "../paths";
9
15
  import { clearModelCache, DEFAULT_MODEL_CACHE_TTL_MS, getFreshCached, getStaleCached, isModelsFetchCoolingDown, markModelsFetchFailure, setCached } from "../model-cache";
10
16
  import { buildModelsRequest, resolveModelsAuthToken } from "../../oauth";
11
17
  import type { OcxConfig, OcxProviderConfig } from "../../types";
@@ -33,7 +39,7 @@ import { redactSecretString } from "../../lib/redact";
33
39
  import upstreamModelsSnapshot from "../data/upstream-models.json";
34
40
 
35
41
 
36
- import { NATIVE_OPENAI_CONTEXT_OVERRIDES, SUPPORTED_NATIVE_OPENAI_SLUGS, UPSTREAM_NATIVE_ENTRIES, hasNativeOpenAiCapabilityMetadata, nativeMultiAgentVersion, nativeOpenAiAutoCompactTokenLimit, nativeOpenAiContextWindow, nativeOpenAiMaxInputTokens, type NativeContextLimitsInput } from "./metadata";
42
+ import { NATIVE_OPENAI_CONTEXT_OVERRIDES, SUPPORTED_NATIVE_OPENAI_SLUGS, UPSTREAM_NATIVE_ENTRIES, hasNativeOpenAiCapabilityMetadata, nativeMultiAgentVersion, nativeOpenAiAutoCompactTokenLimit, nativeOpenAiConfiguredContextLimit, nativeOpenAiContextWindow, nativeOpenAiMaxInputTokens, type NativeContextLimitsInput } from "./metadata";
37
43
  import { clampAutoCompactTokenLimit } from "../../providers/auto-compact-budget";
38
44
  import { trustedAccountBoundNativeCatalogSlug } from "./account-models";
39
45
  import { CODEX_NATIVE_ALIAS_CATALOG_KIND } from "./kinds";
@@ -50,46 +56,22 @@ export function catalogBackupPathFor(catalogPath: string): string {
50
56
  return join(getConfigDir(), `catalog-backup-${id}.json`);
51
57
  }
52
58
 
53
- export function samePath(a: string, b: string): boolean {
54
- const left = resolve(a);
55
- const right = resolve(b);
56
- return process.platform === "win32" ? left.toLowerCase() === right.toLowerCase() : left === right;
57
- }
58
-
59
- export function activeCodexHome(): string | null {
60
- const raw = process.env.CODEX_HOME?.trim();
61
- if (!raw) return null;
62
- const path = resolve(expandUserPath(raw));
63
- try {
64
- return realpathSync.native(path);
65
- } catch {
66
- return path;
67
- }
68
- }
69
-
70
- export function activeCodexConfigPath(): string {
71
- const home = activeCodexHome();
72
- return home ? join(home, "config.toml") : CODEX_CONFIG_PATH;
73
- }
74
-
75
- export function activeDefaultCatalogPath(): string {
76
- const home = activeCodexHome();
77
- return home ? join(home, "opencodex-catalog.json") : DEFAULT_CATALOG_PATH;
78
- }
79
-
80
- export function activeCodexModelsCachePath(): string {
81
- const home = activeCodexHome();
82
- return home ? join(home, "models_cache.json") : CODEX_MODELS_CACHE_PATH;
83
- }
84
-
85
- export function resolveActiveCodexConfigPath(path: string): string {
86
- const home = activeCodexHome();
87
- return home ? resolve(home, path) : resolveCodexConfigPath(path);
88
- }
89
-
90
- export function isDefaultCatalogPath(path: string): boolean {
91
- return samePath(path, activeDefaultCatalogPath());
92
- }
59
+ /**
60
+ * Active-home path resolution now lives in `../paths`, next to the constants it
61
+ * falls back to. It is re-exported here so existing importers keep working; new
62
+ * code that only needs a path should import from `../paths` directly.
63
+ */
64
+ export {
65
+ samePath,
66
+ activeCodexHome,
67
+ activeCodexConfigPath,
68
+ activeDefaultCatalogPath,
69
+ activeCodexModelsCachePath,
70
+ resolveActiveCodexConfigPath,
71
+ isDefaultCatalogPath,
72
+ readCodexCatalogPath,
73
+ readCodexCatalogPathForHome,
74
+ } from "../paths";
93
75
 
94
76
  /** Stable nonsemantic ownership marker for rows projected from config.customModels. */
95
77
  export const CODEX_CUSTOM_MODEL_CATALOG_KIND = "custom-model-v1";
@@ -254,40 +236,6 @@ export function shouldExposeRoutedModel(model: CatalogModel): boolean {
254
236
  return !isMediaGenerationModelId(model.id);
255
237
  }
256
238
 
257
- export function readCodexCatalogPath(): string {
258
- const home = activeCodexHome();
259
- if (home) return readCodexCatalogPathForHome(home);
260
- try {
261
- const configPath = activeCodexConfigPath();
262
- if (existsSync(configPath)) {
263
- const toml = readFileSync(configPath, "utf-8");
264
- const path = readRootTomlString(toml, "model_catalog_json");
265
- if (path) return resolveActiveCodexConfigPath(path);
266
- }
267
- } catch { /* ignore */ }
268
- return activeDefaultCatalogPath();
269
- }
270
-
271
- /**
272
- * Resolve the configured catalog without consulting ambient CODEX_HOME again.
273
- *
274
- * `configText` is for a caller that has already read that same `config.toml` under
275
- * its own constraints - the prompt-text probe reads it bounded, on the request
276
- * thread - so resolving the catalog does not cost a second, unbounded read of the
277
- * file the caller is holding. Omitting it keeps the original behaviour.
278
- */
279
- export function readCodexCatalogPathForHome(codexHome: string, configText?: string): string {
280
- try {
281
- const configPath = join(codexHome, "config.toml");
282
- if (configText !== undefined || existsSync(configPath)) {
283
- const toml = configText ?? readFileSync(configPath, "utf-8");
284
- const path = readRootTomlString(toml, "model_catalog_json");
285
- if (path) return resolve(codexHome, path);
286
- }
287
- } catch { /* ignore */ }
288
- return join(codexHome, "opencodex-catalog.json");
289
- }
290
-
291
239
  /**
292
240
  * Read the configured auto-review model from the root of Codex's config.toml (issue #1225).
293
241
  * Stamped onto catalog entries as `auto_review_model_override` during sync so the auto-review
@@ -506,7 +454,7 @@ export function isNativeOpenAiEntry(entry: RawEntry): boolean {
506
454
  * Narrow any already-resolved native window by the user levers.
507
455
  *
508
456
  * Used for the fields the accessors do not own (`max_context_window`, and preserved rows
509
- * that carry no static override) so every field on a row lands at the same width.
457
+ * that carry no static override). The advertised maximum may exceed the default window.
510
458
  */
511
459
  function narrowNativeMaxContextWindow(
512
460
  slug: string,
@@ -514,12 +462,7 @@ function narrowNativeMaxContextWindow(
514
462
  limits?: NativeContextLimitsInput,
515
463
  ): number | undefined {
516
464
  if (typeof value !== "number" || value <= 0) return value;
517
- const resolved = nativeOpenAiContextWindow(slug, limits);
518
- const authoritative = nativeOpenAiContextWindow(slug);
519
- // The accessor pair tells us how far the levers moved this slug; apply the same delta to a
520
- // field the accessor does not model, without ever raising it.
521
- if (resolved === undefined || authoritative === undefined) return value;
522
- return Math.min(value, Math.max(resolved, 1));
465
+ return applyProviderContextCap(value, nativeOpenAiConfiguredContextLimit(slug, limits));
523
466
  }
524
467
 
525
468
  export function applyNativeOpenAiContextOverride(entry: RawEntry, limits?: NativeContextLimitsInput): void {
@@ -636,7 +579,8 @@ export function ensureStrictCatalogFields(
636
579
  if (
637
580
  typeof entry.max_context_window !== "number"
638
581
  || entry.max_context_window <= 0
639
- || ((options.isRouted === true || !isNativeOpenAiEntry(entry)) && entry.max_context_window > contextWindow)
582
+ || ((options.isRouted === true || (!isNativeOpenAiEntry(entry)
583
+ && trustedAccountBoundNativeCatalogSlug(entry) === undefined)) && entry.max_context_window > contextWindow)
640
584
  ) {
641
585
  entry.max_context_window = contextWindow;
642
586
  }
@@ -117,7 +117,7 @@ import type {
117
117
  CatalogTrustedOpenAiApiPolicySnapshot,
118
118
  } from "../convergence-types";
119
119
  import type { CapturedProviderGather, CatalogGatherProviderAuthOutcome, CatalogGatherProviderModelOutcome, ModelsAuthResolution, ModelsAuthResolver } from "./gather-capture";
120
- import { QUIET_AUTHORITATIVE_CATALOG_PROVIDERS, applyConfigHintsToCachedModels, applyProviderConfigHints, boundedOwnedBy, catalogHintsFromModelsApiItem, catalogHintsFromProviderConfig } from "./model-hints";
120
+ import { QUIET_AUTHORITATIVE_CATALOG_PROVIDERS, applyConfigHintsToCachedModels, applyProviderConfigHints, boundedOwnedBy, catalogHintsFromModelsApiItem, catalogHintsFromProviderConfig, withVisionSidecarInput } from "./model-hints";
121
121
  import { mergeConfiguredModelsIntoLiveCatalog, shouldExposeProviderModel, warnDroppedConfiguredIdsOnce } from "./model-visibility";
122
122
  import { captureModelsRequest, captureProviderGather, materializeCapturedHeaders } from "./gather-capture";
123
123
 
@@ -168,7 +168,7 @@ export function observedModelsAuthResolver(
168
168
  },
169
169
  };
170
170
  }
171
- /** Project captured discovery through config hints and retention; Auto-only Copilot exposes only Auto. */
171
+ /** Project provider evidence without synthetic sidecar image input, which belongs to each gather. */
172
172
  export async function fetchProviderModelsWithAuth(
173
173
  captured: CapturedProviderGather,
174
174
  ttlMs: number,
@@ -211,7 +211,7 @@ export async function fetchProviderModelsWithAuth(
211
211
  const configured: CatalogModel[] = configuredIds.map(id => ({
212
212
  id,
213
213
  provider: name,
214
- ...catalogHintsFromProviderConfig(name, prov, id, contextCap, metadataModelIdCaseFold, captured.effectiveAlias, metadataConfigDir),
214
+ ...catalogHintsFromProviderConfig(name, prov, id, contextCap, metadataModelIdCaseFold, captured.effectiveAlias, metadataConfigDir, false),
215
215
  }));
216
216
  const withConfiguredRetention = (
217
217
  models: CatalogModel[],
@@ -228,6 +228,7 @@ export async function fetchProviderModelsWithAuth(
228
228
  retainComboTargets: options?.retainComboTargets,
229
229
  metadataModelIdCaseFold,
230
230
  metadataConfigDir,
231
+ includeVisionSidecar: false,
231
232
  });
232
233
  if (
233
234
  options?.warnDrops === true
@@ -260,7 +261,7 @@ export async function fetchProviderModelsWithAuth(
260
261
  }
261
262
  return observed(ids.map(id => {
262
263
  const hints = catalogHintsFromProviderConfig(name, prov, id, contextCap,
263
- metadataModelIdCaseFold, captured.effectiveAlias, metadataConfigDir);
264
+ metadataModelIdCaseFold, captured.effectiveAlias, metadataConfigDir, false);
264
265
  const observedWindow = kiroObservedContextWindow(id);
265
266
  return { id, provider: name, ...hints,
266
267
  ...(observedWindow !== undefined
@@ -296,7 +297,7 @@ export async function fetchProviderModelsWithAuth(
296
297
  model => applyProviderConfigHints(name, prov, {
297
298
  id: model.id, provider: name,
298
299
  ...catalogHintsFromModelsApiItem(name, model as ProviderModelsApiItem),
299
- }, contextCap, metadataModelIdCaseFold, captured.effectiveAlias));
300
+ }, contextCap, metadataModelIdCaseFold, captured.effectiveAlias, undefined, false));
300
301
  if (!isCurrentCacheGeneration()) return observed(configured, "degraded");
301
302
  markProviderDiscoveryOk(name, picker.length);
302
303
  // Retain callable manual/combo selectors, but do not advertise named selection
@@ -326,7 +327,7 @@ export async function fetchProviderModelsWithAuth(
326
327
  : [{
327
328
  id: prov.defaultModel,
328
329
  provider: name,
329
- ...catalogHintsFromProviderConfig(name, prov, prov.defaultModel, contextCap, metadataModelIdCaseFold, captured.effectiveAlias, metadataConfigDir),
330
+ ...catalogHintsFromProviderConfig(name, prov, prov.defaultModel, contextCap, metadataModelIdCaseFold, captured.effectiveAlias, metadataConfigDir, false),
330
331
  }];
331
332
  const vertexDefaultSeed = seedVertexDefault ? configured[0] : undefined;
332
333
  const withVertexDefaultSeed = (models: CatalogModel[]): CatalogModel[] => (
@@ -348,13 +349,13 @@ export async function fetchProviderModelsWithAuth(
348
349
  const fresh = getFreshCached(name, ttlMs, Date.now(), authorityIdentity);
349
350
  if (fresh) {
350
351
  return observed(withConfiguredRetention(
351
- applyConfigHintsToCachedModels(name, prov, fresh, contextCap, metadataModelIdCaseFold, captured.effectiveAlias, metadataConfigDir),
352
+ applyConfigHintsToCachedModels(name, prov, fresh, contextCap, metadataModelIdCaseFold, captured.effectiveAlias, metadataConfigDir, false),
352
353
  ), "authoritative");
353
354
  }
354
355
  const scopedStale = getStaleCached(name, authorityIdentity);
355
356
  if (isModelsFetchCoolingDown(name, undefined, undefined, authorityIdentity) && scopedStale) {
356
357
  return observed(withConfiguredRetention(
357
- applyConfigHintsToCachedModels(name, prov, scopedStale, contextCap, metadataModelIdCaseFold, captured.effectiveAlias, metadataConfigDir),
358
+ applyConfigHintsToCachedModels(name, prov, scopedStale, contextCap, metadataModelIdCaseFold, captured.effectiveAlias, metadataConfigDir, false),
358
359
  ), "degraded");
359
360
  }
360
361
  const live = await fetchCodeBuddyModels(profile, apiKey);
@@ -362,7 +363,7 @@ export async function fetchProviderModelsWithAuth(
362
363
  const discovered = live.models.map(id => ({
363
364
  id,
364
365
  provider: name,
365
- ...catalogHintsFromProviderConfig(name, prov, id, contextCap, metadataModelIdCaseFold, captured.effectiveAlias, metadataConfigDir),
366
+ ...catalogHintsFromProviderConfig(name, prov, id, contextCap, metadataModelIdCaseFold, captured.effectiveAlias, metadataConfigDir, false),
366
367
  }));
367
368
  const forCache = withConfiguredRetention(discovered, { retainComboTargets: false });
368
369
  if (!setCached(name, forCache, Date.now(), cacheGeneration, authorityIdentity)) {
@@ -378,7 +379,7 @@ export async function fetchProviderModelsWithAuth(
378
379
  }
379
380
  const stale = getStaleCached(name, authorityIdentity);
380
381
  return observed(withConfiguredRetention(
381
- stale ? applyConfigHintsToCachedModels(name, prov, stale, contextCap, metadataModelIdCaseFold, captured.effectiveAlias, metadataConfigDir) : configured,
382
+ stale ? applyConfigHintsToCachedModels(name, prov, stale, contextCap, metadataModelIdCaseFold, captured.effectiveAlias, metadataConfigDir, false) : configured,
382
383
  ), "degraded");
383
384
  }
384
385
  if (name === "zed" && prov.adapter === "zed") {
@@ -391,14 +392,14 @@ export async function fetchProviderModelsWithAuth(
391
392
  const cached = getFreshCached(name, ttlMs, Date.now(), authorityIdentity);
392
393
  if (cached) {
393
394
  return observed(
394
- withConfiguredRetention(applyConfigHintsToCachedModels(name, prov, cached, contextCap, metadataModelIdCaseFold, captured.effectiveAlias, metadataConfigDir)),
395
+ withConfiguredRetention(applyConfigHintsToCachedModels(name, prov, cached, contextCap, metadataModelIdCaseFold, captured.effectiveAlias, metadataConfigDir, false)),
395
396
  "authoritative",
396
397
  );
397
398
  }
398
399
  const stale = getStaleCached(name, authorityIdentity);
399
400
  if (isModelsFetchCoolingDown(name, undefined, undefined, authorityIdentity) && stale) {
400
401
  return observed(
401
- withConfiguredRetention(applyConfigHintsToCachedModels(name, prov, stale, contextCap, metadataModelIdCaseFold, captured.effectiveAlias, metadataConfigDir)),
402
+ withConfiguredRetention(applyConfigHintsToCachedModels(name, prov, stale, contextCap, metadataModelIdCaseFold, captured.effectiveAlias, metadataConfigDir, false)),
402
403
  "degraded",
403
404
  );
404
405
  }
@@ -418,7 +419,7 @@ export async function fetchProviderModelsWithAuth(
418
419
  ...(model.maxOutputTokens ? { maxOutputTokens: model.maxOutputTokens } : {}),
419
420
  ...(model.supportsImages ? { inputModalities: ["text", "image"] } : {}),
420
421
  ...(reasoningEfforts?.length ? { reasoningEfforts } : {}),
421
- ...catalogHintsFromProviderConfig(name, prov, model.id, contextCap, metadataModelIdCaseFold, captured.effectiveAlias, metadataConfigDir),
422
+ ...catalogHintsFromProviderConfig(name, prov, model.id, contextCap, metadataModelIdCaseFold, captured.effectiveAlias, metadataConfigDir, false),
422
423
  } as CatalogModel;
423
424
  });
424
425
  const forCache = withConfiguredRetention(discovered, { retainComboTargets: false });
@@ -433,7 +434,7 @@ export async function fetchProviderModelsWithAuth(
433
434
  markProviderDiscoveryFailed(name, { reason: "provider" });
434
435
  }
435
436
  return observed(
436
- withConfiguredRetention(stale ? applyConfigHintsToCachedModels(name, prov, stale, contextCap, metadataModelIdCaseFold, captured.effectiveAlias, metadataConfigDir) : configured),
437
+ withConfiguredRetention(stale ? applyConfigHintsToCachedModels(name, prov, stale, contextCap, metadataModelIdCaseFold, captured.effectiveAlias, metadataConfigDir, false) : configured),
437
438
  "degraded",
438
439
  );
439
440
  }
@@ -449,13 +450,13 @@ export async function fetchProviderModelsWithAuth(
449
450
  const fresh = getFreshCached(name, ttlMs, Date.now(), authorityIdentity);
450
451
  if (fresh) {
451
452
  return observed(withConfiguredRetention(
452
- applyConfigHintsToCachedModels(name, prov, fresh, contextCap, metadataModelIdCaseFold, captured.effectiveAlias, metadataConfigDir),
453
+ applyConfigHintsToCachedModels(name, prov, fresh, contextCap, metadataModelIdCaseFold, captured.effectiveAlias, metadataConfigDir, false),
453
454
  ), "authoritative");
454
455
  }
455
456
  const scopedStale = getStaleCached(name, authorityIdentity);
456
457
  if (isModelsFetchCoolingDown(name) && scopedStale) {
457
458
  return observed(withConfiguredRetention(
458
- applyConfigHintsToCachedModels(name, prov, scopedStale, contextCap, metadataModelIdCaseFold, captured.effectiveAlias, metadataConfigDir),
459
+ applyConfigHintsToCachedModels(name, prov, scopedStale, contextCap, metadataModelIdCaseFold, captured.effectiveAlias, metadataConfigDir, false),
459
460
  ), "degraded");
460
461
  }
461
462
  const live = await fetchQoderModels(profile, apiKey);
@@ -463,7 +464,7 @@ export async function fetchProviderModelsWithAuth(
463
464
  const discovered = live.models.map(id => ({
464
465
  id,
465
466
  provider: name,
466
- ...catalogHintsFromProviderConfig(name, prov, id, contextCap, metadataModelIdCaseFold, captured.effectiveAlias, metadataConfigDir),
467
+ ...catalogHintsFromProviderConfig(name, prov, id, contextCap, metadataModelIdCaseFold, captured.effectiveAlias, metadataConfigDir, false),
467
468
  }));
468
469
  const forCache = withConfiguredRetention(discovered, { retainComboTargets: false });
469
470
  if (!setCached(name, forCache, Date.now(), cacheGeneration, authorityIdentity)) {
@@ -479,7 +480,7 @@ export async function fetchProviderModelsWithAuth(
479
480
  }
480
481
  const stale = getStaleCached(name, authorityIdentity);
481
482
  return observed(withConfiguredRetention(
482
- stale ? applyConfigHintsToCachedModels(name, prov, stale, contextCap, metadataModelIdCaseFold, captured.effectiveAlias, metadataConfigDir) : configured,
483
+ stale ? applyConfigHintsToCachedModels(name, prov, stale, contextCap, metadataModelIdCaseFold, captured.effectiveAlias, metadataConfigDir, false) : configured,
483
484
  ), "degraded");
484
485
  }
485
486
  if (prov.adapter === "devin") {
@@ -494,7 +495,7 @@ export async function fetchProviderModelsWithAuth(
494
495
  const cachedDevin = getFreshCached(name, ttlMs, Date.now(), authorityIdentity);
495
496
  if (cachedDevin) {
496
497
  return observed(
497
- withConfiguredRetention(applyConfigHintsToCachedModels(name, prov, cachedDevin, undefined, undefined, undefined, metadataConfigDir)),
498
+ withConfiguredRetention(applyConfigHintsToCachedModels(name, prov, cachedDevin, undefined, undefined, undefined, metadataConfigDir, false)),
498
499
  "authoritative",
499
500
  );
500
501
  }
@@ -502,7 +503,7 @@ export async function fetchProviderModelsWithAuth(
502
503
  const cooling = getStaleCached(name, authorityIdentity);
503
504
  return observed(
504
505
  withConfiguredRetention(
505
- cooling ? applyConfigHintsToCachedModels(name, prov, cooling, undefined, undefined, undefined, metadataConfigDir) : configured,
506
+ cooling ? applyConfigHintsToCachedModels(name, prov, cooling, undefined, undefined, undefined, metadataConfigDir, false) : configured,
506
507
  ),
507
508
  "degraded",
508
509
  );
@@ -540,11 +541,11 @@ export async function fetchProviderModelsWithAuth(
540
541
  ...(liveResult.defaultEfforts[id] ? { defaultReasoningEffort: liveResult.defaultEfforts[id] } : {}),
541
542
  // The account catalog's per-base supportsImages vote collapses to one
542
543
  // modalities value. It spreads before the hints so exact
543
- // modelCapabilities declarations, the legacy modelInputModalities
544
- // record and the vision-sidecar rewrite keep winning — the live
545
- // value survives only when none of them applies.
544
+ // modelCapabilities declarations and the legacy modelInputModalities
545
+ // record keep winning. Sidecar advertisement is added after discovery;
546
+ // the live value survives only when no explicit declaration applies.
546
547
  ...(liveResult.inputModalities[id]?.length ? { inputModalities: liveResult.inputModalities[id] } : {}),
547
- ...catalogHintsFromProviderConfig(name, prov, id, contextCap, metadataModelIdCaseFold, captured.effectiveAlias, metadataConfigDir),
548
+ ...catalogHintsFromProviderConfig(name, prov, id, contextCap, metadataModelIdCaseFold, captured.effectiveAlias, metadataConfigDir, false),
548
549
  } as CatalogModel;
549
550
  });
550
551
  const forCache = withConfiguredRetention(result, { retainComboTargets: false });
@@ -560,7 +561,7 @@ export async function fetchProviderModelsWithAuth(
560
561
  }
561
562
  const stale = getStaleCached(name, authorityIdentity);
562
563
  return observed(
563
- withConfiguredRetention(stale ? applyConfigHintsToCachedModels(name, prov, stale, undefined, undefined, undefined, metadataConfigDir) : configured),
564
+ withConfiguredRetention(stale ? applyConfigHintsToCachedModels(name, prov, stale, undefined, undefined, undefined, metadataConfigDir, false) : configured),
564
565
  "degraded",
565
566
  );
566
567
  }
@@ -577,7 +578,7 @@ export async function fetchProviderModelsWithAuth(
577
578
  const cachedCursor = getFreshCached(name, ttlMs, Date.now(), authorityIdentity);
578
579
  if (cachedCursor) {
579
580
  return observed(
580
- withConfiguredRetention(applyConfigHintsToCachedModels(name, prov, cachedCursor, undefined, metadataModelIdCaseFold, captured.effectiveAlias, metadataConfigDir)),
581
+ withConfiguredRetention(applyConfigHintsToCachedModels(name, prov, cachedCursor, undefined, metadataModelIdCaseFold, captured.effectiveAlias, metadataConfigDir, false)),
581
582
  "authoritative",
582
583
  );
583
584
  }
@@ -585,7 +586,7 @@ export async function fetchProviderModelsWithAuth(
585
586
  const cooling = getStaleCached(name, authorityIdentity);
586
587
  return observed(
587
588
  withConfiguredRetention(
588
- cooling ? applyConfigHintsToCachedModels(name, prov, cooling, undefined, metadataModelIdCaseFold, captured.effectiveAlias, metadataConfigDir) : configured,
589
+ cooling ? applyConfigHintsToCachedModels(name, prov, cooling, undefined, metadataModelIdCaseFold, captured.effectiveAlias, metadataConfigDir, false) : configured,
589
590
  ),
590
591
  "degraded",
591
592
  );
@@ -631,7 +632,7 @@ export async function fetchProviderModelsWithAuth(
631
632
  const staleCursor = getStaleCached(name, authorityIdentity);
632
633
  return observed(
633
634
  withConfiguredRetention(
634
- staleCursor ? applyConfigHintsToCachedModels(name, prov, staleCursor, undefined, metadataModelIdCaseFold, captured.effectiveAlias, metadataConfigDir) : configured,
635
+ staleCursor ? applyConfigHintsToCachedModels(name, prov, staleCursor, undefined, metadataModelIdCaseFold, captured.effectiveAlias, metadataConfigDir, false) : configured,
635
636
  ),
636
637
  "degraded",
637
638
  );
@@ -649,7 +650,7 @@ export async function fetchProviderModelsWithAuth(
649
650
  if (fresh) {
650
651
  return observed(
651
652
  withConfiguredRetention(
652
- withVertexDefaultSeed(applyConfigHintsToCachedModels(name, prov, fresh, contextCap, metadataModelIdCaseFold, captured.effectiveAlias, metadataConfigDir)),
653
+ withVertexDefaultSeed(applyConfigHintsToCachedModels(name, prov, fresh, contextCap, metadataModelIdCaseFold, captured.effectiveAlias, metadataConfigDir, false)),
653
654
  ),
654
655
  "authoritative",
655
656
  ); // dedups Codex's frequent /v1/models polling within the TTL
@@ -661,7 +662,7 @@ export async function fetchProviderModelsWithAuth(
661
662
  return observed(
662
663
  withConfiguredRetention(
663
664
  stale
664
- ? withVertexDefaultSeed(applyConfigHintsToCachedModels(name, prov, stale, contextCap, metadataModelIdCaseFold, captured.effectiveAlias, metadataConfigDir))
665
+ ? withVertexDefaultSeed(applyConfigHintsToCachedModels(name, prov, stale, contextCap, metadataModelIdCaseFold, captured.effectiveAlias, metadataConfigDir, false))
665
666
  : failedDiscoveryConfigured,
666
667
  ),
667
668
  "degraded",
@@ -706,7 +707,7 @@ export async function fetchProviderModelsWithAuth(
706
707
  return {
707
708
  models: withConfiguredRetention(
708
709
  stale
709
- ? withVertexDefaultSeed(applyConfigHintsToCachedModels(name, prov, stale, contextCap, metadataModelIdCaseFold, captured.effectiveAlias, metadataConfigDir))
710
+ ? withVertexDefaultSeed(applyConfigHintsToCachedModels(name, prov, stale, contextCap, metadataModelIdCaseFold, captured.effectiveAlias, metadataConfigDir, false))
710
711
  : failedDiscoveryConfigured,
711
712
  ),
712
713
  fallback: stale ? "stale" : "configured",
@@ -797,7 +798,7 @@ export async function fetchProviderModelsWithAuth(
797
798
  ...(model.contextWindow ? { contextWindow: model.contextWindow } : {}),
798
799
  ...(model.inputModalities ? { inputModalities: model.inputModalities } : {}),
799
800
  }));
800
- const live = applyConfigHintsToCachedModels(name, prov, upstream, contextCap, metadataModelIdCaseFold, captured.effectiveAlias, metadataConfigDir);
801
+ const live = applyConfigHintsToCachedModels(name, prov, upstream, contextCap, metadataModelIdCaseFold, captured.effectiveAlias, metadataConfigDir, false);
801
802
  const forCache = azureMetadata ? upstream : withConfiguredRetention(live, { retainComboTargets: false });
802
803
  if (!isCurrentCacheGeneration()) {
803
804
  return observed(withConfiguredRetention(configured), "degraded");
@@ -878,7 +879,7 @@ export async function fetchProviderModelsWithAuth(
878
879
  };
879
880
  })
880
881
  .filter(m => shouldExposeProviderModel(name, m.id));
881
- const live = applyConfigHintsToCachedModels(name, prov, upstream, contextCap, metadataModelIdCaseFold, captured.effectiveAlias, metadataConfigDir);
882
+ const live = applyConfigHintsToCachedModels(name, prov, upstream, contextCap, metadataModelIdCaseFold, captured.effectiveAlias, metadataConfigDir, false);
882
883
  // Capture the count BEFORE the alias/configured augmentation below pushes extra rows into
883
884
  // `live`; otherwise configured entries would be reported as discovered ones.
884
885
  const liveModelCount = live.length;
@@ -925,6 +926,7 @@ export async function fetchProviderModelsWithAuth(
925
926
  }
926
927
  }
927
928
 
929
+ /** Preserve the provider-only API's default-enabled projection without widening cached evidence. */
928
930
  export async function fetchProviderModels(
929
931
  name: string,
930
932
  prov: OcxProviderConfig,
@@ -937,5 +939,5 @@ export async function fetchProviderModels(
937
939
  ttlMs,
938
940
  contextCap,
939
941
  refreshingModelsAuthResolver,
940
- )).models;
942
+ )).models.map(model => withVisionSidecarInput(model, captured.provider));
941
943
  }
@@ -35,7 +35,6 @@ import {
35
35
  import type { OcxConfig, OcxProviderConfig } from "../../types";
36
36
  import { modelInList } from "../../types";
37
37
  import { CODEX_REASONING_LEVELS, codexEffortRank, configuredReasoningEfforts, modelRecordValue, sanitizeCodexReasoningEfforts } from "../../reasoning-effort";
38
- import { isModelVisionSidecarConsumer } from "../../vision/eligibility";
39
38
  import { getModelMetadata, getModelMetadataCaseInsensitive, listModelMetadata, resolveMetadataProvider, type ModelMetadata } from "../../generated/model-metadata";
40
39
  import { enrichProviderFromRegistry, shouldCaseFoldMetadataModelId } from "../../providers/derive";
41
40
  import {
@@ -103,7 +102,7 @@ import type {
103
102
  CatalogTrustedOpenAiApiPolicySnapshot,
104
103
  } from "../convergence-types";
105
104
  import type { CatalogGatherProviderAuthOutcome, CatalogGatherProviderModelOutcome, GatherFlightCapture, ModelsAuthResolverFactory } from "./gather-capture";
106
- import { applyProviderConfigHints, configuredAutoCompactTokenLimit, configuredMaxInputTokens, configuredReasoningSummarySupport, modelInputModalities, routedMaxOutputTokens } from "./model-hints";
105
+ import { applyProviderConfigHints, configuredAutoCompactTokenLimit, configuredMaxInputTokens, configuredReasoningSummarySupport, modelInputModalities, routedMaxOutputTokens, withVisionSidecarInput } from "./model-hints";
107
106
  import { resolveComboCatalogMember } from "./combo-member";
108
107
  import { captureGatherFlight, captureTrustedOpenAiApiPolicy, gatherFlightKey, keyedGatherBytesIdentity, withCanonicalOpenAiForwardAuthDefault } from "./gather-capture";
109
108
  import { fetchProviderModelsWithAuth, observedModelsAuthResolver, refreshingModelsAuthResolver } from "./provider-models";
@@ -330,6 +329,7 @@ async function gatherRoutedModelsUncached(
330
329
  const localProviderAuthOutcomes = capture.providerAuthOutcomes;
331
330
  const resolveAuth = capture.authResolver;
332
331
  const ttlMs = config.modelCacheTtlMs ?? DEFAULT_MODEL_CACHE_TTL_MS;
332
+ const visionSidecarEnabled = config.visionSidecar?.enabled !== false;
333
333
  // Persisted provider entries can predate newer registry fields (noVisionModels,
334
334
  // modelInputModalities, ...). The ROUTER merges registry seeds at request time
335
335
  // (routedProviderConfig), so the proxy behaves correctly — the catalog listing must see the
@@ -337,6 +337,8 @@ async function gatherRoutedModelsUncached(
337
337
  // vision-sidecar model advertised text-only, blocking image attachments app-side).
338
338
  // Enrich a CLONE: hydrated defaults must never leak into the persisted config.
339
339
  const activeProviders = capture.providers;
340
+ // Use admission-time provider clones for sidecar advertisement, custom rows and combo members.
341
+ const enrichedByName = new Map(activeProviders.map(provider => [provider.name, provider.provider]));
340
342
  const providerResults = await Promise.all(
341
343
  activeProviders.map(provider => fetchProviderModelsWithAuth(
342
344
  provider,
@@ -378,7 +380,9 @@ async function gatherRoutedModelsUncached(
378
380
  config,
379
381
  metadataModelIdCaseFoldByProvider,
380
382
  metadataConfigDirByProvider,
383
+ false,
381
384
  )
385
+ .map(model => withVisionSidecarInput(model, enrichedByName.get(model.provider), visionSidecarEnabled))
382
386
  // Drop image/video generation models (e.g. Grok image/video) by default. Cursor's static catalog
383
387
  // intentionally mirrors Cursor's public model table, including Gemini image preview, so the
384
388
  // exposure decision goes through shouldExposeRoutedModel (single choke point).
@@ -482,9 +486,6 @@ async function gatherRoutedModelsUncached(
482
486
  ...(custom.codexToolMode !== undefined ? { codexToolMode: custom.codexToolMode } : {}),
483
487
  });
484
488
  }
485
- // Enriched (registry-hydrated) provider clones — shared by combo member synthesis and
486
- // custom-model vision-sidecar inheritance so both see the same merged registry view.
487
- const enrichedByName = new Map(activeProviders.map(provider => [provider.name, provider.provider]));
488
489
  for (const id of listComboIds(config)) {
489
490
  const combo = getCombo(config, id);
490
491
  if (!combo) continue;
@@ -521,6 +522,7 @@ async function gatherRoutedModelsUncached(
521
522
  nativeAliasFallback,
522
523
  metadataModelIdCaseFoldByProvider.get(target.provider),
523
524
  metadataConfigDirByProvider.get(target.provider),
525
+ visionSidecarEnabled,
524
526
  ))
525
527
  .filter((member): member is CatalogModel => member !== undefined);
526
528
  const derived = deriveComboCatalogModel(id, combo, members);
@@ -714,7 +716,7 @@ async function gatherRoutedModelsUncached(
714
716
  const customContextHints = merged.contextWindow === undefined && effectiveProvider && canInferVendorContext(cm.provider, effectiveProvider)
715
717
  ? applyProviderConfigHints(cm.provider, effectiveProvider, { provider: cm.provider, id: cm.modelId },
716
718
  providerContextCap(config, cm.provider), metadataModelIdCaseFoldByProvider.get(cm.provider),
717
- undefined, metadataConfigDirByProvider.get(cm.provider))
719
+ undefined, metadataConfigDirByProvider.get(cm.provider), false)
718
720
  : undefined;
719
721
  const contextResolved: CatalogModel = customContextHints?.contextWindow !== undefined
720
722
  ? { ...merged, contextWindow: customContextHints.contextWindow,
@@ -762,13 +764,7 @@ async function gatherRoutedModelsUncached(
762
764
  : mergedWithHardBounds;
763
765
  const enrichedProvider = enrichedByName.get(cm.provider) ?? rawProvider;
764
766
  // Reuse the request-time consumer predicate so custom rows cannot drift from catalog hints.
765
- if (enrichedProvider && isModelVisionSidecarConsumer(enrichedProvider, mergedWithAutoCompact.id)) {
766
- const current = mergedWithAutoCompact.inputModalities ?? ["text"];
767
- if (!current.includes("image")) {
768
- return { ...mergedWithAutoCompact, inputModalities: [...current, "image"] };
769
- }
770
- }
771
- return mergedWithAutoCompact;
767
+ return withVisionSidecarInput(mergedWithAutoCompact, enrichedProvider, visionSidecarEnabled);
772
768
  });
773
769
  // Custom rows override discovered rows that encode to the same Codex-facing slug. A provider whose
774
770
  // captured discovery declares an order (preferFirst) keeps each replacement in the discovered row's
@@ -948,6 +944,7 @@ export function augmentRoutedModelsWithMetadata(
948
944
  caps?: Pick<OcxConfig, "providerContextCaps">,
949
945
  metadataModelIdCaseFoldByProvider?: ReadonlyMap<string, boolean>,
950
946
  metadataConfigDirByProvider?: ReadonlyMap<string, string>,
947
+ includeVisionSidecar = true,
951
948
  ): CatalogModel[] {
952
949
  const out = [...models];
953
950
  const indexByKey = new Map(out.map((model, index) => [`${model.provider}/${model.id}`, index]));
@@ -977,6 +974,7 @@ export function augmentRoutedModelsWithMetadata(
977
974
  metadataModelIdCaseFoldByProvider?.get(provider),
978
975
  undefined,
979
976
  metadataConfigDirByProvider?.get(provider),
977
+ includeVisionSidecar,
980
978
  )
981
979
  : seeded;
982
980
  continue;
@@ -1001,6 +999,7 @@ export function augmentRoutedModelsWithMetadata(
1001
999
  metadataModelIdCaseFoldByProvider?.get(provider),
1002
1000
  undefined,
1003
1001
  metadataConfigDirByProvider?.get(provider),
1002
+ includeVisionSidecar,
1004
1003
  )
1005
1004
  : {}),
1006
1005
  });