@bitkyc08/opencodex 2.84.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 (518) hide show
  1. package/README.md +0 -4
  2. package/bin/ocx.mjs +61 -3
  3. package/gui/dist/assets/App-ZfYbLHkB.js +58 -0
  4. package/gui/dist/assets/App-wT3mXqFz.css +1 -0
  5. package/gui/dist/assets/{Tray-C9TGtwad.js → Tray-DCd-A1az.js} +1 -1
  6. package/gui/dist/assets/index-CfYDMj34.js +94 -0
  7. package/gui/dist/assets/index-DPeB9HpV.css +1 -0
  8. package/gui/dist/assets/quota-summary-ChvmKGOk.js +1 -0
  9. package/gui/dist/index.html +2 -2
  10. package/package.json +1 -2
  11. package/src/adapters/anthropic/beta-allowlist.ts +91 -11
  12. package/src/adapters/anthropic/beta-body-pairs.ts +93 -0
  13. package/src/adapters/anthropic/client-identity.ts +1 -1
  14. package/src/adapters/anthropic/passthrough.ts +16 -3
  15. package/src/adapters/anthropic-progress.ts +84 -0
  16. package/src/adapters/anthropic.ts +108 -21
  17. package/src/adapters/azure.ts +19 -1
  18. package/src/adapters/base.ts +2 -0
  19. package/src/adapters/claude-cli/adapter.ts +111 -23
  20. package/src/adapters/claude-cli/profiles.ts +2 -1
  21. package/src/adapters/claude-cli/stable-replay.ts +108 -0
  22. package/src/adapters/claude-cli/usage-admission.ts +230 -0
  23. package/src/adapters/client-fingerprint.ts +24 -0
  24. package/src/adapters/coding-agent/protocol.ts +1 -1
  25. package/src/adapters/coding-agent/turn.ts +40 -11
  26. package/src/adapters/cursor/arg-codec.ts +17 -5
  27. package/src/adapters/cursor/catalog.ts +8 -0
  28. package/src/adapters/cursor/effort-map.ts +3 -0
  29. package/src/adapters/cursor/freeform-args.ts +75 -0
  30. package/src/adapters/cursor/protobuf-events.ts +36 -23
  31. package/src/adapters/dsh-account.ts +97 -0
  32. package/src/adapters/google-antigravity-wire.ts +22 -1
  33. package/src/adapters/google.ts +37 -10
  34. package/src/adapters/openai-chat/messages.ts +4 -3
  35. package/src/adapters/openai-chat/passthrough.ts +8 -2
  36. package/src/adapters/openai-chat/reasoning-replay.ts +52 -0
  37. package/src/adapters/openai-chat/response-events.ts +59 -1
  38. package/src/adapters/openai-chat.ts +8 -5
  39. package/src/adapters/openai-responses/compaction-search-history.ts +14 -7
  40. package/src/adapters/openai-responses/passthrough.ts +57 -125
  41. package/src/adapters/openai-responses/request-strips.ts +25 -3
  42. package/src/adapters/openai-responses/transport.ts +81 -0
  43. package/src/adapters/qoder/adapter.ts +44 -3
  44. package/src/adapters/registry.ts +5 -0
  45. package/src/adapters/zed.ts +26 -1
  46. package/src/advisor/activity.ts +55 -0
  47. package/src/advisor/budget.ts +62 -0
  48. package/src/advisor/consult.ts +223 -0
  49. package/src/advisor/context.ts +238 -0
  50. package/src/advisor/disclosure.ts +20 -0
  51. package/src/advisor/evidence-tool.ts +21 -0
  52. package/src/advisor/evidence.ts +117 -0
  53. package/src/advisor/runtime.ts +353 -0
  54. package/src/advisor/settings.ts +144 -0
  55. package/src/advisor/state.ts +369 -0
  56. package/src/advisor/synthetic-tool.ts +33 -0
  57. package/src/bridge/errors.ts +24 -1
  58. package/src/bridge/internal.ts +3 -1
  59. package/src/bridge/response-json.ts +18 -3
  60. package/src/bridge/sse.ts +58 -8
  61. package/src/bridge/terminal-refusal.ts +59 -0
  62. package/src/chat/inbound.ts +30 -6
  63. package/src/chat/outbound.ts +5 -0
  64. package/src/claude/desktop-3p-library.ts +72 -7
  65. package/src/claude/desktop-3p.ts +75 -19
  66. package/src/claude/desktop-first-party.ts +1 -1
  67. package/src/claude/desktop-picker-platform.ts +4 -0
  68. package/src/claude/desktop-picker-profile.ts +11 -4
  69. package/src/claude/desktop-picker.ts +15 -8
  70. package/src/claude/desktop-profile.ts +10 -1
  71. package/src/claude/desktop-remote-store-io.ts +5 -3
  72. package/src/claude/desktop-system-proxy.ts +50 -4
  73. package/src/claude/inbound-content-options.ts +11 -0
  74. package/src/claude/inbound.ts +154 -50
  75. package/src/claude/intercept/local-ca.ts +8 -1
  76. package/src/claude/intercept/picker-ca-startup.ts +2 -1
  77. package/src/claude/intercept/picker-runtime.ts +6 -5
  78. package/src/claude/intercept/picker-trust-windows.ts +338 -0
  79. package/src/claude/intercept/picker-trust.ts +19 -0
  80. package/src/claude/intercept/port-policy.ts +30 -0
  81. package/src/claude/intercept/runtime.ts +97 -94
  82. package/src/claude/model-default-settings.ts +154 -0
  83. package/src/claude/model-info.ts +7 -7
  84. package/src/claude/outbound.ts +51 -19
  85. package/src/cli/account-api.ts +1 -0
  86. package/src/cli/account-policy-dto.ts +6 -0
  87. package/src/cli/account-policy.ts +34 -5
  88. package/src/cli/account-preservation.ts +44 -0
  89. package/src/cli/account.ts +8 -2
  90. package/src/cli/advisor.ts +165 -0
  91. package/src/cli/auxiliary-listener-preflight.ts +37 -0
  92. package/src/cli/capabilities-accounts.ts +20 -1
  93. package/src/cli/capabilities-agents-routing.ts +23 -2
  94. package/src/cli/capabilities-base.ts +39 -11
  95. package/src/cli/capabilities-observe-system.ts +63 -3
  96. package/src/cli/capabilities-provider-models.ts +11 -0
  97. package/src/cli/chatgpt-command.ts +35 -14
  98. package/src/cli/claude.ts +46 -6
  99. package/src/cli/codex-cli-update.ts +151 -41
  100. package/src/cli/codex-provider.ts +44 -0
  101. package/src/cli/combo-input.ts +10 -1
  102. package/src/cli/combo-result.ts +1 -1
  103. package/src/cli/combo.ts +2 -1
  104. package/src/cli/cross-home-owner.ts +17 -14
  105. package/src/cli/dispatch.ts +30 -5
  106. package/src/cli/doctor.ts +15 -1
  107. package/src/cli/ensure-desktop-cold-start.ts +382 -0
  108. package/src/cli/export-command.ts +1 -1
  109. package/src/cli/help.ts +4 -2
  110. package/src/cli/index.ts +14 -5
  111. package/src/cli/integration-plan-dto.ts +3 -0
  112. package/src/cli/log-history.ts +172 -0
  113. package/src/cli/models-runtime-subcommands.ts +1 -0
  114. package/src/cli/models-runtime.ts +72 -8
  115. package/src/cli/observe-stream.ts +2 -0
  116. package/src/cli/observe.ts +16 -4
  117. package/src/cli/provider.ts +27 -5
  118. package/src/cli/registry.ts +41 -9
  119. package/src/cli/restart-scope.ts +3 -1
  120. package/src/cli/runtime-api.ts +5 -3
  121. package/src/cli/system-command.ts +40 -8
  122. package/src/cli/uninstall-client-state.ts +32 -0
  123. package/src/cli/usage-compare.ts +309 -0
  124. package/src/cli/usage-model-search.ts +4 -4
  125. package/src/cli/usage-report.ts +2 -2
  126. package/src/cli/v2.ts +1 -1
  127. package/src/client/machine-listener.ts +1 -3
  128. package/src/client/runtime.ts +1 -0
  129. package/src/clients/client-path.ts +51 -0
  130. package/src/clients/config-export/contracts.ts +3 -1
  131. package/src/clients/config-export/model-metadata.ts +3 -2
  132. package/src/clients/config-export/qoder.ts +93 -0
  133. package/src/clients/config-export.ts +35 -48
  134. package/src/clients/gentle-shell-path.ts +87 -0
  135. package/src/codex/account-auto-switch.ts +2 -0
  136. package/src/codex/account-identity.ts +29 -0
  137. package/src/codex/account-label.ts +1 -1
  138. package/src/codex/account-lifecycle.ts +21 -5
  139. package/src/codex/account-preservation.ts +28 -0
  140. package/src/codex/account-use-remaining.ts +23 -0
  141. package/src/codex/app-server-processes.ts +97 -7
  142. package/src/codex/app-server-restart-service.ts +55 -4
  143. package/src/codex/auth-api/account-list.ts +4 -0
  144. package/src/codex/auth-api/account-pause-group.ts +6 -50
  145. package/src/codex/auth-api/account-preservation.ts +57 -0
  146. package/src/codex/auth-api/pool-mode-gate.ts +5 -2
  147. package/src/codex/auth-api/routes.ts +8 -0
  148. package/src/codex/auth-api/use-remaining-route.ts +36 -0
  149. package/src/codex/auth-api.ts +0 -1
  150. package/src/codex/auth-context.ts +81 -7
  151. package/src/codex/autostart-health.ts +4 -0
  152. package/src/codex/catalog/auto-review.ts +20 -3
  153. package/src/codex/catalog/build-entries.ts +19 -2
  154. package/src/codex/catalog/combo-member.ts +14 -5
  155. package/src/codex/catalog/configured-tiers.ts +50 -0
  156. package/src/codex/catalog/effort.ts +4 -1
  157. package/src/codex/catalog/gather-capture.ts +2 -0
  158. package/src/codex/catalog/metadata.ts +11 -9
  159. package/src/codex/catalog/model-hints.ts +42 -19
  160. package/src/codex/catalog/model-visibility.ts +3 -1
  161. package/src/codex/catalog/native-ultrafast.ts +106 -0
  162. package/src/codex/catalog/parsing.ts +35 -86
  163. package/src/codex/catalog/provider-models.ts +40 -34
  164. package/src/codex/catalog/retained-sync.ts +2 -0
  165. package/src/codex/catalog/routed-gather.ts +33 -16
  166. package/src/codex/catalog/vendor-context.ts +82 -0
  167. package/src/codex/cli-install-provenance.ts +105 -17
  168. package/src/codex/cli-update-plan.ts +545 -0
  169. package/src/codex/config-write-lock.ts +63 -4
  170. package/src/codex/convergence.ts +2 -0
  171. package/src/codex/desktop-app/picker-freshness.ts +96 -0
  172. package/src/codex/desktop-app/types.ts +2 -0
  173. package/src/codex/desktop-app/windows.ts +29 -16
  174. package/src/codex/desktop-app-restart.ts +25 -2
  175. package/src/codex/desktop-compatibility/certificate-service.ts +99 -0
  176. package/src/codex/desktop-compatibility/certificate-store.ts +193 -0
  177. package/src/codex/desktop-compatibility/connection-store.ts +87 -0
  178. package/src/codex/desktop-compatibility/installed-build.ts +73 -0
  179. package/src/codex/desktop-compatibility/json-body.ts +29 -0
  180. package/src/codex/desktop-compatibility/native-identity.ts +62 -0
  181. package/src/codex/desktop-compatibility/relay-listener.ts +93 -0
  182. package/src/codex/desktop-compatibility/routing-binding.ts +10 -0
  183. package/src/codex/desktop-compatibility/routing-preflight.ts +69 -0
  184. package/src/codex/desktop-compatibility/runtime-ownership.ts +28 -0
  185. package/src/codex/desktop-compatibility/runtime.ts +182 -0
  186. package/src/codex/desktop-compatibility/service.ts +30 -0
  187. package/src/codex/desktop-compatibility/startup-settings.ts +50 -0
  188. package/src/codex/desktop-compatibility/usage-activation.ts +86 -0
  189. package/src/codex/desktop-compatibility/usage-controlled-fetch.ts +59 -0
  190. package/src/codex/desktop-compatibility/usage-controller.ts +107 -0
  191. package/src/codex/desktop-compatibility/usage-policy.ts +59 -0
  192. package/src/codex/desktop-compatibility/usage-refresh.ts +33 -0
  193. package/src/codex/desktop-compatibility/usage-sse-controller.ts +86 -0
  194. package/src/codex/desktop-compatibility/windows-activation-source.ts +44 -0
  195. package/src/codex/desktop-compatibility/windows-certificate-trust.ts +81 -0
  196. package/src/codex/desktop-compatibility/windows-key-protection.ts +165 -0
  197. package/src/codex/desktop-compatibility/windows-package-command.ts +86 -0
  198. package/src/codex/desktop-compatibility/windows-package-launch.ts +28 -0
  199. package/src/codex/features.ts +85 -45
  200. package/src/codex/history-provider.ts +1 -1
  201. package/src/codex/inject/config-write-section.ts +4 -1
  202. package/src/codex/inject/history-failure.ts +27 -0
  203. package/src/codex/inject/remove.ts +18 -6
  204. package/src/codex/inject/restore.ts +24 -2
  205. package/src/codex/inject/routing-classify.ts +4 -3
  206. package/src/codex/inject.ts +18 -27
  207. package/src/codex/journal.ts +16 -1
  208. package/src/codex/main-account-hard-lock.ts +30 -1
  209. package/src/codex/main-account.ts +99 -15
  210. package/src/codex/native-main-refresh-mode.ts +217 -0
  211. package/src/codex/native-profile-startup.ts +1 -1
  212. package/src/codex/ocx-compaction-history.ts +12 -10
  213. package/src/codex/paths.ts +86 -1
  214. package/src/codex/provider-routing.ts +174 -0
  215. package/src/codex/refresh.ts +2 -2
  216. package/src/codex/routing/health-store.ts +2 -2
  217. package/src/codex/routing/pool-recovery.ts +79 -0
  218. package/src/codex/routing/selection.ts +4 -3
  219. package/src/codex/routing/thread-affinity.ts +1 -1
  220. package/src/codex/shim-diagnostics.ts +6 -0
  221. package/src/codex/shim-overlay.ts +7 -2
  222. package/src/codex/signed-in-identity.ts +43 -0
  223. package/src/codex/subagent-model-fallback.ts +1 -1
  224. package/src/codex/sync.ts +6 -1
  225. package/src/codex/user-identity.ts +16 -5
  226. package/src/codex/windows-local-appdata.ts +84 -0
  227. package/src/combos/failover.ts +6 -1
  228. package/src/combos/index.ts +1 -0
  229. package/src/combos/resolve.ts +46 -10
  230. package/src/combos/types.ts +15 -0
  231. package/src/config/diagnostics.ts +35 -7
  232. package/src/config/live-reconcile.ts +51 -2
  233. package/src/config/load-degrade.ts +23 -1
  234. package/src/config/opencode-wire-upgrade.ts +54 -0
  235. package/src/config/opencode-zen-upgrade.ts +52 -0
  236. package/src/config/owner-registry.ts +22 -17
  237. package/src/config/paths.ts +10 -3
  238. package/src/config/provider-validation.ts +45 -2
  239. package/src/config/schema/config-schema.ts +39 -0
  240. package/src/config/schema/desktop-compatibility.ts +9 -0
  241. package/src/config/schema/leaf-validators.ts +39 -9
  242. package/src/config/schema/main-account-hard-lock-fallback.ts +26 -0
  243. package/src/config/schema/native-reasoning-retention.ts +26 -0
  244. package/src/config.ts +2 -0
  245. package/src/generated/compatibility-version.json +962 -350
  246. package/src/generated/model-metadata.ts +1 -1
  247. package/src/grok/sync.ts +1 -1
  248. package/src/images/loop.ts +46 -8
  249. package/src/integrations/catalog-refresh.ts +1 -1
  250. package/src/integrations/config-io.ts +20 -2
  251. package/src/integrations/journal.ts +15 -1
  252. package/src/integrations/mutation-plan.ts +2 -0
  253. package/src/integrations/registry.ts +15 -0
  254. package/src/integrations/store.ts +3 -0
  255. package/src/integrations/writer.ts +10 -1
  256. package/src/lib/advisor-activation.ts +16 -0
  257. package/src/lib/background-work.ts +65 -0
  258. package/src/lib/bounded-file-read.ts +58 -0
  259. package/src/lib/bun-path-runtime.d.mts +10 -1
  260. package/src/lib/bun-path-runtime.mjs +62 -3
  261. package/src/lib/codex-restart-contract.ts +15 -0
  262. package/src/lib/desktop-proxy-route.ts +30 -0
  263. package/src/lib/desktop-upstream-tunnel.ts +72 -0
  264. package/src/lib/empty-tool-catalog.ts +62 -0
  265. package/src/lib/errors.ts +26 -3
  266. package/src/lib/gcp-adc.ts +96 -9
  267. package/src/lib/gcp-credential-input.ts +12 -0
  268. package/src/lib/lab-live-route-production.ts +5 -0
  269. package/src/lib/local-internal-call-capability.ts +55 -0
  270. package/src/lib/opaque-proxy-tunnel.ts +217 -0
  271. package/src/lib/package-tree-integrity.ts +2 -2
  272. package/src/lib/redact.ts +2 -2
  273. package/src/lib/socks5-fetch.ts +48 -71
  274. package/src/lib/socks5-handshake.ts +112 -0
  275. package/src/lib/translator-budget.ts +7 -1
  276. package/src/oauth/account-quota-rank.ts +69 -2
  277. package/src/oauth/anthropic-account-refusal.ts +20 -2
  278. package/src/oauth/anthropic-oauth-definitions.ts +3 -1
  279. package/src/oauth/anthropic-routing.ts +1 -21
  280. package/src/oauth/anthropic.ts +38 -7
  281. package/src/oauth/callback-server.ts +27 -1
  282. package/src/oauth/chatgpt-device.ts +1 -1
  283. package/src/oauth/chatgpt-shared.ts +83 -0
  284. package/src/oauth/chatgpt.ts +8 -72
  285. package/src/oauth/dsh.ts +203 -0
  286. package/src/oauth/generic-account-failover.ts +62 -12
  287. package/src/oauth/generic-session-affinity.ts +125 -0
  288. package/src/oauth/index.ts +188 -111
  289. package/src/oauth/kiro-device-login.ts +2 -1
  290. package/src/oauth/kiro.ts +7 -1
  291. package/src/oauth/local-token-detect.ts +21 -1
  292. package/src/oauth/login-cli-errors.ts +44 -0
  293. package/src/oauth/login-cli.ts +69 -7
  294. package/src/oauth/login-flow-state.ts +371 -23
  295. package/src/oauth/meta-muse-errors.ts +41 -0
  296. package/src/oauth/meta-muse-keychain.ts +52 -0
  297. package/src/oauth/meta-muse.ts +51 -84
  298. package/src/oauth/model-discovery-auth.ts +3 -0
  299. package/src/oauth/pool-settings-capability.ts +4 -2
  300. package/src/oauth/refresh-flight-retirement.ts +48 -0
  301. package/src/oauth/session-affinity-key.ts +23 -0
  302. package/src/oauth/store-anthropic-instance.ts +9 -2
  303. package/src/oauth/store.ts +12 -5
  304. package/src/protocols/codecs/chat-ir.ts +467 -0
  305. package/src/protocols/codecs/chat.ts +10 -1
  306. package/src/protocols/codecs/messages-ir.ts +500 -0
  307. package/src/protocols/encoders/adapter-events.ts +5 -4
  308. package/src/protocols/encoders/messages.ts +9 -6
  309. package/src/protocols/features.ts +10 -2
  310. package/src/protocols/path.ts +11 -1
  311. package/src/protocols/plan-snapshot.ts +39 -16
  312. package/src/protocols/plan.ts +9 -1
  313. package/src/protocols/provider-summary.ts +2 -1
  314. package/src/protocols/settings.ts +8 -0
  315. package/src/providers/antigravity-account-roster.ts +102 -0
  316. package/src/providers/antigravity-models.ts +85 -7
  317. package/src/providers/api-key-cooldowns.ts +49 -0
  318. package/src/providers/api-key-resolve.ts +11 -6
  319. package/src/providers/api-key-response-ownership.ts +57 -0
  320. package/src/providers/api-key-selection.ts +31 -6
  321. package/src/providers/api-keys.ts +214 -3
  322. package/src/providers/claude-cli-usage.ts +96 -0
  323. package/src/providers/dsh-account-destination.ts +31 -0
  324. package/src/providers/dsh-account-models.ts +41 -0
  325. package/src/providers/gcp-credential-cleanup.ts +59 -0
  326. package/src/providers/key-failover.ts +54 -50
  327. package/src/providers/key-store.ts +7 -2
  328. package/src/providers/model-rename-fields.ts +5 -0
  329. package/src/providers/model-rename-migration.ts +7 -5
  330. package/src/providers/openai-tiers-destination.ts +11 -2
  331. package/src/providers/openai-tiers.ts +5 -12
  332. package/src/providers/opencode-go-transport.ts +3 -1
  333. package/src/providers/quota/account-cache.ts +6 -5
  334. package/src/providers/quota/account-row.ts +34 -0
  335. package/src/providers/quota/antigravity.ts +28 -7
  336. package/src/providers/quota/dsh-account.ts +70 -0
  337. package/src/providers/quota/vendor-probes-oauth.ts +1 -93
  338. package/src/providers/quota-types.ts +2 -0
  339. package/src/providers/quota.ts +14 -20
  340. package/src/providers/registry/entries-core.ts +44 -14
  341. package/src/providers/registry/entries-extended.ts +19 -0
  342. package/src/providers/registry/types.ts +1 -1
  343. package/src/providers/request-pacing.ts +77 -19
  344. package/src/providers/routed-config.ts +339 -0
  345. package/src/providers/service-tier.ts +2 -1
  346. package/src/quota/window-mapping.ts +5 -2
  347. package/src/responses/anthropic-progress.ts +34 -0
  348. package/src/responses/assistant-replay.ts +35 -0
  349. package/src/responses/compaction-history-framing.ts +65 -0
  350. package/src/responses/compaction.ts +6 -1
  351. package/src/responses/incomplete-reason.ts +31 -0
  352. package/src/responses/parser.ts +78 -22
  353. package/src/responses/reasoning-envelope.ts +4 -0
  354. package/src/responses/reasoning-replay-cache.ts +24 -2
  355. package/src/responses/reasoning-retention.ts +173 -0
  356. package/src/responses/retained-compaction.ts +46 -0
  357. package/src/responses/schema.ts +23 -0
  358. package/src/responses/state.ts +32 -29
  359. package/src/router.ts +8 -321
  360. package/src/routing/health.ts +37 -22
  361. package/src/routing/history/build-file.ts +92 -0
  362. package/src/routing/history/indexer.ts +254 -221
  363. package/src/routing/history/ingest.ts +242 -0
  364. package/src/routing/history/rebuild-job.ts +111 -0
  365. package/src/routing/history/rebuild-protocol.ts +15 -0
  366. package/src/routing/history/rebuild-worker.ts +34 -0
  367. package/src/routing/profile.ts +1 -1
  368. package/src/server/admission-model-scope.ts +21 -1
  369. package/src/server/antigravity-direct-eligibility.ts +208 -0
  370. package/src/server/auth-cors.ts +15 -0
  371. package/src/server/chat-completions.ts +82 -10
  372. package/src/server/chat-native-reasoning.ts +123 -0
  373. package/src/server/chat-native-sse.ts +110 -7
  374. package/src/server/chat-native.ts +76 -17
  375. package/src/server/claude-messages.ts +164 -433
  376. package/src/server/effort-policy.ts +1 -1
  377. package/src/server/images.ts +15 -2
  378. package/src/server/index/desktop-compatibility-startup.ts +56 -0
  379. package/src/server/index/model-alias-id.ts +36 -0
  380. package/src/server/index/optional-listeners.ts +2 -0
  381. package/src/server/index/serve-options.ts +11 -3
  382. package/src/server/index/startup-warnings.ts +11 -0
  383. package/src/server/index/websocket-handler.ts +9 -0
  384. package/src/server/index.ts +14 -11
  385. package/src/server/inference/advisor-diagnostics.ts +31 -0
  386. package/src/server/inference/client-encoder-delivery.ts +6 -5
  387. package/src/server/login-shell-path.ts +146 -0
  388. package/src/server/management/advisor-routes.ts +202 -0
  389. package/src/server/management/agent-settings-routes.ts +22 -4
  390. package/src/server/management/claude-desktop-picker-routes.ts +1 -1
  391. package/src/server/management/combo-routes.ts +176 -50
  392. package/src/server/management/companion-routes.ts +4 -0
  393. package/src/server/management/config-routes.ts +77 -6
  394. package/src/server/management/context.ts +4 -0
  395. package/src/server/management/cursor-integration-routes.ts +1 -1
  396. package/src/server/management/desktop-compatibility-routes.ts +46 -0
  397. package/src/server/management/desktop-compatibility-runtime-routes.ts +46 -0
  398. package/src/server/management/desktop-compatibility-settings-routes.ts +27 -0
  399. package/src/server/management/model-routes.ts +13 -8
  400. package/src/server/management/model-rows.ts +14 -0
  401. package/src/server/management/native-integration-routes.ts +2 -7
  402. package/src/server/management/native-reasoning-retention-routes.ts +41 -0
  403. package/src/server/management/oauth-account-routes.ts +87 -14
  404. package/src/server/management/protocol-settings-patch.ts +1 -0
  405. package/src/server/management/provider-gcp-credential-routes.ts +29 -0
  406. package/src/server/management/provider-key-pacing-routes.ts +31 -0
  407. package/src/server/management/provider-overwrite-carry.ts +7 -0
  408. package/src/server/management/provider-routes.ts +25 -30
  409. package/src/server/management/route-registry.ts +21 -1
  410. package/src/server/management/sibling-guard.ts +1 -0
  411. package/src/server/management-api.ts +9 -1
  412. package/src/server/messages-antigravity-direct.ts +195 -0
  413. package/src/server/messages-native-tool-guard.ts +185 -0
  414. package/src/server/messages-native.ts +106 -22
  415. package/src/server/messages-shared.ts +431 -0
  416. package/src/server/relay.ts +1 -1
  417. package/src/server/request-log-terminal-status.ts +1 -1
  418. package/src/server/request-log.ts +13 -1
  419. package/src/server/responses/account-change-state.ts +8 -2
  420. package/src/server/responses/adapter-continuation.ts +48 -19
  421. package/src/server/responses/adapter-delivery.ts +43 -8
  422. package/src/server/responses/adapter-dispatch.ts +48 -4
  423. package/src/server/responses/advisor-plan-slot.ts +27 -0
  424. package/src/server/responses/advisor-retention.ts +117 -0
  425. package/src/server/responses/advisor-slot.ts +368 -0
  426. package/src/server/responses/code-mode-collab-surface.ts +116 -8
  427. package/src/server/responses/codex-ws-wire.ts +15 -8
  428. package/src/server/responses/compact.ts +303 -125
  429. package/src/server/responses/compaction-recovery.ts +18 -10
  430. package/src/server/responses/compaction-retention.ts +61 -0
  431. package/src/server/responses/context-overflow.ts +11 -6
  432. package/src/server/responses/core-auth.ts +6 -0
  433. package/src/server/responses/core-codex-account.ts +11 -1
  434. package/src/server/responses/core-combo-failure.ts +12 -1
  435. package/src/server/responses/core-combo.ts +141 -36
  436. package/src/server/responses/core-errors.ts +3 -3
  437. package/src/server/responses/core-lifetime.ts +7 -2
  438. package/src/server/responses/core-normalize.ts +11 -0
  439. package/src/server/responses/core-opaque-recovery.ts +35 -0
  440. package/src/server/responses/core-options.ts +51 -0
  441. package/src/server/responses/core-pipeline.ts +141 -0
  442. package/src/server/responses/core-replay.ts +25 -6
  443. package/src/server/responses/core.ts +3 -116
  444. package/src/server/responses/fetch-helpers.ts +73 -19
  445. package/src/server/responses/main-hard-lock-routing.ts +109 -0
  446. package/src/server/responses/native-response-control.ts +4 -2
  447. package/src/server/responses/passthrough-delivery.ts +19 -11
  448. package/src/server/responses/passthrough-dispatch.ts +29 -16
  449. package/src/server/responses/request-prepare.ts +144 -17
  450. package/src/server/responses/request-sidecar-auth.ts +2 -2
  451. package/src/server/responses/request-transport.ts +120 -27
  452. package/src/server/responses/response-effects.ts +1 -0
  453. package/src/server/responses/run-turn-execution.ts +166 -38
  454. package/src/server/responses/sidecar-execution.ts +98 -5
  455. package/src/server/responses/terminal-guard.ts +4 -17
  456. package/src/server/responses-undeclared-tool-guard.ts +30 -9
  457. package/src/server/startup-action-control.ts +16 -2
  458. package/src/server/startup-health-cache.ts +1 -0
  459. package/src/service/auth-preflight.ts +71 -0
  460. package/src/service/cli.ts +4 -2
  461. package/src/service/definition.ts +97 -0
  462. package/src/service/desktop-startup.ts +14 -0
  463. package/src/service/guards.ts +1 -61
  464. package/src/service/health-sentinel-linux.ts +112 -0
  465. package/src/service/health-sentinel-main.ts +99 -61
  466. package/src/service/health-sentinel-probe.ts +24 -7
  467. package/src/service/health.ts +5 -89
  468. package/src/service/launchd.ts +27 -7
  469. package/src/service/logging.ts +135 -0
  470. package/src/service/repair.ts +2 -1
  471. package/src/service/state.ts +7 -8
  472. package/src/service/systemd.ts +1 -1
  473. package/src/service/windows-guarded-manager.ts +2 -1
  474. package/src/service/windows-ops.ts +1 -1
  475. package/src/service/windows-scheduler.ts +2 -7
  476. package/src/service/windows-taskxml.ts +2 -2
  477. package/src/service/windows-wscript.ts +14 -0
  478. package/src/service.ts +4 -2
  479. package/src/sidecar/auth.ts +2 -0
  480. package/src/tray/windows-tray.ps1 +43 -24
  481. package/src/types/config.ts +124 -6
  482. package/src/types/opencode-go.ts +61 -0
  483. package/src/types/opencode-zen.ts +29 -0
  484. package/src/types/provider.ts +39 -6
  485. package/src/types/request.ts +38 -2
  486. package/src/types/tools.ts +2 -0
  487. package/src/types/wire.ts +29 -37
  488. package/src/types.ts +1 -0
  489. package/src/update/async-check.ts +1 -1
  490. package/src/update/badge.ts +2 -2
  491. package/src/update/index.ts +88 -15
  492. package/src/update/job-supervised-restart.ts +107 -0
  493. package/src/update/job.ts +32 -32
  494. package/src/update/notify.ts +1 -1
  495. package/src/update/npm-invocation.mjs +12 -2
  496. package/src/update/refresh-scheduler.ts +2 -2
  497. package/src/update/supervised-recovery.d.mts +27 -0
  498. package/src/update/supervised-recovery.mjs +157 -0
  499. package/src/usage/cache-diagnostic.ts +2 -0
  500. package/src/usage/cost.ts +48 -12
  501. package/src/usage/expected-prices.ts +15 -0
  502. package/src/usage/ledger-retention.ts +2 -1
  503. package/src/usage/ledger-scanner.ts +6 -5
  504. package/src/usage/log.ts +17 -8
  505. package/src/usage/summary.ts +22 -9
  506. package/src/usage/user-cost-overlays.ts +14 -19
  507. package/src/usage/user-cost-validation.ts +38 -0
  508. package/src/vision/eligibility.ts +13 -0
  509. package/src/web-search/gemini-executor.ts +2 -2
  510. package/src/web-search/loop.ts +55 -5
  511. package/src/web-search/progress-stream.ts +4 -1
  512. package/src/web-search/run-turn-loop.ts +9 -1
  513. package/assets/sponsors/packycode.png +0 -0
  514. package/gui/dist/assets/App-CSJvmjNs.css +0 -1
  515. package/gui/dist/assets/App-CZtqiGcw.js +0 -56
  516. package/gui/dist/assets/index-BB6uK6tU.css +0 -1
  517. package/gui/dist/assets/index-CTm4l-HQ.js +0 -94
  518. package/gui/dist/assets/quota-summary-BbEeRfPY.js +0 -1
@@ -1,5 +1,7 @@
1
+ import { canInferVendorContext } from "./vendor-context";
1
2
  import { effectiveProviderAlias, effectiveProviderAliasDecision } from "../../providers/default-aliases";
2
3
  import { initialModelSelectionPending } from "../../providers/initial-model-selection";
4
+ import { supportsConfiguredUltraFast } from "./configured-tiers";
3
5
  import { execFileSync } from "node:child_process";
4
6
  import { createHash, createHmac, randomBytes } from "node:crypto";
5
7
  import { copyFileSync, existsSync, mkdirSync, readFileSync, realpathSync } from "node:fs";
@@ -33,7 +35,6 @@ import {
33
35
  import type { OcxConfig, OcxProviderConfig } from "../../types";
34
36
  import { modelInList } from "../../types";
35
37
  import { CODEX_REASONING_LEVELS, codexEffortRank, configuredReasoningEfforts, modelRecordValue, sanitizeCodexReasoningEfforts } from "../../reasoning-effort";
36
- import { isModelVisionSidecarConsumer } from "../../vision/eligibility";
37
38
  import { getModelMetadata, getModelMetadataCaseInsensitive, listModelMetadata, resolveMetadataProvider, type ModelMetadata } from "../../generated/model-metadata";
38
39
  import { enrichProviderFromRegistry, shouldCaseFoldMetadataModelId } from "../../providers/derive";
39
40
  import {
@@ -101,7 +102,7 @@ import type {
101
102
  CatalogTrustedOpenAiApiPolicySnapshot,
102
103
  } from "../convergence-types";
103
104
  import type { CatalogGatherProviderAuthOutcome, CatalogGatherProviderModelOutcome, GatherFlightCapture, ModelsAuthResolverFactory } from "./gather-capture";
104
- import { applyProviderConfigHints, configuredAutoCompactTokenLimit, configuredMaxInputTokens, configuredReasoningSummarySupport, modelInputModalities, routedMaxOutputTokens } from "./model-hints";
105
+ import { applyProviderConfigHints, configuredAutoCompactTokenLimit, configuredMaxInputTokens, configuredReasoningSummarySupport, modelInputModalities, routedMaxOutputTokens, withVisionSidecarInput } from "./model-hints";
105
106
  import { resolveComboCatalogMember } from "./combo-member";
106
107
  import { captureGatherFlight, captureTrustedOpenAiApiPolicy, gatherFlightKey, keyedGatherBytesIdentity, withCanonicalOpenAiForwardAuthDefault } from "./gather-capture";
107
108
  import { fetchProviderModelsWithAuth, observedModelsAuthResolver, refreshingModelsAuthResolver } from "./provider-models";
@@ -318,6 +319,7 @@ function boundCustomNativeReasoning(
318
319
  return bounded;
319
320
  }
320
321
 
322
+ /** Gather discovered and custom rows under one captured provider policy, including speed capabilities. */
321
323
  async function gatherRoutedModelsUncached(
322
324
  config: OcxConfig,
323
325
  capture: GatherFlightCapture,
@@ -327,6 +329,7 @@ async function gatherRoutedModelsUncached(
327
329
  const localProviderAuthOutcomes = capture.providerAuthOutcomes;
328
330
  const resolveAuth = capture.authResolver;
329
331
  const ttlMs = config.modelCacheTtlMs ?? DEFAULT_MODEL_CACHE_TTL_MS;
332
+ const visionSidecarEnabled = config.visionSidecar?.enabled !== false;
330
333
  // Persisted provider entries can predate newer registry fields (noVisionModels,
331
334
  // modelInputModalities, ...). The ROUTER merges registry seeds at request time
332
335
  // (routedProviderConfig), so the proxy behaves correctly — the catalog listing must see the
@@ -334,6 +337,8 @@ async function gatherRoutedModelsUncached(
334
337
  // vision-sidecar model advertised text-only, blocking image attachments app-side).
335
338
  // Enrich a CLONE: hydrated defaults must never leak into the persisted config.
336
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]));
337
342
  const providerResults = await Promise.all(
338
343
  activeProviders.map(provider => fetchProviderModelsWithAuth(
339
344
  provider,
@@ -358,6 +363,7 @@ async function gatherRoutedModelsUncached(
358
363
  const policy = fastPolicyForModel(apiProvider.provider, model.id, apiProvider.name);
359
364
  const supported = serviceTierSupportFromPolicy(policy);
360
365
  if (supported !== undefined) model.supportsServiceTier = supported;
366
+ model.supportsUltraFastTier = supportsConfiguredUltraFast(policy);
361
367
  if (supported === true && policy.fastTierDescription !== undefined) model.fastTierDescription = policy.fastTierDescription;
362
368
  }
363
369
  }
@@ -374,7 +380,9 @@ async function gatherRoutedModelsUncached(
374
380
  config,
375
381
  metadataModelIdCaseFoldByProvider,
376
382
  metadataConfigDirByProvider,
383
+ false,
377
384
  )
385
+ .map(model => withVisionSidecarInput(model, enrichedByName.get(model.provider), visionSidecarEnabled))
378
386
  // Drop image/video generation models (e.g. Grok image/video) by default. Cursor's static catalog
379
387
  // intentionally mirrors Cursor's public model table, including Gemini image preview, so the
380
388
  // exposure decision goes through shouldExposeRoutedModel (single choke point).
@@ -467,7 +475,7 @@ async function gatherRoutedModelsUncached(
467
475
  ...inherited,
468
476
  catalogKind: CODEX_CUSTOM_MODEL_CATALOG_KIND,
469
477
  ...(typeof custom.contextWindow === "number" && custom.contextWindow > 0
470
- ? { contextWindow: custom.contextWindow }
478
+ ? { contextWindow: custom.contextWindow, contextWindowInference: undefined }
471
479
  : {}),
472
480
  ...(Array.isArray(custom.inputModalities)
473
481
  ? { inputModalities: [...custom.inputModalities] }
@@ -478,9 +486,6 @@ async function gatherRoutedModelsUncached(
478
486
  ...(custom.codexToolMode !== undefined ? { codexToolMode: custom.codexToolMode } : {}),
479
487
  });
480
488
  }
481
- // Enriched (registry-hydrated) provider clones — shared by combo member synthesis and
482
- // custom-model vision-sidecar inheritance so both see the same merged registry view.
483
- const enrichedByName = new Map(activeProviders.map(provider => [provider.name, provider.provider]));
484
489
  for (const id of listComboIds(config)) {
485
490
  const combo = getCombo(config, id);
486
491
  if (!combo) continue;
@@ -517,6 +522,7 @@ async function gatherRoutedModelsUncached(
517
522
  nativeAliasFallback,
518
523
  metadataModelIdCaseFoldByProvider.get(target.provider),
519
524
  metadataConfigDirByProvider.get(target.provider),
525
+ visionSidecarEnabled,
520
526
  ))
521
527
  .filter((member): member is CatalogModel => member !== undefined);
522
528
  const derived = deriveComboCatalogModel(id, combo, members);
@@ -654,6 +660,7 @@ async function gatherRoutedModelsUncached(
654
660
  ...(Array.isArray(cm.reasoningEfforts) ? { reasoningEfforts: [...cm.reasoningEfforts] } : {}),
655
661
  ...(cm.defaultReasoningEffort ? { defaultReasoningEffort: cm.defaultReasoningEffort } : {}),
656
662
  ...(typeof supportsServiceTier === "boolean" ? { supportsServiceTier } : {}),
663
+ supportsUltraFastTier: supportsConfiguredUltraFast(fastPolicy),
657
664
  ...(supportsServiceTier === true && fastPolicy?.fastTierDescription !== undefined
658
665
  ? { fastTierDescription: fastPolicy.fastTierDescription }
659
666
  : {}),
@@ -686,7 +693,8 @@ async function gatherRoutedModelsUncached(
686
693
  : undefined;
687
694
  const merged: CatalogModel = replaced ? {
688
695
  ...base,
689
- ...(base.contextWindow === undefined && replaced.contextWindow !== undefined ? { contextWindow: replaced.contextWindow } : {}),
696
+ ...(base.contextWindow === undefined && replaced.contextWindow !== undefined
697
+ ? { contextWindow: replaced.contextWindow, ...(replaced.contextWindowInference ? { contextWindowInference: replaced.contextWindowInference, contextCap: replaced.contextCap, contextCapped: replaced.contextCapped } : {}) } : {}),
690
698
  ...(mergedMaxInput !== undefined ? { maxInputTokens: mergedMaxInput } : {}),
691
699
  ...(mergedMaxOutput !== undefined ? { maxOutputTokens: mergedMaxOutput } : {}),
692
700
  ...(base.autoCompactTokenLimit === undefined && replaced.autoCompactTokenLimit !== undefined
@@ -703,17 +711,29 @@ async function gatherRoutedModelsUncached(
703
711
  ...(base.codexToolMode === undefined && replaced.codexToolMode !== undefined ? { codexToolMode: replaced.codexToolMode } : {}),
704
712
  ...(base.capabilities === undefined && replaced.capabilities !== undefined ? { capabilities: replaced.capabilities } : {}),
705
713
  } : base;
714
+ // A custom-only model may never appear in /models or the provider's configured roster.
715
+ // Fill only its absent context; a full hint overlay would overwrite custom capabilities.
716
+ const customContextHints = merged.contextWindow === undefined && effectiveProvider && canInferVendorContext(cm.provider, effectiveProvider)
717
+ ? applyProviderConfigHints(cm.provider, effectiveProvider, { provider: cm.provider, id: cm.modelId },
718
+ providerContextCap(config, cm.provider), metadataModelIdCaseFoldByProvider.get(cm.provider),
719
+ undefined, metadataConfigDirByProvider.get(cm.provider), false)
720
+ : undefined;
721
+ const contextResolved: CatalogModel = customContextHints?.contextWindow !== undefined
722
+ ? { ...merged, contextWindow: customContextHints.contextWindow,
723
+ ...(customContextHints.contextWindowInference ? { contextWindowInference: customContextHints.contextWindowInference } : {}),
724
+ ...(customContextHints.contextCap !== undefined ? { contextCap: customContextHints.contextCap, contextCapped: customContextHints.contextCapped } : {}) }
725
+ : merged;
706
726
  // Catalog-advertised efforts are bounded whenever the model id is a pinned native
707
727
  // slug. Desktop validates that id, so a gateway such as YYLJ/gpt-6-astra still cannot
708
728
  // advertise none/minimal. Full native identity stays behind the alias predicate.
709
729
  const nativeEffortSource = hasNativeOpenAiCapabilityMetadata(cm.modelId);
710
730
  const reasoningBounded = nativeEffortSource
711
731
  ? boundCustomNativeReasoning(
712
- merged,
732
+ contextResolved,
713
733
  nativeReasoningEfforts(cm.modelId),
714
734
  nativeAliasDefaultEffort ?? nativeDefaultReasoningEffort(cm.modelId),
715
735
  )
716
- : merged;
736
+ : contextResolved;
717
737
  // Vision-sidecar coverage only: when the enriched provider's shared predicate matches
718
738
  // noVisionModels or text-without-image modelInputModalities, advertise image input so the
719
739
  // Codex app lets images reach the sidecar (#349/#344). Deliberately NOT the full
@@ -744,13 +764,7 @@ async function gatherRoutedModelsUncached(
744
764
  : mergedWithHardBounds;
745
765
  const enrichedProvider = enrichedByName.get(cm.provider) ?? rawProvider;
746
766
  // Reuse the request-time consumer predicate so custom rows cannot drift from catalog hints.
747
- if (enrichedProvider && isModelVisionSidecarConsumer(enrichedProvider, mergedWithAutoCompact.id)) {
748
- const current = mergedWithAutoCompact.inputModalities ?? ["text"];
749
- if (!current.includes("image")) {
750
- return { ...mergedWithAutoCompact, inputModalities: [...current, "image"] };
751
- }
752
- }
753
- return mergedWithAutoCompact;
767
+ return withVisionSidecarInput(mergedWithAutoCompact, enrichedProvider, visionSidecarEnabled);
754
768
  });
755
769
  // Custom rows override discovered rows that encode to the same Codex-facing slug. A provider whose
756
770
  // captured discovery declares an order (preferFirst) keeps each replacement in the discovered row's
@@ -930,6 +944,7 @@ export function augmentRoutedModelsWithMetadata(
930
944
  caps?: Pick<OcxConfig, "providerContextCaps">,
931
945
  metadataModelIdCaseFoldByProvider?: ReadonlyMap<string, boolean>,
932
946
  metadataConfigDirByProvider?: ReadonlyMap<string, string>,
947
+ includeVisionSidecar = true,
933
948
  ): CatalogModel[] {
934
949
  const out = [...models];
935
950
  const indexByKey = new Map(out.map((model, index) => [`${model.provider}/${model.id}`, index]));
@@ -959,6 +974,7 @@ export function augmentRoutedModelsWithMetadata(
959
974
  metadataModelIdCaseFoldByProvider?.get(provider),
960
975
  undefined,
961
976
  metadataConfigDirByProvider?.get(provider),
977
+ includeVisionSidecar,
962
978
  )
963
979
  : seeded;
964
980
  continue;
@@ -983,6 +999,7 @@ export function augmentRoutedModelsWithMetadata(
983
999
  metadataModelIdCaseFoldByProvider?.get(provider),
984
1000
  undefined,
985
1001
  metadataConfigDirByProvider?.get(provider),
1002
+ includeVisionSidecar,
986
1003
  )
987
1004
  : {}),
988
1005
  });
@@ -0,0 +1,82 @@
1
+ import { listModelMetadata, resolveMetadataProvider, type ModelMetadata } from "../../generated/model-metadata";
2
+ import { PROVIDER_REGISTRY } from "../../providers/registry";
3
+ import type { OcxProviderConfig } from "../../types";
4
+
5
+ /** A bundled model-window estimate, not evidence of the serving gateway's capacity. */
6
+ export interface VendorContextInference {
7
+ source: "vendor" | "aggregator";
8
+ provider: string;
9
+ modelId: string;
10
+ contextWindow: number;
11
+ }
12
+
13
+ // Capacity and price have different authorities: hosting vendors (Cerebras/Bedrock), Azure
14
+ // and the Codex subscription table must not replace the original vendor's API window.
15
+ const VENDORS = ["anthropic", "openai", "google", "moonshot", "minimax", "deepseek", "xai", "zai", "mistral", "xiaomi"];
16
+ const AGGREGATORS = ["openrouter", "opencode-go"];
17
+ // Snapshot names once, never consult the mutable registry after gather flight admission.
18
+ const BUILTIN_NAMES = new Set(PROVIDER_REGISTRY.map(entry => entry.id.toLowerCase()));
19
+ /**
20
+ * Opt-in only (#1073): an id-only custom row keeps the conservative fallback unless the
21
+ * operator sets `inferContextWindow: true`. Built-in names stay excluded even when set.
22
+ */
23
+ export function canInferVendorContext(provider: string, config: Pick<OcxProviderConfig, "inferContextWindow"> | undefined): boolean {
24
+ return config?.inferContextWindow === true
25
+ && !BUILTIN_NAMES.has(provider.toLowerCase()) && !resolveMetadataProvider(provider);
26
+ }
27
+
28
+ const normalize = (id: string): string => id.toLowerCase().replaceAll(".", "-");
29
+ type Candidate = VendorContextInference | null;
30
+
31
+ /** Build a pure lookup; conflicting windows within one authority tier fail closed. */
32
+ export function createVendorContextLookup(
33
+ vendors: readonly ModelMetadata[],
34
+ aggregators: readonly ModelMetadata[],
35
+ ): (id: string) => VendorContextInference | undefined {
36
+ const build = (rows: readonly ModelMetadata[], source: VendorContextInference["source"]) => {
37
+ const exact = new Map<string, Candidate>();
38
+ const normalized = new Map<string, Candidate>();
39
+ const add = (map: Map<string, Candidate>, id: string, value: VendorContextInference) => {
40
+ if (!map.has(id)) map.set(id, value);
41
+ else if (map.get(id)?.contextWindow !== value.contextWindow) map.set(id, null);
42
+ };
43
+ for (const row of rows) {
44
+ const window = row.contextWindow;
45
+ if (!Number.isSafeInteger(window) || window! <= 0 || row.id.startsWith("~")) continue;
46
+ const value: VendorContextInference = { source, provider: row.provider, modelId: row.id, contextWindow: window! };
47
+ const ids = source === "aggregator" ? [row.id, row.id.slice(row.id.lastIndexOf("/") + 1)] : [row.id];
48
+ for (const id of ids) {
49
+ if (!id) continue;
50
+ add(exact, id, value);
51
+ add(normalized, normalize(id), value);
52
+ }
53
+ }
54
+ return { exact, normalized };
55
+ };
56
+ const tiers = [build(vendors, "vendor"), build(aggregators, "aggregator")];
57
+ return rawId => {
58
+ const id = rawId.trim();
59
+ if (!id || id.startsWith("~")) return undefined;
60
+ for (const tier of tiers) {
61
+ // An exact spelling wins over its normalized alias. A conflicting key does not
62
+ // fall through to a lower-authority source and accidentally regain a larger window.
63
+ const map = tier.exact.has(id) ? tier.exact : tier.normalized;
64
+ const key = map === tier.exact ? id : normalize(id);
65
+ if (map.has(key)) {
66
+ const value = map.get(key);
67
+ return value ? { ...value } : undefined;
68
+ }
69
+ }
70
+ return undefined;
71
+ };
72
+ }
73
+
74
+ let lookup: ReturnType<typeof createVendorContextLookup> | undefined;
75
+ export function vendorContextInference(modelId: string): VendorContextInference | undefined {
76
+ lookup ??= createVendorContextLookup(VENDORS.flatMap(listModelMetadata), AGGREGATORS.flatMap(listModelMetadata));
77
+ return lookup(modelId);
78
+ }
79
+
80
+ export function vendorContextWindow(modelId: string): number | undefined {
81
+ return vendorContextInference(modelId)?.contextWindow;
82
+ }
@@ -18,6 +18,9 @@ import { isSpawnableCodexCandidate } from "./exec-invocation";
18
18
  import {
19
19
  codexRuntimeStatePath,
20
20
  parsePersistedCodexRuntime,
21
+ resolveCodexRuntime,
22
+ type ResolveCodexRuntimeDeps,
23
+ type ResolveCodexRuntimeResult,
21
24
  } from "./runtime";
22
25
  import {
23
26
  inspectCodexShimBackingForCommand,
@@ -46,12 +49,13 @@ export type CodexCliInstallReason =
46
49
  | "shim_state_unknown"
47
50
  | "shim_update_deferred"
48
51
  | "app_bundle"
49
- | "version_manager_owned"
50
- | "npm_global_unverified"
51
- | "selection_unattested"
52
- | "version_mismatch"
53
- | "unverified_standalone"
54
- | "inspection_failed";
52
+ | "version_manager_owned"
53
+ | "npm_global_unverified"
54
+ | "managed_npm_global"
55
+ | "selection_unattested"
56
+ | "version_mismatch"
57
+ | "unverified_standalone"
58
+ | "inspection_failed";
55
59
 
56
60
  export type CodexCliCandidateSource = "environment" | "persisted";
57
61
  export type CodexCliInstallEvidence =
@@ -82,6 +86,12 @@ export interface CodexCliInstallReport {
82
86
  readonly managed: boolean;
83
87
  readonly reason: CodexCliInstallReason;
84
88
  readonly location: string | null;
89
+ /**
90
+ * sha256 digest of the canonical package manifest root when one was observed,
91
+ * otherwise null. Digest-only so the public report still carries no path; plan
92
+ * identity binds this value so two different install roots cannot share a plan.
93
+ */
94
+ readonly installDigest: string | null;
85
95
  readonly packageVersion: string | null;
86
96
  readonly shim: Readonly<{
87
97
  status: "not-tracked" | "matched" | "unknown";
@@ -105,6 +115,19 @@ export interface CodexCliInstallProvenanceDeps {
105
115
  platform: NodeJS.Platform,
106
116
  configDir: string,
107
117
  ) => CodexShimBackingForCommand;
118
+ /**
119
+ * Caller assertion that `env.CODEX_CLI_PATH` is the runtime the launcher actually
120
+ * selected, not merely a configured name. Only a proof-bound caller may set this;
121
+ * a persisted selection and an arbitrary environment stay unattested.
122
+ */
123
+ readonly selectionAttested?: boolean;
124
+ /**
125
+ * The command the runtime resolver actually selected under the same evidence.
126
+ * Attestation additionally requires its canonical path to match the inspected
127
+ * candidate, so a snapshot candidate the resolver would have rejected cannot
128
+ * turn a different installation managed.
129
+ */
130
+ readonly selectedCommand?: string | null;
108
131
  }
109
132
 
110
133
  interface PackageManifestEvidence {
@@ -159,6 +182,7 @@ function unknownReport(
159
182
  managed: false,
160
183
  reason,
161
184
  location: extra.location ?? null,
185
+ installDigest: null,
162
186
  packageVersion: null,
163
187
  shim: Object.freeze({ status: "not-tracked", backingKind: null }),
164
188
  evidence: Object.freeze([]),
@@ -287,6 +311,38 @@ function resolveCandidateCommandPath(
287
311
  return null;
288
312
  }
289
313
 
314
+ /** Resolve selection under inspection's bounded, local-only filesystem policy. */
315
+ export function observeCodexCliSelectedCommand(
316
+ deps: CodexCliInstallProvenanceDeps,
317
+ resolve: (deps: ResolveCodexRuntimeDeps) => ResolveCodexRuntimeResult = resolveCodexRuntime,
318
+ ): string | null {
319
+ const platform = deps.platform ?? process.platform;
320
+ // Even the general resolver's existence checks can touch a remote Windows
321
+ // candidate. Deferred inspection must return before invoking it at all.
322
+ if (platform === "win32") return null;
323
+ const configDir = deps.configDir ?? getConfigDir();
324
+ if (!isSafeLocalInspectionPath(configDir, deps)) return null;
325
+ try {
326
+ return resolve({
327
+ platform,
328
+ env: deps.env,
329
+ configDir,
330
+ discoverAlternatives: false,
331
+ probeVersion: false,
332
+ // Runtime and shim state must use the same no-follow, nonblocking,
333
+ // size-bounded descriptor reads as provenance, including before selection.
334
+ readFileSync: path => {
335
+ const bytes = readBoundedFile(path, MAX_RUNTIME_STATE_BYTES, deps);
336
+ if (!bytes) throw new Error("inspection_state_unavailable");
337
+ return bytes.toString("utf8");
338
+ },
339
+ existsSync: path => resolveCandidateCommandPath(path, deps) !== null,
340
+ }).runtime.command ?? null;
341
+ } catch {
342
+ return null;
343
+ }
344
+ }
345
+
290
346
  function canonicalize(path: string, deps: CodexCliInstallProvenanceDeps): string | null {
291
347
  if (!isSafeLocalInspectionPath(path, deps)) return null;
292
348
  try {
@@ -630,15 +686,16 @@ export async function inspectCodexCliInstall(
630
686
  managed: false,
631
687
  reason: appBundle ? "app_bundle" : "version_manager_owned",
632
688
  location: publicExecutableLocation(lexicalCandidatePath, platform),
689
+ installDigest: null,
633
690
  packageVersion: null,
634
691
  shim: Object.freeze({ status: "unknown", backingKind: null }),
635
692
  evidence: Object.freeze([appBundle ? "app_bundle_path" : "version_manager_path"]),
636
693
  });
637
694
  }
638
- return unknownWindowsReport("windows_inspection_deferred", candidate, {
639
- location: publicExecutableLocation(lexicalCandidatePath, platform),
640
- });
641
- }
695
+ return unknownWindowsReport("windows_inspection_deferred", candidate, {
696
+ location: publicExecutableLocation(lexicalCandidatePath, platform),
697
+ });
698
+ }
642
699
  const configDir = deps.configDir ?? getConfigDir();
643
700
  if (!isSafeLocalInspectionPath(configDir, deps)) {
644
701
  return unknownReport("shim_state_unknown", candidate);
@@ -651,6 +708,23 @@ export async function inspectCodexCliInstall(
651
708
  if (!canonicalCandidatePath) {
652
709
  return unknownReport("candidate_path_unsafe", candidate);
653
710
  }
711
+ // Only a proof-bound caller may attest that the environment candidate is the
712
+ // runtime it actually selected, and only when the runtime resolver picked the
713
+ // same canonical path under the same evidence. A persisted selection records
714
+ // intent, not the resolved binary, and an arbitrary environment is ambient —
715
+ // neither can become managed here; a resolver that would choose a different
716
+ // executable cannot attest the snapshot's installation either.
717
+ const resolverPicked = deps.selectedCommand
718
+ ? canonicalize(
719
+ resolveCandidateCommandPath(deps.selectedCommand, deps) ?? deps.selectedCommand,
720
+ deps,
721
+ )
722
+ : null;
723
+ const selectionAttested =
724
+ deps.selectionAttested === true
725
+ && candidate.evidence === "environment"
726
+ && resolverPicked !== null
727
+ && samePath(resolverPicked, canonicalCandidatePath, platform);
654
728
  const inspectShim = deps.inspectShim ?? inspectCodexShimBackingForCommand;
655
729
  const shim = inspectShim(candidatePath, platform, configDir);
656
730
  if (shim.status === "unknown") {
@@ -658,6 +732,7 @@ export async function inspectCodexCliInstall(
658
732
  ...unknownReport("shim_state_unknown", candidate, {
659
733
  location: publicExecutableLocation(canonicalCandidatePath, platform),
660
734
  }),
735
+ selectionAttested,
661
736
  shim: shimReport(shim),
662
737
  });
663
738
  }
@@ -666,6 +741,7 @@ export async function inspectCodexCliInstall(
666
741
  ...unknownReport("shim_update_deferred", candidate, {
667
742
  location: publicExecutableLocation(canonicalCandidatePath, platform),
668
743
  }),
744
+ selectionAttested,
669
745
  provenance: "standalone-unverified",
670
746
  shim: shimReport(shim),
671
747
  evidence: Object.freeze(["canonical_path", "shim_backing"] as const),
@@ -685,7 +761,7 @@ export async function inspectCodexCliInstall(
685
761
  candidateAvailable: true,
686
762
  candidateVersion: candidate.version,
687
763
  candidateSource: candidate.evidence,
688
- selectionAttested: false,
764
+ selectionAttested,
689
765
  versionEvidence: Object.freeze({
690
766
  kind: candidate.version ? "advisory-runtime" as const : "unavailable" as const,
691
767
  }),
@@ -693,6 +769,7 @@ export async function inspectCodexCliInstall(
693
769
  managed: false,
694
770
  reason: "app_bundle",
695
771
  location,
772
+ installDigest: null,
696
773
  packageVersion: null,
697
774
  shim: shimReport(shim),
698
775
  evidence: Object.freeze(["canonical_path", "app_bundle_path"]),
@@ -706,7 +783,7 @@ export async function inspectCodexCliInstall(
706
783
  candidateAvailable: true,
707
784
  candidateVersion: candidate.version,
708
785
  candidateSource: candidate.evidence,
709
- selectionAttested: false,
786
+ selectionAttested,
710
787
  versionEvidence: Object.freeze({
711
788
  kind: candidate.version ? "advisory-runtime" as const : "unavailable" as const,
712
789
  }),
@@ -714,6 +791,7 @@ export async function inspectCodexCliInstall(
714
791
  managed: false,
715
792
  reason: "version_manager_owned",
716
793
  location,
794
+ installDigest: null,
717
795
  packageVersion: null,
718
796
  shim: shimReport(shim),
719
797
  evidence: Object.freeze(["canonical_path", "version_manager_path"]),
@@ -722,6 +800,7 @@ export async function inspectCodexCliInstall(
722
800
  if (/codex[.]opencodex-real(?:[.](?:cmd|bat|exe))?$/i.test(pathTools(platform).basename(candidatePath))) {
723
801
  return freezeReport({
724
802
  ...unknownReport("shim_state_unknown", candidate, { location }),
803
+ selectionAttested,
725
804
  shim: shimReport(shim),
726
805
  });
727
806
  }
@@ -745,22 +824,30 @@ export async function inspectCodexCliInstall(
745
824
  const versionMatches = candidate.version === null || candidate.version === manifest.version;
746
825
  const reason: CodexCliInstallReason = !global || !linked
747
826
  ? "npm_global_unverified"
748
- : !versionMatches ? "version_mismatch" : "selection_unattested";
827
+ : !versionMatches ? "version_mismatch"
828
+ : selectionAttested ? "managed_npm_global" : "selection_unattested";
749
829
  return freezeReport({
750
830
  schemaVersion: 1,
751
831
  candidateAvailable: true,
752
832
  candidateVersion: candidate.version,
753
833
  candidateSource: candidate.evidence,
754
- selectionAttested: false,
834
+ selectionAttested,
755
835
  versionEvidence: Object.freeze({
756
- kind: manifestOwned && candidate.version !== null && versionMatches
836
+ // The manifest is the on-disk record of what is installed. An absent advisory
837
+ // version (the launcher snapshot carries no version) cannot contradict it,
838
+ // while a reported one that disagrees already classified as version_mismatch.
839
+ kind: manifestOwned && versionMatches
757
840
  ? "package-manifest" as const
758
841
  : candidate.version !== null ? "advisory-runtime" as const : "unavailable" as const,
759
842
  }),
760
843
  provenance: manifestOwned ? "npm-global" : "standalone-unverified",
761
- managed: false,
844
+ managed: manifestOwned && versionMatches && selectionAttested,
762
845
  reason,
763
846
  location,
847
+ // Plan identity binds this digest, not the redacted display location: two
848
+ // package roots that resolve to the same "<path>/codex" string must still
849
+ // produce different plans.
850
+ installDigest: sha256("codex-cli-install-root-v1", normalizePath(manifest.root, platform)),
764
851
  packageVersion: manifest.version,
765
852
  shim: shimReport(shim),
766
853
  evidence: Object.freeze<CodexCliInstallEvidence[]>([
@@ -786,7 +873,7 @@ export async function inspectCodexCliInstall(
786
873
  candidateAvailable: true,
787
874
  candidateVersion: candidate.version,
788
875
  candidateSource: candidate.evidence,
789
- selectionAttested: false,
876
+ selectionAttested,
790
877
  versionEvidence: Object.freeze({
791
878
  kind: candidate.version ? "advisory-runtime" as const : "unavailable" as const,
792
879
  }),
@@ -794,6 +881,7 @@ export async function inspectCodexCliInstall(
794
881
  managed: false,
795
882
  reason: "unverified_standalone",
796
883
  location,
884
+ installDigest: null,
797
885
  packageVersion: null,
798
886
  shim: shimReport(shim),
799
887
  evidence: Object.freeze(["canonical_path"]),