@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
@@ -7,9 +7,9 @@ import { isBuiltinAnthropicInstanceRow } from "../providers/anthropic-instance";
7
7
  * exists is Anthropic's — behind its own opt-in. So xAI, Cursor, Kimi, GitHub Copilot,
8
8
  * Antigravity and Nous have no recovery path on a 429 even with several accounts logged in.
9
9
  *
10
- * Deliberately narrower than the Anthropic pool: no session affinity, no quota-ranked selection,
11
- * no probe leases. Those carry provider-specific meaning; this module only answers "the account
12
- * that just 429'd is cooled, is there another one we may use".
10
+ * Conversation ownership lives in generic-session-affinity.ts; this module owns account
11
+ * eligibility, quota-ranked preference and bounded refusal recovery. Probe leases remain
12
+ * provider-specific.
13
13
  *
14
14
  * NOT a home for Codex (`codex/routing.ts` owns quota scopes and probe leases) or Anthropic
15
15
  * (`oauth/anthropic-routing.ts` owns affinity and a fail-closed local-cli credential rule).
@@ -25,6 +25,7 @@ import {
25
25
  hasHeadroomEvidence,
26
26
  isAccountQuotaExhausted,
27
27
  rankAccountsByHeadroom,
28
+ rankAccountsByResetFirst,
28
29
  classifyModelFamilyForQuota,
29
30
  type QuotaModelFamily,
30
31
  } from "./account-quota-rank";
@@ -41,6 +42,7 @@ import type { KiroRefusalKind } from "../adapters/kiro-refusal";
41
42
  import { kiroAccountEvidence } from "../providers/kiro-usage";
42
43
  import { kiroEvidenceIdentity } from "../providers/kiro-account-state-disk";
43
44
  import { kiroAccountSupportsModel } from "../providers/kiro-model-catalog";
45
+ import { antigravityAccountSupportsModel } from "../providers/antigravity-account-roster";
44
46
  import { ACCOUNT_QUOTA_TTL_MS } from "../providers/quota-wire";
45
47
  import { sweepExpiredOnWrite } from "../lib/state-store-sweeper";
46
48
  import { subscribeOAuthAccountPauseChanges } from "../lib/account-selection-events";
@@ -248,11 +250,31 @@ export function eligibleFailoverAccounts(providerName: string, now = Date.now(),
248
250
  return eligibleIdsIn(getAccountSet(providerName), providerName, now, family);
249
251
  }
250
252
 
251
- /** Model evidence only ranks accounts already eligible and below any configured cap. */
253
+ /** Whether an account's own model listing includes `model`; undefined when unknown. */
254
+ function accountListsModel(providerName: string, accountId: string, model: string): boolean | undefined {
255
+ if (providerName === "kiro") return kiroAccountSupportsModel(accountId, model);
256
+ if (providerName === "google-antigravity") return antigravityAccountSupportsModel(providerName, accountId, model);
257
+ return undefined;
258
+ }
259
+
260
+ /**
261
+ * Model evidence only ranks accounts already eligible and below any configured cap.
262
+ *
263
+ * Kiro and Antigravity both gate models per account. For Antigravity (Claude 4.6 vs 5.5, Argon)
264
+ * an account whose discovered roster lists the model wins; an account whose roster omits it is
265
+ * skipped while any other remains; an account with no roster yet is unknown and stays usable.
266
+ */
252
267
  export function preferKiroModelSupport(
253
268
  providerName: string, ids: string[], model?: string | null, config?: OcxConfig,
254
269
  ): string[] {
255
- if (providerName !== "kiro" || !model) return ids;
270
+ if (!model) return ids;
271
+ if (providerName === "google-antigravity") {
272
+ const positive = ids.filter(id => accountListsModel(providerName, id, model) === true);
273
+ if (positive.length) return positive;
274
+ const notListedAgainst = ids.filter(id => accountListsModel(providerName, id, model) !== false);
275
+ return notListedAgainst.length ? notListedAgainst : ids;
276
+ }
277
+ if (providerName !== "kiro") return ids;
256
278
  const cap = config?.providers?.kiro?.oauthAccountFailover?.maxConcurrentPerAccount;
257
279
  const withRoom = typeof cap === "number" && Number.isInteger(cap) && cap >= 1 && cap <= 100
258
280
  ? ids.filter(id => accountInFlight("kiro", id) < cap) : ids;
@@ -330,7 +352,7 @@ export function rotateAntigravityAccountOnAuthRefusal(
330
352
  }
331
353
 
332
354
  /** Generic pool strategies the kernel can actually run. `quota` IS the pre-kernel path. */
333
- type ActiveGenericStrategy = "round-robin" | "fill-first" | "least-loaded";
355
+ type ActiveGenericStrategy = "round-robin" | "fill-first" | "reset-first" | "least-loaded";
334
356
 
335
357
  /** Matches the Codex and Anthropic pools; the DTO still reports `null` for "not stored". */
336
358
  const DEFAULT_GENERIC_AUTO_SWITCH_THRESHOLD = 80;
@@ -344,10 +366,13 @@ const DEFAULT_GENERIC_AUTO_SWITCH_THRESHOLD = 80;
344
366
  * branch instead of a four-way one.
345
367
  */
346
368
  function activeGenericStrategy(config: OcxConfig, providerName: string): ActiveGenericStrategy | null {
369
+ // All pool strategies run behind the kernel flag; `quota` is the pre-kernel path.
370
+ // Letting `reset-first` bypass it would change 429 selection for pools that never
371
+ // opted into kernel-gated strategies.
347
372
  if (config.pool?.kernel !== true) return null;
348
373
  const raw = config.providers?.[providerName]?.oauthAccountFailover?.strategy;
349
374
  if (providerName === "kiro" && raw === "least-loaded") return raw;
350
- return raw === "round-robin" || raw === "fill-first" ? raw : null;
375
+ return raw === "round-robin" || raw === "fill-first" || raw === "reset-first" ? raw : null;
351
376
  }
352
377
 
353
378
  function genericStickyLimit(config: OcxConfig, providerName: string): number {
@@ -395,6 +420,7 @@ function pickFillFirstGenericAccount(
395
420
  now: number,
396
421
  requestedModelId?: string | null,
397
422
  accountRows?: ReadonlyMap<string, ProviderAccount>,
423
+ preferBelowThreshold = false,
398
424
  ): string | null {
399
425
  const stableAll = stableGenericRoster(providerName);
400
426
  if (stableAll.length < 2) return null;
@@ -410,6 +436,11 @@ function pickFillFirstGenericAccount(
410
436
  }
411
437
  const start = activeId ? stableAll.indexOf(activeId) : -1;
412
438
  const ring = start >= 0 ? [...stableAll.slice(start + 1), ...stableAll.slice(0, start)] : stableAll;
439
+ if (preferBelowThreshold) {
440
+ const below = ring.find(id => id !== activeId && eligible.has(id)
441
+ && !isOverAutoSwitchThreshold(providerName, id, threshold, requestedModelId, accountRows?.get(id)));
442
+ if (below) return below;
443
+ }
413
444
  for (const id of ring) {
414
445
  if (id !== activeId && eligible.has(id)) return id;
415
446
  }
@@ -553,6 +584,10 @@ export function rotateGenericOAuthAccountOnRefusal(
553
584
  }
554
585
  return null;
555
586
  }
587
+ if (strategy === "reset-first") {
588
+ return rankAccountsByResetFirst(providerName, candidates, requestedModelId, now,
589
+ new Map(set.accounts.map(account => [account.id, account])))[0] ?? null;
590
+ }
556
591
  // With no quota evidence this returns the ring untouched, so providers without
557
592
  // per-account quota keep exactly the traversal they have today.
558
593
  return rankAccountsByHeadroom(providerName, candidates, requestedModelId,
@@ -613,6 +648,7 @@ export function preferredInitialAccount(
613
648
  providerName: string,
614
649
  now = Date.now(),
615
650
  requestedModelId?: string | null,
651
+ activeAccountOverride?: string,
616
652
  ): string | null {
617
653
  // The PROACTIVE predicate, not the reactive one: this steers a request upstream has not
618
654
  // refused, so `oauthAccountFailover.enabled: false` must still be able to refuse it.
@@ -621,7 +657,7 @@ export function preferredInitialAccount(
621
657
  // active id separately would delay manual selection and account removal.
622
658
  const selected = getAccountSet(providerName);
623
659
  if (!selected) return null;
624
- const active = selected.activeAccountId;
660
+ const active = activeAccountOverride ?? selected.activeAccountId;
625
661
  const accountRows = new Map(selected.accounts.map(account => [account.id, account]));
626
662
  const order = selected.accounts
627
663
  .filter(account => account.paused !== true && account.needsReauth !== true)
@@ -631,10 +667,13 @@ export function preferredInitialAccount(
631
667
  const modelEligible = preferKiroModelSupport(providerName,
632
668
  eligibleFailoverAccounts(providerName, now, classifyModelFamilyForQuota(providerName, requestedModelId)),
633
669
  requestedModelId, config);
634
- const activeLacks = providerName === "kiro" && !!active && !!requestedModelId
635
- && kiroAccountSupportsModel(active, requestedModelId) === false;
670
+ const activeLacks = (providerName === "kiro" || providerName === "google-antigravity")
671
+ && !!active && !!requestedModelId
672
+ && accountListsModel(providerName, active, requestedModelId) === false;
636
673
  const listingSibling = activeLacks
637
- ? modelEligible.find(id => id !== active && kiroModelListingHasRoom(config, id, requestedModelId!))
674
+ ? modelEligible.find(id => id !== active && (providerName === "kiro"
675
+ ? kiroModelListingHasRoom(config, id, requestedModelId!)
676
+ : accountListsModel(providerName, id, requestedModelId!) === true))
638
677
  : undefined;
639
678
  if (listingSibling) return listingSibling;
640
679
 
@@ -670,9 +709,20 @@ export function preferredInitialAccount(
670
709
  return picked && picked !== active ? picked : null;
671
710
  }
672
711
  if (strategy === "fill-first") {
673
- const picked = pickFillFirstGenericAccount(config, providerName, active, now, requestedModelId, accountRows);
712
+ const picked = pickFillFirstGenericAccount(config, providerName, active, now, requestedModelId, accountRows, activeAccountOverride !== undefined);
674
713
  return picked && picked !== active ? picked : null;
675
714
  }
715
+ if (strategy === "reset-first") {
716
+ // Same candidate set as round-robin: paused, reauth-needed and cooled accounts are out,
717
+ // and an account whose model listing omits the requested model loses to one that has it.
718
+ // Reset ordering needs quota evidence; without it the ordinary active-account path runs.
719
+ const family = classifyModelFamilyForQuota(providerName, requestedModelId);
720
+ const eligibleNow = preferKiroModelSupport(providerName,
721
+ eligibleFailoverAccounts(providerName, now, family), requestedModelId, config);
722
+ if (eligibleNow.length === 0 || !hasHeadroomEvidence(providerName, eligibleNow, requestedModelId, accountRows)) return null;
723
+ const best = rankAccountsByResetFirst(providerName, eligibleNow, requestedModelId, now, accountRows)[0] ?? null;
724
+ return best && best !== active ? best : null;
725
+ }
676
726
 
677
727
  const activeRow = selected.accounts.find(account => account.id === active);
678
728
  if (activeRow && activeRow.paused !== true && activeRow.needsReauth !== true
@@ -0,0 +1,125 @@
1
+ /** Request-local conversation ownership for opt-in generic OAuth pools. No credentials are retained. */
2
+ import { createHash } from "node:crypto";
3
+ import { getConfigDir } from "../config/paths";
4
+ import { subscribeAccountSelections } from "../lib/account-selection-events";
5
+ import type { OcxConfig } from "../types";
6
+ import { classifyModelFamilyForQuota } from "./account-quota-rank";
7
+ import { eligibleFailoverAccounts, isGenericFailoverProvider, preferKiroModelSupport, preferredInitialAccount } from "./generic-account-failover";
8
+ import type { OAuthAccessSnapshot } from "./index";
9
+ import { captureOAuthAccountSelection, credentialGeneration, getAccountCredentialWithStatus } from "./store";
10
+ import type { OAuthAccountSelection } from "./types";
11
+
12
+ const IDLE_TTL_MS = 24 * 60 * 60_000;
13
+ const MAX_ENTRIES = 2_000;
14
+ const bindings = new Map<string, { accountId: string; lastUsedAt: number; selection: OAuthAccountSelection }>();
15
+ const prefix = (provider: string) => `${JSON.stringify([getConfigDir(), provider])}\u0000`;
16
+ const key = (provider: string, session: string) => prefix(provider) + "session:" + createHash("sha256").update(session).digest("hex");
17
+ const cursorKey = (provider: string) => prefix(provider) + "fill-first";
18
+
19
+ /** A persisted operator/roster/policy change revokes old session choices before its writer returns. */
20
+ export function clearGenericSessionAffinity(provider?: string): void {
21
+ if (!provider) { bindings.clear(); return; }
22
+ const scope = prefix(provider);
23
+ for (const id of bindings.keys()) if (id.startsWith(scope)) bindings.delete(id);
24
+ }
25
+ subscribeAccountSelections(event => { if (event.kind === "oauth") clearGenericSessionAffinity(event.provider); });
26
+
27
+ /** Unlike new-session strategy selection, keeping the surviving account needs no 2+ quorum. */
28
+ export function genericSessionAffinityEnabled(config: OcxConfig, provider: string): boolean {
29
+ const row = config.providers[provider];
30
+ const enabled = !!row && row.disabled !== true && provider !== "anthropic2" && isGenericFailoverProvider(provider, row)
31
+ && row.oauthAccountFailover?.sessionAffinity === "conversation"
32
+ && (row.oauthAccountFailover.enabled ?? config.oauthAccountFailover?.enabled) === true;
33
+ if (!enabled) clearGenericSessionAffinity(provider);
34
+ return enabled;
35
+ }
36
+
37
+ export function genericSessionAccountEligible(
38
+ config: OcxConfig, provider: string, accountId: string, model?: string | null, now = Date.now(), allowActiveFallback = false,
39
+ ): boolean {
40
+ const eligible = eligibleFailoverAccounts(provider, now, classifyModelFamilyForQuota(provider, model));
41
+ // Existing conversation ownership is independent of transient per-account load.
42
+ if (preferKiroModelSupport(provider, eligible, model).includes(accountId)) return true;
43
+ // Cached automatic exclusions remain preferences when no alternative exists. Preserve the
44
+ // ordinary active/singleton attempt and its upstream refusal instead of inventing a conflict.
45
+ if (!allowActiveFallback || eligible.length > 0 || captureOAuthAccountSelection(provider)?.accountId !== accountId) return false;
46
+ const row = getAccountCredentialWithStatus(provider, accountId);
47
+ return !!row && !row.paused && !row.needsReauth;
48
+ }
49
+
50
+ /** Peek only. The admitted snapshot, never a speculative token lookup, establishes ownership. */
51
+ export function genericSessionAccount(
52
+ config: OcxConfig, provider: string, session: string | null, model?: string | null, now = Date.now(),
53
+ ): string | null {
54
+ if (!genericSessionAffinityEnabled(config, provider) || !session) return null;
55
+ return readBinding(config, provider, key(provider, session), model, now);
56
+ }
57
+
58
+ function readBinding(config: OcxConfig, provider: string, id: string, model: string | null | undefined, now: number): string | null {
59
+ const bound = bindings.get(id);
60
+ if (!bound) return null;
61
+ const current = captureOAuthAccountSelection(provider);
62
+ if (!current || current.accountId !== bound.selection.accountId || current.revision !== bound.selection.revision
63
+ || now - bound.lastUsedAt >= IDLE_TTL_MS || now < bound.lastUsedAt
64
+ || !genericSessionAccountEligible(config, provider, bound.accountId, model, now)) {
65
+ bindings.delete(id);
66
+ return null;
67
+ }
68
+ return bound.accountId;
69
+ }
70
+
71
+ /** New sessions use the configured strategy; an ineligible active account cannot defeat release. */
72
+ export function preferredGenericSessionAccount(
73
+ config: OcxConfig, provider: string, session: string | null, model?: string | null, now = Date.now(),
74
+ ): string | null {
75
+ if (!genericSessionAffinityEnabled(config, provider)) return null;
76
+ const bound = genericSessionAccount(config, provider, session, model, now);
77
+ if (bound) return bound;
78
+ const cursor = readBinding(config, provider, cursorKey(provider), model, now);
79
+ const active = cursor ?? captureOAuthAccountSelection(provider)?.accountId;
80
+ const preferred = preferredInitialAccount(config, provider, now, model, active);
81
+ if (preferred) return preferred;
82
+ if (active && genericSessionAccountEligible(config, provider, active, model, now)) return active;
83
+ return preferKiroModelSupport(provider,
84
+ eligibleFailoverAccounts(provider, now, classifyModelFamilyForQuota(provider, model)), model, config)[0]
85
+ ?? captureOAuthAccountSelection(provider)?.accountId ?? null;
86
+ }
87
+
88
+ /**
89
+ * Validate against the operator's persisted selection without moving it. ALL automatic choices
90
+ * in affinity mode use this receipt, including unkeyed requests and failovers, so concurrent
91
+ * conversations cannot invalidate each other's selection revision. Manual A→B→A still fences.
92
+ */
93
+ export function admitGenericSessionAccount(
94
+ config: OcxConfig, provider: string, expected: OAuthAccountSelection,
95
+ snapshot: OAuthAccessSnapshot, model?: string | null, modeEnabled = genericSessionAffinityEnabled(config, provider),
96
+ ): OAuthAccountSelection | null {
97
+ if (!modeEnabled || snapshot.provider !== provider) return null;
98
+ const current = captureOAuthAccountSelection(provider);
99
+ if (!current || current.accountId !== expected.accountId || current.revision !== expected.revision) return null;
100
+ const row = getAccountCredentialWithStatus(provider, snapshot.accountId);
101
+ if (!row || row.paused || row.needsReauth || credentialGeneration(row.credential) !== snapshot.generation
102
+ || !genericSessionAccountEligible(config, provider, snapshot.accountId, model, Date.now(), true)) return null;
103
+ return current;
104
+ }
105
+
106
+ export function bindGenericSessionAccount(
107
+ config: OcxConfig, provider: string, session: string | null, accountId: string, model?: string | null, now = Date.now(),
108
+ ): void {
109
+ if (!genericSessionAffinityEnabled(config, provider)
110
+ || !genericSessionAccountEligible(config, provider, accountId, model, now)) return;
111
+ for (const [id, entry] of bindings) {
112
+ if (now - entry.lastUsedAt >= IDLE_TTL_MS || now < entry.lastUsedAt) bindings.delete(id);
113
+ }
114
+ const selection = captureOAuthAccountSelection(provider);
115
+ if (!selection) return;
116
+ const keep = (id: string) => {
117
+ bindings.delete(id);
118
+ bindings.set(id, { accountId, lastUsedAt: now, selection });
119
+ };
120
+ const hit = session && genericSessionAccount(config, provider, session, model, now) === accountId;
121
+ if (!hit && config.pool?.kernel === true && config.providers[provider]?.oauthAccountFailover?.strategy === "fill-first")
122
+ keep(cursorKey(provider));
123
+ if (session) keep(key(provider, session));
124
+ while (bindings.size > MAX_ENTRIES) bindings.delete(bindings.keys().next().value!);
125
+ }