@bitkyc08/opencodex 2.86.0 → 2.88.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 (400) hide show
  1. package/gui/dist/assets/App-CHC89EXQ.js +58 -0
  2. package/gui/dist/assets/App-wT3mXqFz.css +1 -0
  3. package/gui/dist/assets/{Tray-DyR9yBee.js → Tray-B5bC7AWL.js} +1 -1
  4. package/gui/dist/assets/index-B5bsvxor.js +94 -0
  5. package/gui/dist/assets/index-DPeB9HpV.css +1 -0
  6. package/gui/dist/assets/quota-summary-D8y2wTsB.js +1 -0
  7. package/gui/dist/index.html +2 -2
  8. package/package.json +1 -1
  9. package/src/adapters/anthropic/beta-allowlist.ts +9 -8
  10. package/src/adapters/anthropic/beta-body-pairs.ts +93 -0
  11. package/src/adapters/anthropic/passthrough.ts +7 -23
  12. package/src/adapters/anthropic-progress.ts +84 -0
  13. package/src/adapters/anthropic-rejection-memo.ts +71 -0
  14. package/src/adapters/anthropic-replay.ts +135 -0
  15. package/src/adapters/anthropic-tool-catalog.ts +11 -0
  16. package/src/adapters/anthropic.ts +151 -33
  17. package/src/adapters/base.ts +2 -0
  18. package/src/adapters/claude-cli/adapter.ts +111 -23
  19. package/src/adapters/claude-cli/profiles.ts +2 -1
  20. package/src/adapters/claude-cli/stable-replay.ts +108 -0
  21. package/src/adapters/claude-cli/usage-admission.ts +230 -0
  22. package/src/adapters/coding-agent/protocol.ts +1 -1
  23. package/src/adapters/coding-agent/turn.ts +18 -2
  24. package/src/adapters/dsh-account.ts +97 -0
  25. package/src/adapters/google-progress-history.ts +53 -0
  26. package/src/adapters/google.ts +52 -9
  27. package/src/adapters/openai-chat/messages.ts +4 -3
  28. package/src/adapters/openai-chat/passthrough.ts +8 -2
  29. package/src/adapters/openai-chat/reasoning-replay.ts +52 -0
  30. package/src/adapters/openai-responses/compaction-search-history.ts +14 -7
  31. package/src/adapters/openai-responses/passthrough.ts +57 -125
  32. package/src/adapters/openai-responses/request-strips.ts +25 -3
  33. package/src/adapters/openai-responses/transport.ts +81 -0
  34. package/src/adapters/qoder/adapter.ts +4 -2
  35. package/src/adapters/registry.ts +5 -0
  36. package/src/adapters/zed.ts +26 -1
  37. package/src/advisor/activity.ts +55 -0
  38. package/src/advisor/budget.ts +62 -0
  39. package/src/advisor/consult.ts +4 -1
  40. package/src/advisor/context.ts +16 -4
  41. package/src/advisor/evidence-tool.ts +21 -0
  42. package/src/advisor/evidence.ts +117 -0
  43. package/src/advisor/runtime.ts +79 -31
  44. package/src/advisor/settings.ts +11 -2
  45. package/src/bridge/errors.ts +19 -1
  46. package/src/bridge/internal.ts +7 -1
  47. package/src/bridge/response-json.ts +18 -3
  48. package/src/bridge/sse.ts +59 -8
  49. package/src/bridge/terminal-refusal.ts +59 -0
  50. package/src/claude/desktop-3p-library.ts +32 -7
  51. package/src/claude/desktop-3p.ts +55 -13
  52. package/src/claude/desktop-first-party.ts +1 -1
  53. package/src/claude/desktop-picker-platform.ts +4 -0
  54. package/src/claude/desktop-picker.ts +15 -8
  55. package/src/claude/desktop-system-proxy.ts +50 -4
  56. package/src/claude/inbound-content-options.ts +77 -0
  57. package/src/claude/inbound.ts +33 -0
  58. package/src/claude/intercept/picker-ca-startup.ts +2 -1
  59. package/src/claude/intercept/picker-runtime.ts +6 -5
  60. package/src/claude/intercept/picker-trust-windows.ts +338 -0
  61. package/src/claude/intercept/picker-trust.ts +19 -0
  62. package/src/claude/intercept/port-policy.ts +30 -0
  63. package/src/claude/intercept/runtime.ts +97 -94
  64. package/src/claude/outbound.ts +46 -13
  65. package/src/cli/account-api.ts +1 -0
  66. package/src/cli/account-policy-dto.ts +6 -0
  67. package/src/cli/account-policy.ts +34 -5
  68. package/src/cli/account-preservation.ts +44 -0
  69. package/src/cli/account.ts +8 -2
  70. package/src/cli/advisor.ts +17 -3
  71. package/src/cli/capabilities-accounts.ts +20 -1
  72. package/src/cli/capabilities-agents-routing.ts +5 -2
  73. package/src/cli/capabilities-base.ts +38 -10
  74. package/src/cli/capabilities-observe-system.ts +63 -3
  75. package/src/cli/capabilities-provider-models.ts +11 -0
  76. package/src/cli/codex-cli-update.ts +151 -41
  77. package/src/cli/codex-provider.ts +44 -0
  78. package/src/cli/combo-input.ts +10 -1
  79. package/src/cli/combo-result.ts +1 -1
  80. package/src/cli/combo.ts +2 -1
  81. package/src/cli/cross-home-owner.ts +33 -16
  82. package/src/cli/dispatch.ts +25 -4
  83. package/src/cli/doctor.ts +6 -1
  84. package/src/cli/ensure-desktop-cold-start.ts +60 -7
  85. package/src/cli/help.ts +3 -2
  86. package/src/cli/index.ts +1 -1
  87. package/src/cli/integration-plan-dto.ts +1 -0
  88. package/src/cli/log-history.ts +172 -0
  89. package/src/cli/loopback-family.ts +31 -0
  90. package/src/cli/models-runtime-subcommands.ts +1 -0
  91. package/src/cli/models-runtime.ts +72 -8
  92. package/src/cli/observe-stream.ts +2 -0
  93. package/src/cli/observe.ts +16 -4
  94. package/src/cli/provider.ts +27 -5
  95. package/src/cli/registry.ts +28 -8
  96. package/src/cli/runtime-api.ts +5 -3
  97. package/src/cli/system-command.ts +40 -8
  98. package/src/cli/uninstall-client-state.ts +32 -0
  99. package/src/cli/usage-compare.ts +309 -0
  100. package/src/cli/usage-model-search.ts +4 -4
  101. package/src/cli/usage-report.ts +2 -2
  102. package/src/cli/v2.ts +1 -1
  103. package/src/client/machine-listener.ts +1 -3
  104. package/src/client/runtime.ts +1 -0
  105. package/src/clients/client-path.ts +51 -0
  106. package/src/clients/config-export/contracts.ts +2 -1
  107. package/src/clients/config-export.ts +18 -44
  108. package/src/clients/gentle-shell-path.ts +87 -0
  109. package/src/codex/account-auto-switch.ts +2 -0
  110. package/src/codex/account-identity.ts +29 -0
  111. package/src/codex/account-label.ts +1 -1
  112. package/src/codex/account-lifecycle.ts +21 -5
  113. package/src/codex/account-preservation.ts +28 -0
  114. package/src/codex/account-use-remaining.ts +23 -0
  115. package/src/codex/app-server-processes.ts +97 -7
  116. package/src/codex/app-server-restart-service.ts +55 -4
  117. package/src/codex/auth-api/account-list.ts +4 -0
  118. package/src/codex/auth-api/account-pause-group.ts +6 -50
  119. package/src/codex/auth-api/account-preservation.ts +57 -0
  120. package/src/codex/auth-api/routes.ts +8 -0
  121. package/src/codex/auth-api/use-remaining-route.ts +36 -0
  122. package/src/codex/auth-api.ts +0 -1
  123. package/src/codex/auth-context.ts +2 -2
  124. package/src/codex/catalog/auto-review.ts +20 -3
  125. package/src/codex/catalog/build-entries.ts +9 -1
  126. package/src/codex/catalog/combo-member.ts +14 -5
  127. package/src/codex/catalog/configured-tiers.ts +1 -1
  128. package/src/codex/catalog/gather-capture.ts +2 -0
  129. package/src/codex/catalog/metadata.ts +11 -9
  130. package/src/codex/catalog/model-hints.ts +21 -16
  131. package/src/codex/catalog/model-visibility.ts +3 -1
  132. package/src/codex/catalog/parsing.ts +30 -86
  133. package/src/codex/catalog/provider-models.ts +36 -34
  134. package/src/codex/catalog/routed-gather.ts +14 -13
  135. package/src/codex/cli-install-provenance.ts +105 -17
  136. package/src/codex/cli-update-plan.ts +545 -0
  137. package/src/codex/config-write-lock.ts +23 -2
  138. package/src/codex/desktop-app/picker-freshness.ts +96 -0
  139. package/src/codex/desktop-app-restart.ts +21 -0
  140. package/src/codex/desktop-compatibility/installed-build.ts +24 -6
  141. package/src/codex/desktop-compatibility/routing-preflight.ts +1 -1
  142. package/src/codex/desktop-compatibility/runtime.ts +12 -3
  143. package/src/codex/desktop-compatibility/windows-key-protection.ts +115 -9
  144. package/src/codex/features.ts +85 -45
  145. package/src/codex/inject/history-failure.ts +27 -0
  146. package/src/codex/inject/restore.ts +24 -2
  147. package/src/codex/inject/routing-classify.ts +4 -3
  148. package/src/codex/inject.ts +18 -27
  149. package/src/codex/main-account-hard-lock.ts +30 -1
  150. package/src/codex/native-profile-startup.ts +1 -1
  151. package/src/codex/ocx-compaction-history.ts +12 -10
  152. package/src/codex/paths.ts +86 -1
  153. package/src/codex/provider-routing.ts +174 -0
  154. package/src/codex/refresh.ts +2 -2
  155. package/src/codex/routing/health-store.ts +1 -1
  156. package/src/codex/routing/selection.ts +4 -3
  157. package/src/codex/routing/thread-affinity.ts +1 -1
  158. package/src/codex/signed-in-identity.ts +43 -0
  159. package/src/codex/subagent-model-fallback.ts +1 -1
  160. package/src/codex/sync.ts +6 -1
  161. package/src/codex/user-identity.ts +16 -5
  162. package/src/codex/websocket-activity.ts +161 -0
  163. package/src/codex/websocket-registry.ts +25 -1
  164. package/src/codex/windows-local-appdata.ts +84 -0
  165. package/src/combos/failover.ts +6 -1
  166. package/src/combos/index.ts +1 -0
  167. package/src/combos/resolve.ts +46 -10
  168. package/src/combos/types.ts +15 -0
  169. package/src/config/diagnostics.ts +33 -6
  170. package/src/config/live-reconcile.ts +38 -2
  171. package/src/config/load-degrade.ts +17 -0
  172. package/src/config/opencode-wire-upgrade.ts +54 -0
  173. package/src/config/opencode-zen-upgrade.ts +53 -0
  174. package/src/config/owner-registry.ts +22 -17
  175. package/src/config/provider-validation.ts +45 -2
  176. package/src/config/schema/config-schema.ts +22 -0
  177. package/src/config/schema/leaf-validators.ts +38 -9
  178. package/src/config/schema/main-account-hard-lock-fallback.ts +26 -0
  179. package/src/config/schema/native-reasoning-retention.ts +26 -0
  180. package/src/config.ts +2 -0
  181. package/src/generated/compatibility-version.json +657 -297
  182. package/src/grok/sync.ts +1 -1
  183. package/src/images/loop.ts +116 -27
  184. package/src/integrations/catalog-refresh.ts +1 -1
  185. package/src/integrations/mutation-plan.ts +1 -0
  186. package/src/integrations/registry.ts +8 -0
  187. package/src/lib/codex-restart-contract.ts +15 -0
  188. package/src/lib/desktop-upstream-tunnel.ts +18 -53
  189. package/src/lib/empty-tool-catalog.ts +62 -0
  190. package/src/lib/errors.ts +19 -3
  191. package/src/lib/gcp-adc.ts +96 -9
  192. package/src/lib/gcp-credential-input.ts +12 -0
  193. package/src/lib/lab-live-route-production.ts +5 -0
  194. package/src/lib/opaque-proxy-tunnel.ts +217 -0
  195. package/src/lib/redact.ts +2 -2
  196. package/src/lib/request-failure-model.ts +2 -0
  197. package/src/oauth/account-quota-rank.ts +69 -2
  198. package/src/oauth/anthropic-account-refusal.ts +20 -2
  199. package/src/oauth/anthropic-oauth-definitions.ts +3 -1
  200. package/src/oauth/anthropic-routing.ts +1 -21
  201. package/src/oauth/anthropic.ts +38 -7
  202. package/src/oauth/callback-server.ts +27 -1
  203. package/src/oauth/chatgpt-device.ts +1 -1
  204. package/src/oauth/chatgpt-shared.ts +83 -0
  205. package/src/oauth/chatgpt.ts +8 -72
  206. package/src/oauth/dsh.ts +203 -0
  207. package/src/oauth/generic-account-failover.ts +40 -11
  208. package/src/oauth/generic-session-affinity.ts +125 -0
  209. package/src/oauth/index.ts +184 -109
  210. package/src/oauth/local-token-detect.ts +21 -1
  211. package/src/oauth/login-cli-errors.ts +44 -0
  212. package/src/oauth/login-cli.ts +69 -7
  213. package/src/oauth/login-flow-state.ts +371 -23
  214. package/src/oauth/meta-muse-errors.ts +41 -0
  215. package/src/oauth/meta-muse-keychain.ts +52 -0
  216. package/src/oauth/meta-muse.ts +51 -84
  217. package/src/oauth/model-discovery-auth.ts +3 -0
  218. package/src/oauth/pool-settings-capability.ts +4 -2
  219. package/src/oauth/refresh-flight-retirement.ts +48 -0
  220. package/src/oauth/session-affinity-key.ts +23 -0
  221. package/src/oauth/store-anthropic-instance.ts +9 -2
  222. package/src/oauth/store.ts +12 -5
  223. package/src/protocols/encoders/adapter-events.ts +6 -5
  224. package/src/protocols/encoders/messages.ts +9 -6
  225. package/src/protocols/provider-summary.ts +2 -1
  226. package/src/providers/api-key-cooldowns.ts +49 -0
  227. package/src/providers/api-key-resolve.ts +11 -6
  228. package/src/providers/api-key-response-ownership.ts +57 -0
  229. package/src/providers/api-key-selection.ts +31 -6
  230. package/src/providers/api-keys.ts +214 -3
  231. package/src/providers/claude-cli-usage.ts +96 -0
  232. package/src/providers/dsh-account-destination.ts +31 -0
  233. package/src/providers/dsh-account-models.ts +41 -0
  234. package/src/providers/gcp-credential-cleanup.ts +59 -0
  235. package/src/providers/key-failover.ts +54 -50
  236. package/src/providers/key-store.ts +7 -2
  237. package/src/providers/model-rename-fields.ts +3 -0
  238. package/src/providers/model-rename-migration.ts +2 -2
  239. package/src/providers/openai-tiers-destination.ts +11 -2
  240. package/src/providers/openai-tiers.ts +5 -12
  241. package/src/providers/opencode-go-transport.ts +3 -1
  242. package/src/providers/opencode-zen-rate-limit.ts +7 -2
  243. package/src/providers/quota/account-cache.ts +6 -5
  244. package/src/providers/quota/account-row.ts +34 -0
  245. package/src/providers/quota/antigravity.ts +28 -7
  246. package/src/providers/quota/dsh-account.ts +70 -0
  247. package/src/providers/quota/vendor-probes-oauth.ts +1 -93
  248. package/src/providers/quota-types.ts +2 -0
  249. package/src/providers/quota.ts +14 -20
  250. package/src/providers/registry/entries-core.ts +37 -8
  251. package/src/providers/registry/types.ts +1 -1
  252. package/src/providers/request-pacing.ts +77 -19
  253. package/src/providers/routed-config.ts +339 -0
  254. package/src/providers/service-tier.ts +2 -1
  255. package/src/quota/window-mapping.ts +5 -2
  256. package/src/responses/anthropic-progress.ts +34 -0
  257. package/src/responses/assistant-replay.ts +35 -0
  258. package/src/responses/compaction-history-framing.ts +65 -0
  259. package/src/responses/compaction.ts +6 -1
  260. package/src/responses/incomplete-reason.ts +31 -0
  261. package/src/responses/parser.ts +50 -5
  262. package/src/responses/reasoning-envelope.ts +4 -0
  263. package/src/responses/reasoning-replay-cache.ts +74 -6
  264. package/src/responses/reasoning-retention.ts +173 -0
  265. package/src/responses/retained-compaction.ts +46 -0
  266. package/src/responses/schema.ts +23 -0
  267. package/src/router.ts +8 -321
  268. package/src/routing/analytics.ts +7 -15
  269. package/src/routing/health.ts +37 -22
  270. package/src/routing/history/indexer.ts +6 -5
  271. package/src/routing/history/ingest.ts +6 -2
  272. package/src/routing/profile.ts +1 -1
  273. package/src/server/admission-model-scope.ts +17 -0
  274. package/src/server/auth-cors.ts +11 -0
  275. package/src/server/chat-completions.ts +7 -1
  276. package/src/server/chat-native-reasoning.ts +123 -0
  277. package/src/server/chat-native-sse.ts +110 -7
  278. package/src/server/chat-native.ts +76 -17
  279. package/src/server/claude-messages.ts +56 -414
  280. package/src/server/effort-policy.ts +1 -1
  281. package/src/server/images.ts +15 -2
  282. package/src/server/index/live-sideband.ts +3 -2
  283. package/src/server/index/model-alias-id.ts +36 -0
  284. package/src/server/index/optional-listeners.ts +2 -0
  285. package/src/server/index/serve-options.ts +20 -6
  286. package/src/server/index/startup-warnings.ts +2 -0
  287. package/src/server/index/websocket-handler.ts +18 -4
  288. package/src/server/index.ts +8 -8
  289. package/src/server/inference/advisor-diagnostics.ts +31 -0
  290. package/src/server/lifecycle.ts +13 -1
  291. package/src/server/management/advisor-routes.ts +15 -4
  292. package/src/server/management/claude-desktop-picker-routes.ts +1 -1
  293. package/src/server/management/combo-routes.ts +176 -50
  294. package/src/server/management/companion-routes.ts +1 -1
  295. package/src/server/management/config-routes.ts +75 -4
  296. package/src/server/management/cursor-integration-routes.ts +1 -1
  297. package/src/server/management/model-routes.ts +11 -6
  298. package/src/server/management/model-rows.ts +14 -0
  299. package/src/server/management/native-integration-routes.ts +2 -7
  300. package/src/server/management/native-reasoning-retention-routes.ts +41 -0
  301. package/src/server/management/oauth-account-routes.ts +87 -14
  302. package/src/server/management/provider-gcp-credential-routes.ts +29 -0
  303. package/src/server/management/provider-key-pacing-routes.ts +31 -0
  304. package/src/server/management/provider-overwrite-carry.ts +7 -0
  305. package/src/server/management/provider-routes.ts +25 -30
  306. package/src/server/management/route-registry.ts +10 -1
  307. package/src/server/management-api.ts +3 -1
  308. package/src/server/messages-native-tool-guard.ts +185 -0
  309. package/src/server/messages-native.ts +117 -24
  310. package/src/server/messages-shared.ts +431 -0
  311. package/src/server/relay.ts +1 -1
  312. package/src/server/request-log-terminal-status.ts +1 -1
  313. package/src/server/request-log.ts +6 -1
  314. package/src/server/responses/account-change-state.ts +8 -2
  315. package/src/server/responses/adapter-continuation.ts +47 -19
  316. package/src/server/responses/adapter-delivery.ts +72 -12
  317. package/src/server/responses/adapter-dispatch.ts +46 -3
  318. package/src/server/responses/advisor-retention.ts +117 -0
  319. package/src/server/responses/advisor-slot.ts +73 -41
  320. package/src/server/responses/anthropic-thinking-recovery.ts +153 -0
  321. package/src/server/responses/codex-ws-wire.ts +15 -8
  322. package/src/server/responses/combo-stream-preflight.ts +3 -2
  323. package/src/server/responses/compact.ts +303 -125
  324. package/src/server/responses/compaction-recovery.ts +20 -10
  325. package/src/server/responses/compaction-retention.ts +61 -0
  326. package/src/server/responses/context-overflow.ts +11 -6
  327. package/src/server/responses/core-auth.ts +6 -0
  328. package/src/server/responses/core-codex-account.ts +11 -1
  329. package/src/server/responses/core-combo-failure.ts +2 -0
  330. package/src/server/responses/core-combo.ts +121 -36
  331. package/src/server/responses/core-errors.ts +3 -3
  332. package/src/server/responses/core-lifetime.ts +8 -4
  333. package/src/server/responses/core-opaque-recovery.ts +35 -0
  334. package/src/server/responses/core-options.ts +7 -0
  335. package/src/server/responses/core-pipeline.ts +141 -0
  336. package/src/server/responses/core-replay.ts +23 -3
  337. package/src/server/responses/core.ts +2 -115
  338. package/src/server/responses/fetch-helpers.ts +73 -19
  339. package/src/server/responses/main-hard-lock-routing.ts +109 -0
  340. package/src/server/responses/native-response-control.ts +4 -2
  341. package/src/server/responses/passthrough-delivery.ts +3 -1
  342. package/src/server/responses/passthrough-dispatch.ts +15 -13
  343. package/src/server/responses/passthrough-execution.ts +3 -2
  344. package/src/server/responses/request-prepare.ts +77 -12
  345. package/src/server/responses/request-sidecar-auth.ts +2 -2
  346. package/src/server/responses/request-transport.ts +104 -27
  347. package/src/server/responses/response-effects.ts +2 -0
  348. package/src/server/responses/response-tool-ownership.ts +21 -0
  349. package/src/server/responses/run-turn-execution.ts +96 -30
  350. package/src/server/responses/sidecar-execution.ts +63 -9
  351. package/src/server/responses/sidecar-send-budget.ts +2 -2
  352. package/src/server/responses/terminal-guard.ts +2 -16
  353. package/src/server/responses-undeclared-tool-guard.ts +1 -1
  354. package/src/server/ws-bridge.ts +2 -0
  355. package/src/service/auth-preflight.ts +71 -0
  356. package/src/service/cli.ts +4 -2
  357. package/src/service/definition.ts +97 -0
  358. package/src/service/guards.ts +1 -61
  359. package/src/service/health-sentinel-linux.ts +112 -0
  360. package/src/service/health-sentinel-main.ts +76 -57
  361. package/src/service/health.ts +5 -89
  362. package/src/service/launchd.ts +27 -7
  363. package/src/service/logging.ts +72 -9
  364. package/src/service/repair.ts +2 -1
  365. package/src/service/state.ts +7 -8
  366. package/src/service/systemd.ts +1 -1
  367. package/src/service/windows-guarded-manager.ts +2 -1
  368. package/src/service/windows-ops.ts +1 -1
  369. package/src/service/windows-scheduler.ts +2 -7
  370. package/src/service/windows-taskxml.ts +2 -2
  371. package/src/service/windows-wscript.ts +14 -0
  372. package/src/service.ts +4 -2
  373. package/src/sidecar/auth.ts +2 -0
  374. package/src/types/config.ts +64 -3
  375. package/src/types/opencode-go.ts +61 -0
  376. package/src/types/opencode-zen.ts +35 -0
  377. package/src/types/provider.ts +32 -6
  378. package/src/types/request.ts +31 -2
  379. package/src/types/wire.ts +31 -37
  380. package/src/types.ts +2 -0
  381. package/src/update/npm-invocation.mjs +12 -2
  382. package/src/usage/cost.ts +48 -12
  383. package/src/usage/expected-prices.ts +3 -0
  384. package/src/usage/ledger-retention.ts +2 -1
  385. package/src/usage/ledger-scanner.ts +6 -5
  386. package/src/usage/log.ts +17 -8
  387. package/src/usage/summary.ts +30 -11
  388. package/src/usage/telemetry-contract.ts +1 -0
  389. package/src/usage/user-cost-overlays.ts +14 -19
  390. package/src/usage/user-cost-validation.ts +38 -0
  391. package/src/vision/eligibility.ts +13 -0
  392. package/src/web-search/anthropic-history.ts +59 -0
  393. package/src/web-search/loop.ts +236 -73
  394. package/src/web-search/progress-stream.ts +15 -2
  395. package/src/web-search/run-turn-loop.ts +9 -1
  396. package/gui/dist/assets/App-BjlttVLy.js +0 -58
  397. package/gui/dist/assets/App-CSJvmjNs.css +0 -1
  398. package/gui/dist/assets/index-BB6uK6tU.css +0 -1
  399. package/gui/dist/assets/index-BMssP4k3.js +0 -94
  400. package/gui/dist/assets/quota-summary-D8M1bc_R.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";
@@ -351,7 +352,7 @@ export function rotateAntigravityAccountOnAuthRefusal(
351
352
  }
352
353
 
353
354
  /** Generic pool strategies the kernel can actually run. `quota` IS the pre-kernel path. */
354
- type ActiveGenericStrategy = "round-robin" | "fill-first" | "least-loaded";
355
+ type ActiveGenericStrategy = "round-robin" | "fill-first" | "reset-first" | "least-loaded";
355
356
 
356
357
  /** Matches the Codex and Anthropic pools; the DTO still reports `null` for "not stored". */
357
358
  const DEFAULT_GENERIC_AUTO_SWITCH_THRESHOLD = 80;
@@ -365,10 +366,13 @@ const DEFAULT_GENERIC_AUTO_SWITCH_THRESHOLD = 80;
365
366
  * branch instead of a four-way one.
366
367
  */
367
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.
368
372
  if (config.pool?.kernel !== true) return null;
369
373
  const raw = config.providers?.[providerName]?.oauthAccountFailover?.strategy;
370
374
  if (providerName === "kiro" && raw === "least-loaded") return raw;
371
- return raw === "round-robin" || raw === "fill-first" ? raw : null;
375
+ return raw === "round-robin" || raw === "fill-first" || raw === "reset-first" ? raw : null;
372
376
  }
373
377
 
374
378
  function genericStickyLimit(config: OcxConfig, providerName: string): number {
@@ -416,6 +420,7 @@ function pickFillFirstGenericAccount(
416
420
  now: number,
417
421
  requestedModelId?: string | null,
418
422
  accountRows?: ReadonlyMap<string, ProviderAccount>,
423
+ preferBelowThreshold = false,
419
424
  ): string | null {
420
425
  const stableAll = stableGenericRoster(providerName);
421
426
  if (stableAll.length < 2) return null;
@@ -431,6 +436,11 @@ function pickFillFirstGenericAccount(
431
436
  }
432
437
  const start = activeId ? stableAll.indexOf(activeId) : -1;
433
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
+ }
434
444
  for (const id of ring) {
435
445
  if (id !== activeId && eligible.has(id)) return id;
436
446
  }
@@ -473,7 +483,8 @@ export function noteGenericPoolSelection(
473
483
  * Cool the account that actually 429'd and name the next eligible one, or null.
474
484
  *
475
485
  * Returns the id only; the caller mints the credential so a failed refresh does not leave the
476
- * cooldown applied to an account we then could not use.
486
+ * cooldown applied to an account we then could not use. `commit: false` still cools the failed
487
+ * account but only peeks the round-robin ring, for callers that will not dispatch the answer.
477
488
  */
478
489
  export function rotateGenericOAuthAccountOn429(
479
490
  config: OcxConfig,
@@ -482,9 +493,10 @@ export function rotateGenericOAuthAccountOn429(
482
493
  retryAfterHeader: string | null | undefined,
483
494
  now = Date.now(),
484
495
  requestedModelId?: string | null,
496
+ commit = true,
485
497
  ): string | null {
486
498
  return rotateGenericOAuthAccountOnRefusal(config, providerName, failedAccountId, "rate",
487
- retryAfterHeader, now, requestedModelId);
499
+ retryAfterHeader, now, requestedModelId, undefined, commit);
488
500
  }
489
501
 
490
502
  /** Kiro adds account-scoped refusal classes; all other providers retain their 429 policy. */
@@ -497,6 +509,7 @@ export function rotateGenericOAuthAccountOnRefusal(
497
509
  now = Date.now(),
498
510
  requestedModelId?: string | null,
499
511
  monthlyCooldownMs?: number,
512
+ commit = true,
500
513
  ): string | null {
501
514
  if (kind === "other" || (providerName !== "kiro" && kind !== "rate")) return null;
502
515
  if (!isGenericOAuthFailoverEnabled(config, providerName)) return null;
@@ -556,8 +569,8 @@ export function rotateGenericOAuthAccountOnRefusal(
556
569
  }
557
570
  if (strategy === "round-robin") {
558
571
  // PICK here, not peek: the failure already happened and this answer is the one being used,
559
- // so the ring genuinely advances.
560
- return pickRoundRobinAccount(
572
+ // so the ring genuinely advances. A caller that only asks whether a sibling exists peeks.
573
+ return (commit ? pickRoundRobinAccount : peekRoundRobinAccount)(
561
574
  genericPoolKey(providerName),
562
575
  candidates,
563
576
  genericStickyLimit(config, providerName),
@@ -574,6 +587,10 @@ export function rotateGenericOAuthAccountOnRefusal(
574
587
  }
575
588
  return null;
576
589
  }
590
+ if (strategy === "reset-first") {
591
+ return rankAccountsByResetFirst(providerName, candidates, requestedModelId, now,
592
+ new Map(set.accounts.map(account => [account.id, account])))[0] ?? null;
593
+ }
577
594
  // With no quota evidence this returns the ring untouched, so providers without
578
595
  // per-account quota keep exactly the traversal they have today.
579
596
  return rankAccountsByHeadroom(providerName, candidates, requestedModelId,
@@ -634,6 +651,7 @@ export function preferredInitialAccount(
634
651
  providerName: string,
635
652
  now = Date.now(),
636
653
  requestedModelId?: string | null,
654
+ activeAccountOverride?: string,
637
655
  ): string | null {
638
656
  // The PROACTIVE predicate, not the reactive one: this steers a request upstream has not
639
657
  // refused, so `oauthAccountFailover.enabled: false` must still be able to refuse it.
@@ -642,7 +660,7 @@ export function preferredInitialAccount(
642
660
  // active id separately would delay manual selection and account removal.
643
661
  const selected = getAccountSet(providerName);
644
662
  if (!selected) return null;
645
- const active = selected.activeAccountId;
663
+ const active = activeAccountOverride ?? selected.activeAccountId;
646
664
  const accountRows = new Map(selected.accounts.map(account => [account.id, account]));
647
665
  const order = selected.accounts
648
666
  .filter(account => account.paused !== true && account.needsReauth !== true)
@@ -694,9 +712,20 @@ export function preferredInitialAccount(
694
712
  return picked && picked !== active ? picked : null;
695
713
  }
696
714
  if (strategy === "fill-first") {
697
- const picked = pickFillFirstGenericAccount(config, providerName, active, now, requestedModelId, accountRows);
715
+ const picked = pickFillFirstGenericAccount(config, providerName, active, now, requestedModelId, accountRows, activeAccountOverride !== undefined);
698
716
  return picked && picked !== active ? picked : null;
699
717
  }
718
+ if (strategy === "reset-first") {
719
+ // Same candidate set as round-robin: paused, reauth-needed and cooled accounts are out,
720
+ // and an account whose model listing omits the requested model loses to one that has it.
721
+ // Reset ordering needs quota evidence; without it the ordinary active-account path runs.
722
+ const family = classifyModelFamilyForQuota(providerName, requestedModelId);
723
+ const eligibleNow = preferKiroModelSupport(providerName,
724
+ eligibleFailoverAccounts(providerName, now, family), requestedModelId, config);
725
+ if (eligibleNow.length === 0 || !hasHeadroomEvidence(providerName, eligibleNow, requestedModelId, accountRows)) return null;
726
+ const best = rankAccountsByResetFirst(providerName, eligibleNow, requestedModelId, now, accountRows)[0] ?? null;
727
+ return best && best !== active ? best : null;
728
+ }
700
729
 
701
730
  const activeRow = selected.accounts.find(account => account.id === active);
702
731
  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
+ }