@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,4 +1,5 @@
1
1
  import * as z from "zod/v4";
2
+ import { isCompactionItemType } from "./compaction";
2
3
 
3
4
  const inputTextSchema = z.object({ type: z.literal("input_text"), text: z.string() });
4
5
  const plainTextSchema = z.object({ type: z.literal("text"), text: z.string() });
@@ -75,6 +76,28 @@ const reasoningItemSchema = z.object({
75
76
  // Round-tripped opaque payload (native OpenAI encryption OR the proxy's ocxr1 envelope).
76
77
  encrypted_content: z.string().optional(),
77
78
  });
79
+
80
+ /** A native v1 replacement history may establish replay provenance only after body success. */
81
+ export const nativeCompactResponseSchema = z.object({
82
+ status: z.literal("completed").optional(),
83
+ error: z.null().optional(),
84
+ output: z.array(z.union([
85
+ userMessageItemSchema.required({ content: true }),
86
+ systemMessageItemSchema.required({ content: true }),
87
+ assistantMessageItemSchema.required({ content: true }),
88
+ reasoningItemSchema,
89
+ z.object({
90
+ type: z.string().refine(isCompactionItemType),
91
+ encrypted_content: z.string().refine(value => value.trim().length > 0),
92
+ }),
93
+ ])).min(1).refine(items => items.some(item => {
94
+ if (isCompactionItemType(item.type)) return true;
95
+ if (!("role" in item)) return false;
96
+ return typeof item.content === "string" ? item.content.trim().length > 0
97
+ : item.content.some(part => "text" in part && part.text.trim().length > 0);
98
+ })),
99
+ });
100
+
78
101
  const functionCallItemSchema = z.object({
79
102
  type: z.literal("function_call"),
80
103
  id: z.string().optional(),
@@ -710,11 +710,6 @@ export function sweepOrphanedResponseSpills(): number {
710
710
  type SnapshotWriteOutcome = "stable" | "unstable" | "failed";
711
711
 
712
712
  async function writeBoundedSnapshot(path: string, attemptLimit: number): Promise<SnapshotWriteOutcome> {
713
- // Serialize writers so concurrent flush + debounce cannot race on temps / ACL (#612).
714
- const previous = persistGate;
715
- let release!: () => void;
716
- persistGate = new Promise<void>(resolve => { release = resolve; });
717
- await previous;
718
713
  try {
719
714
  for (let attempt = 0; attempt < attemptLimit; attempt += 1) {
720
715
  const revision = stateRevision;
@@ -751,13 +746,19 @@ async function writeBoundedSnapshot(path: string, attemptLimit: number): Promise
751
746
  lastSnapshotTarget = resolveWriteTarget(path);
752
747
  }
753
748
  persistAttemptHookForTests?.();
754
- if (revision === stateRevision) return "stable";
749
+ if (revision === stateRevision) {
750
+ // Cancel only the timer whose mutation this stable revision includes,
751
+ // before an awaiting caller's microtask can record a newer revision.
752
+ if (persistTimer) clearTimeout(persistTimer);
753
+ persistTimer = null;
754
+ pendingPersistPath = null;
755
+ drainPendingSpillUnlinks();
756
+ return "stable";
757
+ }
755
758
  }
756
759
  return "unstable";
757
760
  } catch {
758
761
  return "failed";
759
- } finally {
760
- release();
761
762
  }
762
763
  }
763
764
 
@@ -792,21 +793,30 @@ function schedulePersistAt(path: string, replace = false): void {
792
793
  }
793
794
 
794
795
  async function persistNow(path: string, awaitFollowUp = false): Promise<void> {
795
- if (persistTimer) {
796
- clearTimeout(persistTimer);
797
- persistTimer = null;
798
- }
799
- pendingPersistPath = null;
800
- const attemptLimit = awaitFollowUp ? MAX_SNAPSHOT_REWRITE_ATTEMPTS : 1;
801
- let outcome = await writeBoundedSnapshot(path, attemptLimit);
802
- if (outcome === "unstable" && awaitFollowUp) {
803
- if (persistTimer) clearTimeout(persistTimer);
804
- persistTimer = null;
796
+ // Include follow-up scheduling in the gate: releasing after only the disk write
797
+ // lets shutdown observe an empty timer before the background pass re-arms it.
798
+ const previous = persistGate;
799
+ let release!: () => void;
800
+ persistGate = new Promise<void>(resolve => { release = resolve; });
801
+ await previous;
802
+ try {
803
+ if (persistTimer) {
804
+ clearTimeout(persistTimer);
805
+ persistTimer = null;
806
+ }
805
807
  pendingPersistPath = null;
806
- outcome = await writeBoundedSnapshot(path, attemptLimit);
808
+ const attemptLimit = awaitFollowUp ? MAX_SNAPSHOT_REWRITE_ATTEMPTS : 1;
809
+ let outcome = await writeBoundedSnapshot(path, attemptLimit);
810
+ if (outcome === "unstable" && awaitFollowUp) {
811
+ if (persistTimer) clearTimeout(persistTimer);
812
+ persistTimer = null;
813
+ pendingPersistPath = null;
814
+ outcome = await writeBoundedSnapshot(path, attemptLimit);
815
+ }
816
+ if (outcome === "unstable" && !awaitFollowUp) schedulePersistAt(path, true);
817
+ } finally {
818
+ release();
807
819
  }
808
- if (outcome === "stable") drainPendingSpillUnlinks();
809
- else if (outcome === "unstable" && !awaitFollowUp) schedulePersistAt(path, true);
810
820
  }
811
821
 
812
822
  function schedulePersist(): void {
@@ -816,15 +826,8 @@ function schedulePersist(): void {
816
826
  }
817
827
 
818
828
  async function flushResponseSnapshot(): Promise<void> {
819
- if (persistTimer) {
820
- await persistNow(pendingPersistPath ?? snapshotPath(), true);
821
- return;
822
- }
823
- // No pending timer: still await any in-flight write so shutdown does not race (#612).
829
+ // The gate includes the background pass's timer decision, not just its write.
824
830
  await persistGate;
825
- // A bounded background pass may have scheduled its same-path follow-up while
826
- // this flush was waiting on the single-flight gate. Shutdown owns one awaited
827
- // bounded follow-up rather than returning behind that unref'd timer.
828
831
  if (persistTimer) await persistNow(pendingPersistPath ?? snapshotPath(), true);
829
832
  }
830
833
 
package/src/router.ts CHANGED
@@ -14,25 +14,16 @@ import {
14
14
  import type { NormalizedComboConfig } from "./combos/types";
15
15
  import { hasOwnProvider } from "./config/provider-name";
16
16
  import { anthropicInstanceRowShapeMatches } from "./providers/anthropic-instance-id";
17
- import { providerUsesKeyAuthOverride, resolveProviderApiKey } from "./providers/key-store";
18
- import { captureProviderApiKeySelection } from "./providers/api-key-selection-capture";
17
+ import { reconcileRouterWarningMemos, routedProviderConfig } from "./providers/routed-config";
19
18
  import { assertProviderDestinationAllowed } from "./lib/destination-policy";
20
- import { redactSecretString, redactUrlForLog } from "./lib/redact";
21
19
  import {
22
20
  PROVIDER_REGISTRY,
23
21
  providerCodexAccountMode,
24
- registryEntryForProviderDestination,
25
22
  } from "./providers/registry";
26
23
  // Imported from the module directly rather than through the registry facade, which is at its
27
24
  // file-size cap.
28
25
  import { registryModelIdKeys } from "./providers/registry/model-ids";
29
- import { applyDirectReasoningEffortContracts, hasLegacyClinePassReasoningEfforts } from "./providers/derive";
30
- import { cloneFastWire } from "./providers/fastwire";
31
- import { fastSwitchOff } from "./providers/fast-opt-in";
32
- import {
33
- providerMatchesRegistryTransportWithStaticGuards,
34
- providerSupportsLiveModelDiscovery,
35
- } from "./providers/static-model-discovery";
26
+ import { providerMatchesRegistryTransportWithStaticGuards } from "./providers/static-model-discovery";
36
27
  import {
37
28
  isCanonicalOpenAiForwardProvider,
38
29
  LEGACY_CHATGPT_PROVIDER_ID,
@@ -202,82 +193,6 @@ export function knownModelIdsForProvider(
202
193
  return [...ids];
203
194
  }
204
195
 
205
- /** Same endpoint modulo surrounding space and trailing slashes — matches `matchBaseUrlChoice`. */
206
- function isSameEndpoint(a: string, b: string): boolean {
207
- return a.trim().replace(/\/+$/, "") === b.trim().replace(/\/+$/, "");
208
- }
209
-
210
- /**
211
- * Origin of a user-configured URL, with the path withheld.
212
- *
213
- * A configured `baseUrl` is user-controlled and its path may itself be the credential — an
214
- * account-scoped route token such as `https://proxy.example/v1/8fK2mP7qR4nV6x` is opaque and
215
- * high-entropy, so it matches none of the prefix patterns in `redactSecretString`. Pattern
216
- * redaction cannot be trusted for this value, so no path segment is logged at all. `URL.origin`
217
- * also excludes userinfo, query and fragment.
218
- *
219
- * `…/…` marks that a path was present without revealing it, so a reader can tell an origin-only
220
- * config apart from one whose path was dropped.
221
- */
222
- function configuredOriginForLog(url: string): string {
223
- try {
224
- const parsed = new URL(url.trim());
225
- // "null" is what URL.origin yields for non-special schemes; treat it as unusable.
226
- if (!parsed.origin || parsed.origin === "null") return "(unloggable URL)";
227
- const hasPath = parsed.pathname !== "" && parsed.pathname !== "/";
228
- return hasPath ? `${parsed.origin}/…` : parsed.origin;
229
- } catch {
230
- return "(unparseable URL)";
231
- }
232
- }
233
-
234
- // `routedProviderConfig` runs per request, so warn once per (provider, discarded, effective) triple.
235
- // Keyed by the URLs too: editing config.json to a different wrong value warns again.
236
- const discardedBaseUrlWarnings = new Set<string>();
237
- let lastWarningReconciledGeneration = 0;
238
-
239
- export function reconcileRouterWarningMemos(generation: number): number {
240
- if (generation <= lastWarningReconciledGeneration) return 0;
241
- const removed = discardedBaseUrlWarnings.size;
242
- discardedBaseUrlWarnings.clear();
243
- lastWarningReconciledGeneration = generation;
244
- return removed;
245
- }
246
-
247
- /**
248
- * A pinned registry entry — non-template `baseUrl`, no `allowBaseUrlOverride` — outranks a saved
249
- * `baseUrl`. Dropping it silently is a footgun: requests go to an endpoint the user never
250
- * configured, and a wrong-region or wrong-account URL then surfaces only as a 401 with nothing
251
- * pointing back at the discarded setting.
252
- *
253
- * Warns rather than throws. The effective route is exactly what it was before, so a hard error
254
- * here would break configs that route fine today (a stale `baseUrl` left over from an earlier
255
- * provider is harmless whenever it names the same endpoint the registry pins).
256
- */
257
- function warnIfBaseUrlDiscarded(providerName: string, userBaseUrl: string, effectiveBaseUrl: string): void {
258
- if (isSameEndpoint(userBaseUrl, effectiveBaseUrl)) return;
259
- // Asymmetric on purpose. Past the guard above, `effectiveBaseUrl` is necessarily
260
- // `registryEntry.baseUrl`: the caller passes the resolved URL, and whenever that resolution
261
- // kept the user's value the two are equal and we have already returned. So the effective side
262
- // is a constant from this repo's registry and safe to print in full — it is also the useful
263
- // half, naming the endpoint requests will actually use. The configured side is untrusted.
264
- const discarded = configuredOriginForLog(userBaseUrl);
265
- const effective = redactSecretString(redactUrlForLog(effectiveBaseUrl));
266
- // Key off the logged forms: no raw credential is retained for the process lifetime, and
267
- // rotating a key embedded in the URL no longer re-warns about the same endpoint mismatch.
268
- // Coarser than the raw URLs — two bad paths on one host warn once, which is the right grain.
269
- const key = `${providerName} | ${discarded} | ${effective}`;
270
- if (discardedBaseUrlWarnings.has(key)) return;
271
- discardedBaseUrlWarnings.add(key);
272
- console.warn(
273
- // Routing is what this warning speaks for: an adapter may adjust the endpoint again
274
- // downstream (kiro re-derives the region), so do not promise where the request lands.
275
- `⚠️ config.json provider "${providerName}": configured baseUrl ${discarded} is ignored`
276
- + ` because this provider's endpoint is fixed at ${effective}. A URL saved for a different`
277
- + ` account or region is a common cause of 401s here — drop it, or use the provider whose endpoint matches.`,
278
- );
279
- }
280
-
281
196
  /**
282
197
  * One notice per provider id: the destination is an operator-configured key
283
198
  * (never a caller-supplied string), so the log carries no request content.
@@ -297,239 +212,10 @@ export function resetCompactionFallbackWarningsForTests(): void {
297
212
  compactionFallbackWarnings.clear();
298
213
  }
299
214
 
300
- function usableResolvedApiKey(apiKey: string | undefined): string | undefined {
301
- const resolved = resolveProviderApiKey(apiKey);
302
- return typeof resolved === "string" && resolved.trim().length > 0 ? resolved : undefined;
303
- }
304
-
305
- export function routedProviderConfig(providerName: string, provider: OcxProviderConfig): OcxProviderConfig {
306
- provider = { ...provider, _apiKeyAttempt: provider._apiKeyAttempt ?? captureProviderApiKeySelection(provider) };
307
- const registryEntry = PROVIDER_REGISTRY.find(entry => entry.id === providerName);
308
- if (!registryEntry || !providerMatchesRegistryTransportWithStaticGuards(providerName, provider)) {
309
- assertProviderDestinationAllowed(providerName, provider);
310
- // A row whose adapter no longer matches its registry entry still reaches the Responses
311
- // adapter when one model opts in through `modelAdapters` — a Volcengine Coding Plan config
312
- // saved on Chat, for instance. The replay-drop flag belongs to the DESTINATION rather than
313
- // to the provider-wide wire, so it is filled here too; without it that continuation forwards
314
- // the reasoning item the upstream answers 400 to. Destination matching refuses templated and
315
- // overridable base URLs, so this cannot follow a retargeted row, and an explicit value wins.
316
- const destination = registryEntryForProviderDestination(provider);
317
- return {
318
- ...provider,
319
- apiKey: usableResolvedApiKey(provider.apiKey),
320
- ...(provider.dropResponsesReasoningItems === undefined && destination?.dropResponsesReasoningItems !== undefined
321
- ? { dropResponsesReasoningItems: destination.dropResponsesReasoningItems }
322
- : {}),
323
- };
324
- }
325
- const resolvedApiKey = usableResolvedApiKey(provider.apiKey);
326
- const staticModelCatalog = !providerSupportsLiveModelDiscovery(providerName, provider);
327
- const repairLegacyMimoFreeAuth = providerName === "mimo-free"
328
- && staticModelCatalog
329
- && (provider.authMode === undefined || provider.authMode === "local");
330
- const explicitKeyOverride = providerUsesKeyAuthOverride(registryEntry, provider, resolvedApiKey);
331
- const canonicalAuthMode = explicitKeyOverride
332
- ? "key"
333
- : repairLegacyMimoFreeAuth
334
- ? "key"
335
- : registryEntry.authKind === "forward" || registryEntry.authKind === "oauth"
336
- ? registryEntry.authKind
337
- : provider.authMode === "forward" ? undefined : provider.authMode;
338
- const staticPolicy = resolveModelPolicy({
339
- providerName,
340
- modelId: "__provider_static__",
341
- provider,
342
- registryEntry,
343
- transportMatchedRegistry: true,
344
- ...(canonicalAuthMode ? { effectiveAuth: { authMode: canonicalAuthMode } } : {}),
345
- }).provider;
346
- const reasoningEffortMap = staticPolicy.reasoningEffortMap;
347
- const modelReasoningEffortMap = staticPolicy.modelReasoningEffortMap;
348
- const modelReasoningEfforts = staticPolicy.modelReasoningEfforts;
349
- const modelDefaultReasoningEfforts = staticPolicy.modelDefaultReasoningEfforts;
350
- const modelContextWindows = staticPolicy.modelContextWindows;
351
- const modelInputModalities = staticPolicy.modelInputModalities;
352
- // Registry static headers are documented as applying to every upstream request, so they are
353
- // filled at resolve time rather than only at seed time: a config written before a header
354
- // existed, or one carrying any header of its own, would otherwise never receive it. User
355
- // headers win, matched case-insensitively so an override replaces rather than duplicates.
356
- const headers = staticPolicy.headers;
357
- const modelMaxInputTokens = staticPolicy.modelMaxInputTokens;
358
- const modelMaxOutputTokens = staticPolicy.modelMaxOutputTokens;
359
- const modelSupportsServiceTier = staticPolicy.modelSupportsServiceTier;
360
- const modelSupportsVerbosity = staticPolicy.modelSupportsVerbosity;
361
- const noVisionModels = staticPolicy.noVisionModels;
362
- const noReasoningModels = staticPolicy.noReasoningModels;
363
- const noTemperatureModels = staticPolicy.noTemperatureModels;
364
- const noTopPModels = staticPolicy.noTopPModels;
365
- const noStopModels = staticPolicy.noStopModels;
366
- const noPenaltyModels = staticPolicy.noPenaltyModels;
367
- const noJsonSchemaModels = staticPolicy.noJsonSchemaModels;
368
- const autoToolChoiceOnlyModels = staticPolicy.autoToolChoiceOnlyModels;
369
- const preserveReasoningContentModels = staticPolicy.preserveReasoningContentModels;
370
- const requiresReasoningPlaceholderModels = staticPolicy.requiresReasoningPlaceholderModels;
371
- const reasoningSplitModels = staticPolicy.reasoningSplitModels;
372
- const inlineThinkTagModels = staticPolicy.inlineThinkTagModels;
373
- const reasoningDetailsModels = staticPolicy.reasoningDetailsModels;
374
- const thinkingToggleModels = staticPolicy.thinkingToggleModels;
375
- const thinkingBudgetModels = staticPolicy.thinkingBudgetModels;
376
- const registryBaseUrlIsTemplate = /\{[^}]*\}/.test(registryEntry.baseUrl);
377
- const userBaseUrl = typeof provider.baseUrl === "string" ? provider.baseUrl.trim() : "";
378
- const userBaseUrlIsResolved = userBaseUrl.length > 0 && !/\{[^}]*\}/.test(userBaseUrl);
379
- if (registryEntry.allowBaseUrlOverride && !userBaseUrlIsResolved) {
380
- throw new Error(`Invalid baseUrl for provider "${providerName}": expected a nonblank URL without unresolved placeholders`);
381
- }
382
- // Registry template URLs are presets; local/self-hosted entries opt in explicitly.
383
- const baseUrl = (registryBaseUrlIsTemplate || registryEntry.allowBaseUrlOverride) && userBaseUrlIsResolved
384
- ? userBaseUrl
385
- : registryEntry.baseUrl;
386
- if (userBaseUrlIsResolved) warnIfBaseUrlDiscarded(providerName, userBaseUrl, baseUrl);
387
- assertProviderDestinationAllowed(providerName, { baseUrl, allowPrivateNetwork: provider.allowPrivateNetwork });
388
-
389
- const resolved: OcxProviderConfig = {
390
- ...provider,
391
- adapter: registryEntry.adapter,
392
- baseUrl,
393
- ...(provider.responsesPath === undefined && registryEntry.responsesPath !== undefined
394
- ? { responsesPath: registryEntry.responsesPath }
395
- : {}),
396
- ...(provider.chatCompletionsPath === undefined && registryEntry.chatCompletionsPath !== undefined
397
- ? { chatCompletionsPath: registryEntry.chatCompletionsPath }
398
- : {}),
399
- ...(provider.requiresAdjacentResponsesToolResults === undefined
400
- && registryEntry.requiresAdjacentResponsesToolResults !== undefined
401
- ? { requiresAdjacentResponsesToolResults: registryEntry.requiresAdjacentResponsesToolResults }
402
- : {}),
403
- ...(provider.requiresPairedResponsesToolResults === undefined
404
- && registryEntry.requiresPairedResponsesToolResults !== undefined
405
- ? { requiresPairedResponsesToolResults: registryEntry.requiresPairedResponsesToolResults }
406
- : {}),
407
- ...(provider.annotateEmptyToolOutputs === undefined
408
- && registryEntry.annotateEmptyToolOutputs !== undefined
409
- ? { annotateEmptyToolOutputs: registryEntry.annotateEmptyToolOutputs }
410
- : {}),
411
- ...(provider.fastWire === undefined && registryEntry.fastWire !== undefined
412
- ? {
413
- fastWire: cloneFastWire(registryEntry.fastWire),
414
- }
415
- : {}),
416
- ...(provider.supportsServiceTier === undefined && registryEntry.supportsServiceTier !== undefined
417
- ? { supportsServiceTier: registryEntry.supportsServiceTier }
418
- : {}),
419
- // An off Fast switch is a provider-wide denial on the runtime provider, so a Fast policy
420
- // resolved without the provider name still refuses (providerFastSwitchOff).
421
- ...(fastSwitchOff(provider, registryEntry) ? { supportsServiceTier: false } : {}),
422
- // Registry-only web-search capability: without this backfill a saved provider row reaches
423
- // the Responses adapter with the flag `undefined`, so the capability gate added in #2262
424
- // reads "unclassified" and forwards Codex's OpenAI-only `web_search` config fields. xAI
425
- // rejects the whole request before inference ("Argument not supported:
426
- // external_web_access"), which killed every routed Grok turn on the Responses lane.
427
- // enrichProviderFromRegistry() already fills this, but the request path resolves through
428
- // routedProviderConfig() and never called it.
429
- ...(provider.supportsOpenAiWebSearchToolFields === undefined
430
- && registryEntry.supportsOpenAiWebSearchToolFields !== undefined
431
- ? { supportsOpenAiWebSearchToolFields: registryEntry.supportsOpenAiWebSearchToolFields }
432
- : {}),
433
- ...(provider.supportsResponsesCustomTools === undefined && registryEntry.supportsResponsesCustomTools !== undefined
434
- ? { supportsResponsesCustomTools: registryEntry.supportsResponsesCustomTools }
435
- : {}),
436
- ...(provider.preserveResponsesReasoningContent === undefined && registryEntry.preserveResponsesReasoningContent !== undefined
437
- ? { preserveResponsesReasoningContent: registryEntry.preserveResponsesReasoningContent }
438
- : {}),
439
- ...(provider.preserveResponsesInputItemIds === undefined && registryEntry.preserveResponsesInputItemIds !== undefined
440
- ? { preserveResponsesInputItemIds: registryEntry.preserveResponsesInputItemIds }
441
- : {}),
442
- ...(provider.preserveResponsesMessageMetadata === undefined && registryEntry.preserveResponsesMessageMetadata !== undefined
443
- ? { preserveResponsesMessageMetadata: registryEntry.preserveResponsesMessageMetadata }
444
- : {}),
445
- ...(provider.dropResponsesReasoningItems === undefined && registryEntry.dropResponsesReasoningItems !== undefined
446
- ? { dropResponsesReasoningItems: registryEntry.dropResponsesReasoningItems }
447
- : {}),
448
- // The request path resolves through routedProviderConfig() and never calls
449
- // enrichProviderFromRegistry(), so a saved provider row written before the
450
- // registry learned this flag must be backfilled here or route.provider never
451
- // carries it and the showThinkingSummary opt-in stays dead.
452
- ...(provider.showThinkingSummary === undefined && registryEntry.showThinkingSummary !== undefined
453
- ? { showThinkingSummary: registryEntry.showThinkingSummary }
454
- : {}),
455
- // Registry-only client-facing repair policy (#938): fill only when the
456
- // saved provider has no explicit policy; clone so runtime never aliases
457
- // the registry constant.
458
- ...(provider.responsesItemIdRepair === undefined && registryEntry.responsesItemIdRepair
459
- ? {
460
- responsesItemIdRepair: {
461
- ...(registryEntry.responsesItemIdRepair.message ? { message: [...registryEntry.responsesItemIdRepair.message] } : {}),
462
- ...(registryEntry.responsesItemIdRepair.reasoning ? { reasoning: [...registryEntry.responsesItemIdRepair.reasoning] } : {}),
463
- ...(registryEntry.responsesItemIdRepair.repairMissingTerminalIds !== undefined
464
- ? { repairMissingTerminalIds: registryEntry.responsesItemIdRepair.repairMissingTerminalIds }
465
- : {}),
466
- ...(registryEntry.responsesItemIdRepair.repairInvalidIds !== undefined
467
- ? { repairInvalidIds: registryEntry.responsesItemIdRepair.repairInvalidIds }
468
- : {}),
469
- },
470
- }
471
- : {}),
472
- authMode: canonicalAuthMode,
473
- apiKey: resolvedApiKey,
474
- ...(staticModelCatalog ? { liveModels: false } : {}),
475
- ...(headers ? { headers } : {}),
476
- // Backfill the Google wire mode + Vertex project/location from the registry when the user
477
- // config omits them, so a minimal `google-vertex`/`google-antigravity` entry still routes
478
- // through the correct branch (CCA/Vertex) instead of falling back to AI Studio.
479
- ...(provider.googleMode === undefined && registryEntry.googleMode !== undefined ? { googleMode: registryEntry.googleMode } : {}),
480
- ...(provider.project === undefined && registryEntry.project !== undefined ? { project: registryEntry.project } : {}),
481
- ...(provider.location === undefined && registryEntry.location !== undefined ? { location: registryEntry.location } : {}),
482
- ...(provider.contextWindow === undefined && registryEntry.contextWindow !== undefined ? { contextWindow: registryEntry.contextWindow } : {}),
483
- ...((provider.reasoningEfforts === undefined || hasLegacyClinePassReasoningEfforts(providerName, provider))
484
- && registryEntry.reasoningEfforts !== undefined
485
- ? { reasoningEfforts: [...registryEntry.reasoningEfforts] }
486
- : {}),
487
- ...(provider.escapeBuiltinToolNames === undefined && registryEntry.escapeBuiltinToolNames !== undefined ? { escapeBuiltinToolNames: registryEntry.escapeBuiltinToolNames } : {}),
488
- ...(provider.keyOptional === undefined && registryEntry.keyOptional !== undefined ? { keyOptional: registryEntry.keyOptional } : {}),
489
- ...(provider.modelSuffixBracketStrip === undefined && registryEntry.modelSuffixBracketStrip !== undefined ? { modelSuffixBracketStrip: registryEntry.modelSuffixBracketStrip } : {}),
490
- // Scalar backfill: a persisted config created before the flag shipped inherits the registry
491
- // opt-in, while an explicit user `false` keeps overriding registry `true`.
492
- ...(provider.parallelToolCalls === undefined && registryEntry.parallelToolCalls !== undefined ? { parallelToolCalls: registryEntry.parallelToolCalls } : {}),
493
- ...(provider.promptCacheKey === undefined && registryEntry.promptCacheKey !== undefined ? { promptCacheKey: registryEntry.promptCacheKey } : {}),
494
- ...(provider.chatServiceTier === undefined && registryEntry.chatServiceTier !== undefined ? { chatServiceTier: registryEntry.chatServiceTier } : {}),
495
- ...(provider.openaiChatEofTolerance === undefined && registryEntry.openaiChatEofTolerance !== undefined
496
- ? { openaiChatEofTolerance: registryEntry.openaiChatEofTolerance }
497
- : {}),
498
- ...(provider.reasoningWireFormat === undefined && registryEntry.reasoningWireFormat !== undefined
499
- ? { reasoningWireFormat: registryEntry.reasoningWireFormat }
500
- : {}),
501
- ...(provider.defaultMaxOutputTokens === undefined && registryEntry.defaultMaxOutputTokens !== undefined
502
- ? { defaultMaxOutputTokens: registryEntry.defaultMaxOutputTokens }
503
- : {}),
504
- ...(modelContextWindows ? { modelContextWindows } : {}),
505
- ...(modelInputModalities ? { modelInputModalities } : {}),
506
- ...(modelMaxInputTokens ? { modelMaxInputTokens } : {}),
507
- ...(modelMaxOutputTokens ? { modelMaxOutputTokens } : {}),
508
- ...(modelSupportsServiceTier ? { modelSupportsServiceTier } : {}),
509
- ...(modelSupportsVerbosity ? { modelSupportsVerbosity } : {}),
510
- ...(modelReasoningEfforts ? { modelReasoningEfforts } : {}),
511
- ...(modelDefaultReasoningEfforts ? { modelDefaultReasoningEfforts } : {}),
512
- ...(reasoningEffortMap ? { reasoningEffortMap } : {}),
513
- ...(modelReasoningEffortMap ? { modelReasoningEffortMap } : {}),
514
- ...(noVisionModels ? { noVisionModels } : {}),
515
- ...(noReasoningModels ? { noReasoningModels } : {}),
516
- ...(noTemperatureModels ? { noTemperatureModels } : {}),
517
- ...(noTopPModels ? { noTopPModels } : {}),
518
- ...(noStopModels ? { noStopModels } : {}),
519
- ...(noPenaltyModels ? { noPenaltyModels } : {}),
520
- ...(noJsonSchemaModels ? { noJsonSchemaModels } : {}),
521
- ...(autoToolChoiceOnlyModels ? { autoToolChoiceOnlyModels } : {}),
522
- ...(preserveReasoningContentModels ? { preserveReasoningContentModels } : {}),
523
- ...(requiresReasoningPlaceholderModels ? { requiresReasoningPlaceholderModels } : {}),
524
- ...(reasoningSplitModels ? { reasoningSplitModels } : {}),
525
- ...(inlineThinkTagModels ? { inlineThinkTagModels } : {}),
526
- ...(reasoningDetailsModels ? { reasoningDetailsModels } : {}),
527
- ...(thinkingToggleModels ? { thinkingToggleModels } : {}),
528
- ...(thinkingBudgetModels ? { thinkingBudgetModels } : {}),
529
- };
530
- applyDirectReasoningEffortContracts(registryEntry, resolved, provider);
531
- return resolved;
532
- }
215
+ // `routedProviderConfig` and its warning memo moved to `providers/routed-config.ts`: the
216
+ // request-time resolver needs no routing state, and `providers/api-key-selection.ts` importing
217
+ // it through this file created an import cycle (router -> quota -> api-keys -> api-key-selection).
218
+ export { reconcileRouterWarningMemos, routedProviderConfig } from "./providers/routed-config";
533
219
 
534
220
  /**
535
221
  * Rows eligible for bare-model inference (configured default model, model list, model alias).
@@ -622,7 +308,8 @@ function crossProviderRedirectTarget(config: OcxConfig, providerName: string, va
622
308
  }
623
309
  }
624
310
 
625
- function isBareOpenAiFamilyModel(modelId: string): boolean {
311
+ /** Exported for the main-account hard-lock fallback: only this family is ever redirected. */
312
+ export function isBareOpenAiFamilyModel(modelId: string): boolean {
626
313
  return !modelId.includes("/")
627
314
  && (/^(?:gpt-|o1-|o3-|o4-)/.test(modelId) || CODEX_INTERNAL_OPENAI_MODELS.has(modelId));
628
315
  }
@@ -94,7 +94,6 @@ interface HealthSample {
94
94
  }
95
95
 
96
96
  interface HealthRow extends HealthSample {
97
- attemptCount?: number;
98
97
  rowJson?: string | null;
99
98
  }
100
99
 
@@ -103,33 +102,53 @@ interface HealthRow extends HealthSample {
103
102
  * Combo/failover requests store each upstream try in `entry.attempts` while
104
103
  * the top-level row records the final outcome; a provider/model that failed
105
104
  * as a non-final attempt must still contribute its own health samples.
105
+ *
106
+ * Only physical sends are evidence: an attempt answered locally or refused
107
+ * before it left the proxy says nothing about the target. The row's terminal
108
+ * facts belong to its last physical send. Returns `undefined` when the row
109
+ * itself is the sample (no attempts, a single attempt, or none named for this
110
+ * candidate) and `[]` when the row sent nothing upstream at all.
106
111
  */
107
112
  function attemptSamplesFor(
108
- row: Pick<HealthRow, "timestamp" | "attemptCount" | "rowJson">,
113
+ row: Pick<HealthRow, "timestamp" | "rowJson">,
109
114
  provider: string,
110
115
  model: string,
111
- ): HealthSample[] {
112
- if (!row.rowJson || (row.attemptCount ?? 1) <= 1) return [];
116
+ ): HealthSample[] | undefined {
117
+ if (!row.rowJson) return undefined;
113
118
  try {
114
- const parsed = JSON.parse(row.rowJson) as { attempts?: unknown };
115
- if (!Array.isArray(parsed.attempts)) return [];
119
+ const parsed = JSON.parse(row.rowJson) as {
120
+ attempts?: unknown;
121
+ closeReason?: unknown;
122
+ terminalStatus?: unknown;
123
+ failureCause?: unknown;
124
+ };
125
+ // A local refusal logged before any attempt began never reached a target.
126
+ if (!Array.isArray(parsed.attempts)) return parsed.failureCause === "local-refusal" ? [] : undefined;
127
+ const attempts = parsed.attempts.filter(
128
+ (attempt): attempt is Record<string, unknown> =>
129
+ !!attempt && typeof attempt === "object" && !Array.isArray(attempt),
130
+ );
131
+ const sent = attempts.filter(attempt => attempt.locallyAnswered !== true && attempt.sendCount !== 0);
132
+ if (sent.length === 0) return [];
133
+ const mine = attempts.filter(attempt => attempt.provider === provider && attempt.model === model);
134
+ if (attempts.length <= 1 || mine.length === 0) return undefined;
135
+ const finalSend = sent.at(-1);
116
136
  const samples: HealthSample[] = [];
117
- for (const attempt of parsed.attempts) {
118
- if (!attempt || typeof attempt !== "object" || Array.isArray(attempt)) continue;
119
- const record = attempt as Record<string, unknown>;
120
- if (record.provider !== provider || record.model !== model) continue;
137
+ for (const record of mine) {
138
+ if (!sent.includes(record)) continue;
121
139
  if (typeof record.status !== "number" || typeof record.durationMs !== "number") continue;
140
+ const isFinal = record === finalSend;
122
141
  samples.push({
123
142
  status: record.status,
124
- closeReason: null,
125
- terminalStatus: null,
143
+ closeReason: isFinal && typeof parsed.closeReason === "string" ? parsed.closeReason : null,
144
+ terminalStatus: isFinal && typeof parsed.terminalStatus === "string" ? parsed.terminalStatus : null,
126
145
  durationMs: record.durationMs,
127
146
  timestamp: row.timestamp,
128
147
  });
129
148
  }
130
149
  return samples;
131
150
  } catch {
132
- return [];
151
+ return undefined;
133
152
  }
134
153
  }
135
154
 
@@ -243,8 +262,7 @@ function computeHistoricalHealthEvidence(
243
262
  }
244
263
  const rows = handle.query(
245
264
  `SELECT status, close_reason AS closeReason, terminal_status AS terminalStatus,
246
- duration_ms AS durationMs, timestamp,
247
- attempt_count AS attemptCount, row_json AS rowJson
265
+ duration_ms AS durationMs, timestamp, row_json AS rowJson
248
266
  FROM requests WHERE ${where.join(" AND ")}
249
267
  ORDER BY timestamp DESC LIMIT ?`,
250
268
  ).all(...values, HEALTH_MAX_SAMPLES) as HealthRow[];
@@ -254,7 +272,7 @@ function computeHistoricalHealthEvidence(
254
272
  // rows that cannot contribute samples for this candidate.
255
273
  const escapeLike = (value: string): string => value.replace(/[\\%_]/g, match => `\\${match}`);
256
274
  const attemptRows = handle.query(
257
- `SELECT timestamp, attempt_count AS attemptCount, row_json AS rowJson
275
+ `SELECT timestamp, row_json AS rowJson
258
276
  FROM requests WHERE timestamp >= ? AND attempt_count > 1
259
277
  AND row_json LIKE ? ESCAPE '\\'
260
278
  AND row_json LIKE ? ESCAPE '\\'
@@ -267,17 +285,14 @@ function computeHistoricalHealthEvidence(
267
285
  input.provider,
268
286
  input.model,
269
287
  HEALTH_MAX_SAMPLES,
270
- ) as Array<
271
- Pick<HealthRow, "timestamp" | "attemptCount" | "rowJson">
272
- >;
288
+ ) as Array<Pick<HealthRow, "timestamp" | "rowJson">>;
273
289
 
274
290
  const samples: HealthSample[] = [];
275
291
  for (const row of rows) {
276
- const attemptSamples = attemptSamplesFor(row, input.provider, input.model);
277
- samples.push(...(attemptSamples.length > 0 ? attemptSamples : [row]));
292
+ samples.push(...(attemptSamplesFor(row, input.provider, input.model) ?? [row]));
278
293
  }
279
294
  for (const row of attemptRows) {
280
- samples.push(...attemptSamplesFor(row, input.provider, input.model));
295
+ samples.push(...(attemptSamplesFor(row, input.provider, input.model) ?? []));
281
296
  }
282
297
  // Newest first for the consecutive-failure walk; attempt samples inherit
283
298
  // their row's timestamp.