@bitkyc08/opencodex 2.83.0 → 2.86.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 (405) hide show
  1. package/README.md +0 -4
  2. package/bin/ocx.mjs +61 -3
  3. package/gui/dist/assets/App-BjlttVLy.js +58 -0
  4. package/gui/dist/assets/Tray-DyR9yBee.js +1 -0
  5. package/gui/dist/assets/{index-BAbcj51J.css → index-BB6uK6tU.css} +1 -1
  6. package/gui/dist/assets/index-BMssP4k3.js +94 -0
  7. package/gui/dist/assets/quota-summary-D8M1bc_R.js +1 -0
  8. package/gui/dist/index.html +2 -2
  9. package/package.json +2 -3
  10. package/src/adapters/anthropic/beta-allowlist.ts +87 -8
  11. package/src/adapters/anthropic/client-identity.ts +1 -1
  12. package/src/adapters/anthropic/passthrough.ts +33 -4
  13. package/src/adapters/azure.ts +19 -1
  14. package/src/adapters/client-fingerprint.ts +24 -0
  15. package/src/adapters/coding-agent/protocol.ts +15 -8
  16. package/src/adapters/coding-agent/turn.ts +22 -9
  17. package/src/adapters/cursor/arg-codec.ts +17 -5
  18. package/src/adapters/cursor/catalog.ts +8 -0
  19. package/src/adapters/cursor/effort-map.ts +3 -0
  20. package/src/adapters/cursor/freeform-args.ts +75 -0
  21. package/src/adapters/cursor/protobuf-events.ts +36 -23
  22. package/src/adapters/google-antigravity-wire.ts +22 -1
  23. package/src/adapters/google.ts +2 -2
  24. package/src/adapters/ollama-native.ts +29 -1
  25. package/src/adapters/openai-chat/messages.ts +8 -4
  26. package/src/adapters/openai-chat/response-events.ts +59 -1
  27. package/src/adapters/openai-chat.ts +8 -5
  28. package/src/adapters/openai-responses/empty-assistant.ts +32 -0
  29. package/src/adapters/openai-responses/passthrough.ts +3 -0
  30. package/src/adapters/openai-responses/request-strips.ts +10 -2
  31. package/src/adapters/qoder/adapter.ts +56 -15
  32. package/src/advisor/consult.ts +220 -0
  33. package/src/advisor/context.ts +226 -0
  34. package/src/advisor/disclosure.ts +20 -0
  35. package/src/advisor/runtime.ts +305 -0
  36. package/src/advisor/settings.ts +135 -0
  37. package/src/advisor/state.ts +369 -0
  38. package/src/advisor/synthetic-tool.ts +33 -0
  39. package/src/bridge/errors.ts +24 -0
  40. package/src/bridge.ts +1 -1
  41. package/src/chat/inbound.ts +30 -6
  42. package/src/chat/outbound.ts +5 -0
  43. package/src/claude/context-windows.ts +54 -1
  44. package/src/claude/desktop-3p-library.ts +43 -3
  45. package/src/claude/desktop-3p.ts +22 -8
  46. package/src/claude/desktop-picker-profile.ts +11 -4
  47. package/src/claude/desktop-profile.ts +10 -1
  48. package/src/claude/desktop-remote-store-io.ts +5 -3
  49. package/src/claude/inbound.ts +121 -50
  50. package/src/claude/intercept/connect-proxy.ts +10 -3
  51. package/src/claude/intercept/local-ca.ts +8 -1
  52. package/src/claude/intercept/picker-ca-persistence.ts +3 -1
  53. package/src/claude/intercept/picker-listener.ts +49 -1
  54. package/src/claude/intercept/settings-no-proxy.ts +146 -0
  55. package/src/claude/intercept/settings.ts +101 -9
  56. package/src/claude/model-default-settings.ts +154 -0
  57. package/src/claude/model-info.ts +7 -7
  58. package/src/claude/outbound.ts +13 -6
  59. package/src/cli/account-api.ts +65 -0
  60. package/src/cli/account-auth.ts +26 -2
  61. package/src/cli/account.ts +41 -5
  62. package/src/cli/advisor.ts +151 -0
  63. package/src/cli/auxiliary-listener-preflight.ts +37 -0
  64. package/src/cli/capabilities-access-remote.ts +8 -0
  65. package/src/cli/capabilities-agents-routing.ts +18 -0
  66. package/src/cli/capabilities-base.ts +14 -2
  67. package/src/cli/catalog-command-result.ts +2 -1
  68. package/src/cli/chatgpt-command.ts +35 -14
  69. package/src/cli/claude.ts +60 -11
  70. package/src/cli/config-command.ts +8 -2
  71. package/src/cli/debug.ts +8 -0
  72. package/src/cli/dispatch.ts +21 -4
  73. package/src/cli/doctor.ts +9 -0
  74. package/src/cli/ensure-desktop-cold-start.ts +329 -0
  75. package/src/cli/export-command.ts +15 -2
  76. package/src/cli/help.ts +2 -1
  77. package/src/cli/hub-disable.ts +99 -0
  78. package/src/cli/hub.ts +4 -2
  79. package/src/cli/index.ts +26 -8
  80. package/src/cli/integration-plan-dto.ts +2 -0
  81. package/src/cli/link-child.ts +104 -0
  82. package/src/cli/link-enrollment.ts +96 -0
  83. package/src/cli/link.ts +39 -24
  84. package/src/cli/observe-stream.ts +6 -1
  85. package/src/cli/observe.ts +10 -2
  86. package/src/cli/provider.ts +12 -0
  87. package/src/cli/registry.ts +20 -7
  88. package/src/cli/restart-scope.ts +3 -1
  89. package/src/cli/runtime-api.ts +27 -2
  90. package/src/cli/status.ts +104 -20
  91. package/src/client/connect.ts +39 -4
  92. package/src/client/hub-client.ts +14 -7
  93. package/src/client/hub-state.ts +25 -14
  94. package/src/client/link-home.ts +248 -0
  95. package/src/client/link-management-relay.ts +61 -0
  96. package/src/client/link-relay.ts +6 -1
  97. package/src/client/link-status.ts +14 -0
  98. package/src/client/machine-api.ts +14 -7
  99. package/src/client/machine-listener.ts +29 -4
  100. package/src/clients/config-export/contracts.ts +1 -0
  101. package/src/clients/config-export/model-metadata.ts +3 -2
  102. package/src/clients/config-export/qoder.ts +93 -0
  103. package/src/clients/config-export.ts +59 -16
  104. package/src/clients/omo-role-models.ts +6 -3
  105. package/src/codex/agent-role-models.ts +26 -8
  106. package/src/codex/auth-api/account-list.ts +17 -2
  107. package/src/codex/auth-api/account-selection.ts +17 -7
  108. package/src/codex/auth-api/pool-mode-gate.ts +5 -2
  109. package/src/codex/auth-api/routes.ts +5 -1
  110. package/src/codex/auth-context.ts +79 -5
  111. package/src/codex/autostart-health.ts +7 -1
  112. package/src/codex/catalog/build-entries.ts +11 -2
  113. package/src/codex/catalog/bundled.ts +5 -1
  114. package/src/codex/catalog/configured-tiers.ts +50 -0
  115. package/src/codex/catalog/effort.ts +6 -3
  116. package/src/codex/catalog/metadata.ts +4 -2
  117. package/src/codex/catalog/model-hints.ts +21 -3
  118. package/src/codex/catalog/native-models.ts +16 -2
  119. package/src/codex/catalog/native-ultrafast.ts +106 -0
  120. package/src/codex/catalog/parsing.ts +5 -0
  121. package/src/codex/catalog/provider-models.ts +4 -0
  122. package/src/codex/catalog/retained-sync.ts +2 -0
  123. package/src/codex/catalog/routed-gather.ts +22 -4
  124. package/src/codex/catalog/vendor-context.ts +82 -0
  125. package/src/codex/config-write-lock.ts +40 -2
  126. package/src/codex/convergence.ts +2 -0
  127. package/src/codex/desktop-app/types.ts +2 -0
  128. package/src/codex/desktop-app/windows.ts +29 -16
  129. package/src/codex/desktop-app-restart.ts +4 -2
  130. package/src/codex/desktop-compatibility/certificate-service.ts +99 -0
  131. package/src/codex/desktop-compatibility/certificate-store.ts +193 -0
  132. package/src/codex/desktop-compatibility/connection-store.ts +87 -0
  133. package/src/codex/desktop-compatibility/installed-build.ts +55 -0
  134. package/src/codex/desktop-compatibility/json-body.ts +29 -0
  135. package/src/codex/desktop-compatibility/native-identity.ts +62 -0
  136. package/src/codex/desktop-compatibility/relay-listener.ts +93 -0
  137. package/src/codex/desktop-compatibility/routing-binding.ts +10 -0
  138. package/src/codex/desktop-compatibility/routing-preflight.ts +69 -0
  139. package/src/codex/desktop-compatibility/runtime-ownership.ts +28 -0
  140. package/src/codex/desktop-compatibility/runtime.ts +173 -0
  141. package/src/codex/desktop-compatibility/service.ts +30 -0
  142. package/src/codex/desktop-compatibility/startup-settings.ts +50 -0
  143. package/src/codex/desktop-compatibility/usage-activation.ts +86 -0
  144. package/src/codex/desktop-compatibility/usage-controlled-fetch.ts +59 -0
  145. package/src/codex/desktop-compatibility/usage-controller.ts +107 -0
  146. package/src/codex/desktop-compatibility/usage-policy.ts +59 -0
  147. package/src/codex/desktop-compatibility/usage-refresh.ts +33 -0
  148. package/src/codex/desktop-compatibility/usage-sse-controller.ts +86 -0
  149. package/src/codex/desktop-compatibility/windows-activation-source.ts +44 -0
  150. package/src/codex/desktop-compatibility/windows-certificate-trust.ts +81 -0
  151. package/src/codex/desktop-compatibility/windows-key-protection.ts +59 -0
  152. package/src/codex/desktop-compatibility/windows-package-command.ts +86 -0
  153. package/src/codex/desktop-compatibility/windows-package-launch.ts +28 -0
  154. package/src/codex/history-provider.ts +1 -1
  155. package/src/codex/inject/config-write-section.ts +4 -1
  156. package/src/codex/inject/remove.ts +18 -6
  157. package/src/codex/journal.ts +16 -1
  158. package/src/codex/main-account.ts +99 -15
  159. package/src/codex/native-main-refresh-mode.ts +217 -0
  160. package/src/codex/native-profile-startup.ts +4 -1
  161. package/src/codex/prompt-journal.ts +20 -2
  162. package/src/codex/prompt-layers/encoding.ts +49 -0
  163. package/src/codex/prompt-layers/import-source.ts +30 -0
  164. package/src/codex/prompt-layers/toml-edit.ts +269 -37
  165. package/src/codex/prompt-layers/toml-read.ts +52 -4
  166. package/src/codex/prompt-layers.ts +383 -20
  167. package/src/codex/prompt-lock-claim.ts +13 -3
  168. package/src/codex/prompt-lock-evidence.ts +3 -3
  169. package/src/codex/prompt-lock-owner.ts +14 -6
  170. package/src/codex/prompt-text-probe.ts +34 -5
  171. package/src/codex/quota.ts +13 -6
  172. package/src/codex/role-auto-assign.ts +2 -5
  173. package/src/codex/routing/active-account.ts +21 -0
  174. package/src/codex/routing/health-store.ts +1 -1
  175. package/src/codex/routing/pool-recovery.ts +79 -0
  176. package/src/codex/routing.ts +1 -1
  177. package/src/codex/runtime.ts +196 -8
  178. package/src/codex/shim-diagnostics.ts +6 -0
  179. package/src/codex/shim-overlay.ts +15 -6
  180. package/src/codex/shim.ts +1 -1
  181. package/src/combos/failover.ts +3 -0
  182. package/src/config/api-key-reconcile.ts +34 -0
  183. package/src/config/diagnostics.ts +18 -1
  184. package/src/config/feature-flags.ts +5 -0
  185. package/src/config/live-reconcile.ts +56 -29
  186. package/src/config/live-topology.ts +29 -0
  187. package/src/config/load-degrade.ts +8 -2
  188. package/src/config/paths.ts +13 -3
  189. package/src/config/salvage.ts +3 -1
  190. package/src/config/schema/config-schema.ts +19 -0
  191. package/src/config/schema/desktop-compatibility.ts +9 -0
  192. package/src/config/schema/leaf-validators.ts +1 -0
  193. package/src/generated/compatibility-version.json +713 -285
  194. package/src/generated/model-metadata.ts +1 -1
  195. package/src/grok/grpc-web.ts +23 -1
  196. package/src/grok/reset-coupon-ledger.ts +75 -5
  197. package/src/grok/reset-coupons.ts +4 -2
  198. package/src/integrations/catalog-refresh.ts +1 -1
  199. package/src/integrations/config-io.ts +20 -2
  200. package/src/integrations/journal.ts +15 -1
  201. package/src/integrations/mutation-plan.ts +14 -7
  202. package/src/integrations/registry.ts +52 -11
  203. package/src/integrations/state.ts +19 -7
  204. package/src/integrations/store.ts +3 -0
  205. package/src/integrations/target.ts +42 -5
  206. package/src/integrations/writer.ts +24 -7
  207. package/src/lib/advisor-activation.ts +16 -0
  208. package/src/lib/app-owned-memory-stores.ts +4 -0
  209. package/src/lib/app-owned-memory.ts +11 -0
  210. package/src/lib/background-work.ts +65 -0
  211. package/src/lib/bounded-file-read.ts +58 -0
  212. package/src/lib/bun-path-runtime.d.mts +10 -1
  213. package/src/lib/bun-path-runtime.mjs +62 -3
  214. package/src/lib/desktop-proxy-route.ts +30 -0
  215. package/src/lib/desktop-upstream-tunnel.ts +107 -0
  216. package/src/lib/errors.ts +7 -0
  217. package/src/lib/json-byte-size.ts +31 -0
  218. package/src/lib/local-internal-call-capability.ts +55 -0
  219. package/src/lib/optional-shutdown-hooks.ts +35 -0
  220. package/src/lib/outbound-credential-registry.ts +210 -0
  221. package/src/lib/package-tree-integrity.ts +2 -2
  222. package/src/lib/passive-read.ts +35 -0
  223. package/src/lib/shadow-call.ts +44 -5
  224. package/src/lib/socks5-fetch.ts +48 -71
  225. package/src/lib/socks5-handshake.ts +112 -0
  226. package/src/lib/translator-budget.ts +7 -1
  227. package/src/link/codex-settings.ts +155 -0
  228. package/src/link/management-routes.ts +89 -0
  229. package/src/link/remote-probe.ts +38 -0
  230. package/src/link/ssh-argv.ts +26 -5
  231. package/src/link/ssh-runner.ts +58 -27
  232. package/src/link/status-projection.ts +22 -4
  233. package/src/link/supervisor.ts +144 -49
  234. package/src/link/tunnel-state.ts +11 -4
  235. package/src/oauth/anthropic-continuity.ts +4 -3
  236. package/src/oauth/anthropic-identity.ts +13 -2
  237. package/src/oauth/anthropic-model-quota.ts +13 -1
  238. package/src/oauth/anthropic-routing.ts +230 -23
  239. package/src/oauth/anthropic.ts +18 -4
  240. package/src/oauth/generic-account-failover.ts +29 -5
  241. package/src/oauth/google-antigravity.ts +24 -7
  242. package/src/oauth/index.ts +28 -17
  243. package/src/oauth/kiro-account-identity.ts +106 -0
  244. package/src/oauth/kiro-device-login.ts +2 -1
  245. package/src/oauth/kiro.ts +55 -6
  246. package/src/oauth/plan.ts +29 -0
  247. package/src/oauth/store.ts +40 -3
  248. package/src/oauth/types.ts +2 -0
  249. package/src/plugins/loader.ts +11 -0
  250. package/src/protocols/codecs/chat-ir.ts +467 -0
  251. package/src/protocols/codecs/chat.ts +10 -1
  252. package/src/protocols/codecs/messages-ir.ts +500 -0
  253. package/src/protocols/features.ts +10 -2
  254. package/src/protocols/path.ts +11 -1
  255. package/src/protocols/plan-snapshot.ts +39 -16
  256. package/src/protocols/plan.ts +9 -1
  257. package/src/protocols/settings.ts +8 -0
  258. package/src/providers/anthropic-billing-ownership.ts +74 -0
  259. package/src/providers/anthropic-billing-recovery.ts +31 -0
  260. package/src/providers/anthropic-billing-refusal.ts +55 -0
  261. package/src/providers/anthropic-reset-admission.ts +62 -0
  262. package/src/providers/anthropic-reset-grant-ledger.ts +28 -6
  263. package/src/providers/antigravity-account-roster.ts +102 -0
  264. package/src/providers/antigravity-models.ts +85 -7
  265. package/src/providers/api-key-resolve.ts +17 -2
  266. package/src/providers/key-failover.ts +52 -5
  267. package/src/providers/model-rename-fields.ts +2 -0
  268. package/src/providers/model-rename-migration.ts +5 -3
  269. package/src/providers/model-rename-startup.ts +5 -3
  270. package/src/providers/quota/account-cache.ts +11 -0
  271. package/src/providers/quota/anthropic-account-quota.ts +4 -2
  272. package/src/providers/quota/report-cache.ts +14 -0
  273. package/src/providers/quota/vendor-probes-oauth.ts +3 -3
  274. package/src/providers/quota.ts +4 -1
  275. package/src/providers/registry/entries-core.ts +21 -20
  276. package/src/providers/registry/entries-extended.ts +19 -1
  277. package/src/providers/registry/model-seeds.ts +17 -0
  278. package/src/providers/request-pacing.ts +9 -0
  279. package/src/providers/stale-no-vision-seed-migration.ts +72 -0
  280. package/src/responses/compaction.ts +14 -0
  281. package/src/responses/parser.ts +37 -20
  282. package/src/responses/state.ts +32 -29
  283. package/src/routing/history/build-file.ts +92 -0
  284. package/src/routing/history/indexer.ts +248 -216
  285. package/src/routing/history/ingest.ts +238 -0
  286. package/src/routing/history/rebuild-job.ts +111 -0
  287. package/src/routing/history/rebuild-protocol.ts +15 -0
  288. package/src/routing/history/rebuild-worker.ts +34 -0
  289. package/src/server/admission-model-scope.ts +4 -1
  290. package/src/server/antigravity-direct-eligibility.ts +208 -0
  291. package/src/server/auth-cors.ts +46 -0
  292. package/src/server/chat-completions.ts +75 -9
  293. package/src/server/claude-messages.ts +110 -20
  294. package/src/server/configured-credentials.ts +194 -0
  295. package/src/server/index/desktop-compatibility-startup.ts +56 -0
  296. package/src/server/index/link-management.ts +58 -0
  297. package/src/server/index/optional-listeners.ts +5 -1
  298. package/src/server/index/serve-options.ts +15 -2
  299. package/src/server/index/startup-warnings.ts +9 -0
  300. package/src/server/index/websocket-handler.ts +9 -0
  301. package/src/server/index.ts +13 -10
  302. package/src/server/inference/client-encoder-delivery.ts +6 -5
  303. package/src/server/lifecycle.ts +2 -1
  304. package/src/server/login-shell-path.ts +146 -0
  305. package/src/server/management/advisor-routes.ts +191 -0
  306. package/src/server/management/agent-settings-routes.ts +50 -14
  307. package/src/server/management/anthropic-reset-grant-routes.ts +17 -1
  308. package/src/server/management/codex-agent-role-routes.ts +19 -6
  309. package/src/server/management/codex-prompt-routes.ts +136 -5
  310. package/src/server/management/codex-role-auto-assign.ts +1 -1
  311. package/src/server/management/companion-routes.ts +4 -0
  312. package/src/server/management/config-routes.ts +2 -2
  313. package/src/server/management/context.ts +4 -0
  314. package/src/server/management/desktop-compatibility-routes.ts +46 -0
  315. package/src/server/management/desktop-compatibility-runtime-routes.ts +46 -0
  316. package/src/server/management/desktop-compatibility-settings-routes.ts +27 -0
  317. package/src/server/management/grok-coupon-routes.ts +78 -27
  318. package/src/server/management/link-enrollment.ts +192 -0
  319. package/src/server/management/link-route-state.ts +185 -0
  320. package/src/server/management/link-routes.ts +82 -402
  321. package/src/server/management/model-routes.ts +2 -2
  322. package/src/server/management/model-rows.ts +3 -1
  323. package/src/server/management/protocol-settings-patch.ts +1 -0
  324. package/src/server/management/provider-overwrite-carry.ts +10 -0
  325. package/src/server/management/provider-routes.ts +1 -0
  326. package/src/server/management/route-registry.ts +16 -3
  327. package/src/server/management/shared.ts +3 -0
  328. package/src/server/management/sibling-guard.ts +1 -0
  329. package/src/server/management-api.ts +9 -1
  330. package/src/server/messages-antigravity-direct.ts +195 -0
  331. package/src/server/messages-native.ts +73 -43
  332. package/src/server/relay-eager.ts +2 -0
  333. package/src/server/request-log-filter.ts +6 -1
  334. package/src/server/request-log-terminal-status.ts +63 -6
  335. package/src/server/request-log.ts +7 -0
  336. package/src/server/responses/adapter-continuation.ts +35 -18
  337. package/src/server/responses/adapter-delivery.ts +24 -2
  338. package/src/server/responses/adapter-dispatch.ts +261 -81
  339. package/src/server/responses/advisor-plan-slot.ts +27 -0
  340. package/src/server/responses/advisor-slot.ts +336 -0
  341. package/src/server/responses/code-mode-collab-surface.ts +313 -0
  342. package/src/server/responses/codex-ws-exchange.ts +147 -6
  343. package/src/server/responses/codex-ws-wire.ts +203 -0
  344. package/src/server/responses/collaboration.ts +36 -7
  345. package/src/server/responses/compact.ts +126 -48
  346. package/src/server/responses/context-overflow.ts +13 -1
  347. package/src/server/responses/core-codex-account.ts +105 -1
  348. package/src/server/responses/core-combo-failure.ts +24 -1
  349. package/src/server/responses/core-combo.ts +28 -2
  350. package/src/server/responses/core-errors.ts +29 -1
  351. package/src/server/responses/core-lifetime.ts +2 -0
  352. package/src/server/responses/core-normalize.ts +11 -0
  353. package/src/server/responses/core-options.ts +48 -0
  354. package/src/server/responses/core-replay.ts +7 -4
  355. package/src/server/responses/core.ts +1 -1
  356. package/src/server/responses/deferred-collab-surface.ts +158 -0
  357. package/src/server/responses/deferred-send-reporter.ts +25 -0
  358. package/src/server/responses/fetch-helpers.ts +12 -1
  359. package/src/server/responses/passthrough-delivery.ts +20 -11
  360. package/src/server/responses/passthrough-dispatch.ts +29 -7
  361. package/src/server/responses/request-prepare.ts +74 -9
  362. package/src/server/responses/request-transport.ts +22 -8
  363. package/src/server/responses/run-turn-execution.ts +70 -8
  364. package/src/server/responses/sidecar-execution.ts +89 -5
  365. package/src/server/responses/terminal-guard.ts +2 -1
  366. package/src/server/responses/ws-upstream.ts +9 -1
  367. package/src/server/responses-undeclared-tool-guard.ts +30 -9
  368. package/src/server/startup-action-control.ts +38 -4
  369. package/src/server/startup-health-cache.ts +5 -0
  370. package/src/server/system-env-shell.ts +7 -2
  371. package/src/server/system-env.ts +11 -1
  372. package/src/service/cli.ts +5 -1
  373. package/src/service/desktop-startup.ts +161 -34
  374. package/src/service/desktop-supervision.d.mts +27 -1
  375. package/src/service/desktop-supervision.mjs +111 -5
  376. package/src/service/health-sentinel-launch.ts +176 -0
  377. package/src/service/health-sentinel-main.ts +466 -0
  378. package/src/service/health-sentinel-policy.ts +241 -0
  379. package/src/service/health-sentinel-probe.ts +71 -0
  380. package/src/service/health-sentinel-windows.ts +87 -0
  381. package/src/service/linux-linger.ts +20 -0
  382. package/src/service/logging.ts +72 -0
  383. package/src/tray/windows-tray.ps1 +43 -24
  384. package/src/types/config.ts +68 -6
  385. package/src/types/provider.ts +7 -0
  386. package/src/types/request.ts +15 -0
  387. package/src/types/tools.ts +2 -0
  388. package/src/update/async-check.ts +1 -1
  389. package/src/update/badge.ts +2 -2
  390. package/src/update/index.ts +88 -15
  391. package/src/update/job-supervised-restart.ts +107 -0
  392. package/src/update/job.ts +32 -32
  393. package/src/update/notify.ts +1 -1
  394. package/src/update/refresh-scheduler.ts +2 -2
  395. package/src/update/supervised-recovery.d.mts +27 -0
  396. package/src/update/supervised-recovery.mjs +157 -0
  397. package/src/usage/cache-diagnostic.ts +2 -0
  398. package/src/usage/expected-prices.ts +16 -0
  399. package/src/usage/log.ts +19 -1
  400. package/src/web-search/gemini-executor.ts +2 -2
  401. package/assets/sponsors/packycode.png +0 -0
  402. package/gui/dist/assets/App-Ba8uG696.js +0 -54
  403. package/gui/dist/assets/Tray-IImbNPRy.js +0 -1
  404. package/gui/dist/assets/index-BHRZdJuv.js +0 -94
  405. package/gui/dist/assets/quota-summary-DiJO6sYY.js +0 -1
@@ -64,6 +64,7 @@ export interface ProtocolRolloutSettings {
64
64
  managedMessagesNative: boolean;
65
65
  managedMessagesNativeOAuth: boolean;
66
66
  directEncoders: boolean;
67
+ antigravityDirect: boolean;
67
68
  shadowPlan: boolean;
68
69
  }
69
70
 
@@ -101,6 +102,12 @@ export function resolveProtocolSettings(config: ProtocolConfig, providerName?: s
101
102
  // OAuth extension is meaningless without the key-auth path it extends.
102
103
  managedMessagesNativeOAuth: managedMessagesNative && nativeOn("managedMessagesNativeOAuth"),
103
104
  directEncoders: on("directEncoders"),
105
+ // Default on: an absent switch takes the Antigravity direct lane, which falls back to the
106
+ // bridge per request whenever its eligibility rule declines. Only an explicit `false` turns
107
+ // it off; a non-boolean value or a malformed container fails closed like every other switch.
108
+ antigravityDirect: validContainers && (Object.hasOwn(rollout, "antigravityDirect")
109
+ ? rollout.antigravityDirect === true
110
+ : true),
104
111
  shadowPlan: on("shadowPlan"),
105
112
  }),
106
113
  });
@@ -145,6 +152,7 @@ export function protocolPolicyRevision(config: ProtocolConfig & Pick<OcxConfig,
145
152
  secondarySettings.rollout.managedMessagesNative,
146
153
  secondarySettings.rollout.managedMessagesNativeOAuth,
147
154
  settings.rollout.directEncoders,
155
+ settings.rollout.antigravityDirect,
148
156
  settings.rollout.shadowPlan,
149
157
  ],
150
158
  });
@@ -0,0 +1,74 @@
1
+ import type { OcxProviderConfig } from "../types";
2
+ import { resolveAnthropicMessagesUrl } from "../adapters/anthropic";
3
+ import { providerApiKeySelectionIsCurrent } from "./api-key-selection";
4
+ import { hasKeyPoolFailover } from "./key-failover";
5
+ import type { OcxConfig } from "../types";
6
+ import { registerProviderResponseProvenance } from "./request-pacing";
7
+ import { isEgressTransparentExecutor, markEgressTransparentExecutor } from "../lib/provider-egress";
8
+
9
+ interface BillingSendOwnership {
10
+ readonly providerName: string;
11
+ readonly provider: OcxProviderConfig;
12
+ readonly target: string;
13
+ }
14
+ const responseOwners = new WeakMap<Response, BillingSendOwnership | null>();
15
+ registerProviderResponseProvenance(responseOwners);
16
+
17
+ /** Runs inside connection policy, after plugin/header rewrites, at the physical fetch. */
18
+ export function anthropicBillingPhysicalFetch(
19
+ physicalFetch: typeof globalThis.fetch,
20
+ providerName: string,
21
+ provider: OcxProviderConfig,
22
+ ): typeof globalThis.fetch {
23
+ const wrapped = (async (input, init) => {
24
+ const target = input instanceof Request ? input.url : String(input);
25
+ const headers = new Headers(init?.headers ?? (input instanceof Request ? input.headers : undefined));
26
+ const selection = provider._apiKeyAttempt;
27
+ const ownsCredential = provider.apiKeyTransport === "bearer"
28
+ ? headers.get("authorization") === `Bearer ${provider.apiKey}` && !headers.has("x-api-key")
29
+ : headers.get("x-api-key") === provider.apiKey && !headers.has("authorization");
30
+ const ownsKey = provider.adapter === "anthropic" && (provider.authMode === undefined || provider.authMode === "key")
31
+ && hasKeyPoolFailover(provider) && selection !== undefined && provider.apiKey !== undefined
32
+ && ownsCredential
33
+ && target === resolveAnthropicMessagesUrl(provider);
34
+ const owner: BillingSendOwnership | null = ownsKey ? Object.freeze({
35
+ providerName, target,
36
+ provider: Object.freeze({ ...provider,
37
+ _apiKeyAttempt: Object.freeze({ ...selection }),
38
+ headers: Object.freeze({ ...provider.headers }),
39
+ apiKeyPool: provider.apiKeyPool?.map(entry => Object.freeze({ ...entry })),
40
+ }),
41
+ }) : null;
42
+ // No await between capturing the selected credential and invoking the actual send.
43
+ const response = await physicalFetch(input, init);
44
+ // An executor reusing a Response across physical sends cannot assign it two owners.
45
+ responseOwners.set(response, responseOwners.has(response) ? null : owner);
46
+ return response;
47
+ }) as typeof globalThis.fetch;
48
+ return isEgressTransparentExecutor(physicalFetch) ? markEgressTransparentExecutor(wrapped) : wrapped;
49
+ }
50
+
51
+ /** Immutable response ownership, matched to the attempted route and still-current selection. */
52
+ export function anthropicBillingResponseOwner(
53
+ response: Response, config: OcxConfig, providerName: string, attemptedProvider: OcxProviderConfig,
54
+ ): OcxProviderConfig | null {
55
+ const owner = responseOwners.get(response);
56
+ if (!owner || owner.providerName !== providerName || owner.target !== resolveAnthropicMessagesUrl(attemptedProvider)) return null;
57
+ const sent = owner.provider;
58
+ const current = config.providers[providerName];
59
+ if (!current || current.adapter !== sent.adapter || current.adapter !== "anthropic"
60
+ || (current.authMode !== undefined && current.authMode !== "key") || !hasKeyPoolFailover(current)) return null;
61
+ const sentTransport = sent.apiKeyTransport ?? "x-api-key";
62
+ if ((current.apiKeyTransport ?? "x-api-key") !== sentTransport
63
+ || (attemptedProvider.apiKeyTransport ?? "x-api-key") !== sentTransport) return null;
64
+ // Configured headers can change billing/account ownership independently of key selection.
65
+ const headerShape = (provider: OcxProviderConfig) => JSON.stringify(Object.entries(provider.headers ?? {}).sort(([a], [b]) => a.localeCompare(b)));
66
+ if (headerShape(current) !== headerShape(sent)) return null;
67
+ const attempted = attemptedProvider._apiKeyAttempt;
68
+ if (!attempted || attemptedProvider.apiKey !== sent.apiKey || attemptedProvider.baseUrl !== sent.baseUrl
69
+ || attemptedProvider.authMode !== sent.authMode || attemptedProvider.adapter !== sent.adapter
70
+ || attempted.entryId !== sent._apiKeyAttempt?.entryId || attempted.reference !== sent._apiKeyAttempt?.reference
71
+ || attempted.revision !== sent._apiKeyAttempt?.revision
72
+ || !providerApiKeySelectionIsCurrent(config, providerName, sent)) return null;
73
+ return sent;
74
+ }
@@ -0,0 +1,31 @@
1
+ import type { OcxConfig, OcxProviderConfig } from "../types";
2
+ import { isNonReplayableResponse } from "../lib/upstream-retry";
3
+ import { anthropicBillingResponseOwner } from "./anthropic-billing-ownership";
4
+ import { readAnthropicBillingRefusal } from "./anthropic-billing-refusal";
5
+ import { hasKeyPoolFailover, rotateProviderTransportOnAnthropicBilling } from "./key-failover";
6
+ import type { OcxProviderTransport } from "./xai-transport";
7
+
8
+ /**
9
+ * Whether a provider can recover a credit-balance refusal by key rotation. This is an
10
+ * eligibility predicate for send accounting, not a recovery site: the one billing recovery
11
+ * loop in adapter-dispatch.ts keeps its own `hasKeyPoolFailover` guard.
12
+ */
13
+ export function isAnthropicBillingKeyPool(provider: OcxProviderConfig): boolean {
14
+ return provider.adapter === "anthropic" && hasKeyPoolFailover(provider);
15
+ }
16
+
17
+ /** Pre-output billing recovery cools the refused key for the credit-balance window. */
18
+ export async function rotateAnthropicBillingKeyOnResponse(
19
+ response: Response, config: OcxConfig, providerName: string, attemptedProvider: OcxProviderConfig,
20
+ options: { signal?: AbortSignal; promptCacheKey?: string } = {},
21
+ ): Promise<OcxProviderTransport | null> {
22
+ if (options.signal?.aborted || isNonReplayableResponse(response)
23
+ || !anthropicBillingResponseOwner(response, config, providerName, attemptedProvider)) return null;
24
+ if (!await readAnthropicBillingRefusal(response, options.signal) || options.signal?.aborted) return null;
25
+ // Reading is asynchronous: fence a late refusal against manual selection or key replacement.
26
+ const owner = anthropicBillingResponseOwner(response, config, providerName, attemptedProvider);
27
+ if (!owner) return null;
28
+ return rotateProviderTransportOnAnthropicBilling(config, providerName, owner, {
29
+ attemptedKey: owner.apiKey, attemptedSelection: owner._apiKeyAttempt, promptCacheKey: options.promptCacheKey,
30
+ });
31
+ }
@@ -0,0 +1,55 @@
1
+ /** Pure credit-balance evidence, independent of OAuth account routing. */
2
+ import { readBoundedResponseBody } from "../lib/bounded-body";
3
+ const MAX_BILLING_REFUSAL_BYTES = 8 * 1024;
4
+ const BILLING_REFUSAL_DEADLINE_MS = 250;
5
+ // Keep the whole-message credit-balance pattern used by anthropic-account-refusal.
6
+ const CREDIT_BALANCE_REFUSAL = /^Your credit balance is too low to access the Anthropic API\.(?: Please go to Plans & Billing to upgrade or purchase credits\.)?$/i;
7
+ const BILLING_ERROR_TYPES = new Set(["invalid_request_error", "billing_error", "permission_error"]);
8
+ const ACCOUNT_REFUSAL_CODES = new Set(["subscription_required", "insufficient_quota", "permission_denied"]);
9
+
10
+ function record(value: unknown): value is Record<string, unknown> {
11
+ return value !== null && typeof value === "object" && !Array.isArray(value);
12
+ }
13
+
14
+ /** Only an Anthropic error envelope with exact billing wording is evidence. */
15
+ export function isAnthropicBillingRefusal(status: number, payload: unknown): boolean {
16
+ if (status !== 400 && status !== 402) return false;
17
+ if (!record(payload) || payload.type !== "error" || !record(payload.error)) return false;
18
+ const error = payload.error;
19
+ if (typeof error.type !== "string" || !BILLING_ERROR_TYPES.has(error.type)
20
+ || typeof error.message !== "string" || Buffer.byteLength(error.message, "utf8") > MAX_BILLING_REFUSAL_BYTES) return false;
21
+ if (error.code != null && (typeof error.code !== "string" || !ACCOUNT_REFUSAL_CODES.has(error.code))) return false;
22
+ return CREDIT_BALANCE_REFUSAL.test(error.message.trim());
23
+ }
24
+
25
+ /** Bounded combo evidence after existing HTTP error formatting, before any output. */
26
+ export function isAnthropicBillingFailure(status: number, message: string, code?: string | null): boolean {
27
+ if (status !== 400 && status !== 402 || Buffer.byteLength(message, "utf8") > MAX_BILLING_REFUSAL_BYTES) return false;
28
+ if (code != null && !BILLING_ERROR_TYPES.has(code) && !ACCOUNT_REFUSAL_CODES.has(code)) return false;
29
+ let text = message.trim();
30
+ const prefix = `Provider error ${status}: `;
31
+ if (text.startsWith(prefix)) text = text.slice(prefix.length);
32
+ try { return isAnthropicBillingRefusal(status, JSON.parse(text)); } catch { /* Exact prose needs a structured type. */ }
33
+ return typeof code === "string" && BILLING_ERROR_TYPES.has(code)
34
+ && isAnthropicBillingRefusal(status, { type: "error", error: { type: code, message: text } });
35
+ }
36
+
37
+ /** Inspect a clone, never the terminal body; the shared reader owns interruption/cleanup. */
38
+ export async function readAnthropicBillingRefusal(response: Response, signal?: AbortSignal): Promise<boolean> {
39
+ if (signal?.aborted || (response.status !== 400 && response.status !== 402)) return false;
40
+ const contentType = response.headers.get("content-type")?.split(";", 1)[0]?.trim().toLowerCase();
41
+ if (!contentType || !/^application\/(?:json|[a-z0-9.+-]+\+json)$/.test(contentType)) return false;
42
+ try {
43
+ const deadline = AbortSignal.timeout(BILLING_REFUSAL_DEADLINE_MS);
44
+ const body = await readBoundedResponseBody(response.clone(), {
45
+ signal: signal ? AbortSignal.any([signal, deadline]) : deadline,
46
+ maxBytes: MAX_BILLING_REFUSAL_BYTES, fatalUtf8: true,
47
+ totalTimeoutMs: BILLING_REFUSAL_DEADLINE_MS,
48
+ });
49
+ if (signal?.aborted || !body.displaySafe || body.truncated || body.timedOut || body.oversized) return false;
50
+ return isAnthropicBillingRefusal(response.status, JSON.parse(body.text));
51
+ } catch {
52
+ // Untrusted malformed, unreadable and invalid UTF-8 bodies confer no authority.
53
+ return false;
54
+ }
55
+ }
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Put one Anthropic account back into the pool after a settled reset grant.
3
+ *
4
+ * A fresh usage read lifts a 429 quota cooldown whose rejected windows read
5
+ * open, whatever its deadline source (#6989), but a grant should not have to
6
+ * wait for one: the grant itself lifts admission, as far as its evidence goes:
7
+ *
8
+ * - `claimedAt` is when the operation was first journaled, before its first
9
+ * claim was sent. A refusal recorded at or after it may already describe the
10
+ * post-reset account (another request can be refused while the claim is in
11
+ * flight), so it stays.
12
+ * - `cleared` is the window list the canonical settlement reported. The
13
+ * cooldown is lifted only when those windows cover every window its refusal
14
+ * named; an empty or partial list leaves it in place.
15
+ *
16
+ * Whatever the routing outcome (other than a newer refusal), the cached bars
17
+ * are dropped and usage is reread with its own request: joining a read that
18
+ * started before the grant could republish the pre-reset bars, and a fresh
19
+ * reading is what lets usage recovery release a cooldown the cleared
20
+ * list did not cover. The reread is best-effort; the grant is already spent.
21
+ *
22
+ * Model-family exclusions are not cleared here. Only that reread may release
23
+ * one, and only when its payload proves the family is no longer rejected.
24
+ */
25
+ import { anthropicRoutingFor, type AnthropicQuotaWindow } from "../oauth/anthropic-routing";
26
+ import type { AnthropicInstanceId } from "./anthropic-instance";
27
+ import { fetchAnthropicAccountQuota } from "./quota/anthropic-account-quota";
28
+ import { forgetCachedProviderAccountQuota } from "./quota/account-cache";
29
+ import { anthropicCooldownRecoveryFor } from "./quota/anthropic-cooldown-recovery";
30
+
31
+ export interface AnthropicResetAdmissionEvidence {
32
+ /** Journal time of the operation's first record, written before the first claim was sent. */
33
+ claimedAt: number;
34
+ /** Windows the canonical settlement reported cleared (reset-grant window names). */
35
+ cleared: readonly string[];
36
+ }
37
+
38
+ const QUOTA_WINDOW_FOR_RESET_WINDOW: Readonly<Record<string, AnthropicQuotaWindow>> = {
39
+ five_hour: "five-hour",
40
+ seven_day: "weekly",
41
+ };
42
+
43
+ export async function admitAnthropicAccountAfterReset(
44
+ instance: AnthropicInstanceId,
45
+ accountId: string,
46
+ evidence: AnthropicResetAdmissionEvidence,
47
+ ): Promise<void> {
48
+ const cleared = new Set<AnthropicQuotaWindow>();
49
+ for (const window of evidence.cleared) {
50
+ const quotaWindow = QUOTA_WINDOW_FOR_RESET_WINDOW[window];
51
+ if (quotaWindow) cleared.add(quotaWindow);
52
+ }
53
+ const outcome = anthropicRoutingFor(instance).clearAnthropicAccountCooldownForReset(accountId, evidence.claimedAt, cleared);
54
+ if (outcome === "newer") return;
55
+ // Move any usage read that started before the grant onto a stale generation
56
+ // before dropping the cached row, so it cannot republish the pre-reset bars.
57
+ // "cleared" and "uncovered" already advanced the generation in routing (an
58
+ // uncovered entry moved with it, so the reread below can still recover it).
59
+ if (outcome === "absent") anthropicCooldownRecoveryFor(instance).noteAnthropicCooldownMutation(accountId);
60
+ forgetCachedProviderAccountQuota(instance, accountId);
61
+ await fetchAnthropicAccountQuota(instance, accountId, true, undefined, true);
62
+ }
@@ -44,6 +44,8 @@ interface OperationRecord {
44
44
  status: "open" | "settled";
45
45
  code?: string;
46
46
  resetsLeft?: number | null;
47
+ /** Windows the settled claim reported cleared; absent on records written before this field. */
48
+ cleared?: string[];
47
49
  attempts: number;
48
50
  leaseUntil: number;
49
51
  createdAt: number;
@@ -64,7 +66,7 @@ export interface AnthropicResetOperationIdentity {
64
66
 
65
67
  export type AnthropicResetBegin =
66
68
  | { kind: "execute"; attempt: number }
67
- | { kind: "replay"; code: string; resetsLeft: number | null; settledAt: number }
69
+ | { kind: "replay"; code: string; resetsLeft: number | null; settledAt: number; claimedAt: number; cleared: string[] }
68
70
  | { kind: "identity-mismatch" }
69
71
  | { kind: "in-flight" }
70
72
  | { kind: "expired" }
@@ -102,7 +104,9 @@ function isRecordShape(value: unknown): value is OperationRecord {
102
104
  return typeof record.accountId === "string" && typeof record.grantId === "string"
103
105
  && typeof record.orgDigest === "string" && (record.status === "open" || record.status === "settled")
104
106
  && typeof record.attempts === "number" && typeof record.leaseUntil === "number"
105
- && typeof record.createdAt === "number" && typeof record.updatedAt === "number";
107
+ && typeof record.createdAt === "number" && typeof record.updatedAt === "number"
108
+ && (record.cleared === undefined
109
+ || Array.isArray(record.cleared) && record.cleared.every(window => typeof window === "string"));
106
110
  }
107
111
 
108
112
  /** Missing file → empty journal. Present but unreadable → throws; never an empty fallback. */
@@ -197,7 +201,10 @@ export function beginAnthropicResetOperation(
197
201
  return { kind: "identity-mismatch" };
198
202
  }
199
203
  if (existing.status === "settled") {
200
- return { kind: "replay", code: existing.code ?? "unavailable", resetsLeft: existing.resetsLeft ?? null, settledAt: existing.updatedAt };
204
+ return {
205
+ kind: "replay", code: existing.code ?? "unavailable", resetsLeft: existing.resetsLeft ?? null,
206
+ settledAt: existing.updatedAt, claimedAt: existing.createdAt, cleared: existing.cleared ?? [],
207
+ };
201
208
  }
202
209
  if (existing.leaseUntil > now) return { kind: "in-flight" };
203
210
  if (now - existing.createdAt >= ANTHROPIC_RESET_RETRY_WINDOW_MS) return { kind: "expired" };
@@ -232,6 +239,12 @@ export function beginAnthropicResetOperation(
232
239
  export interface AnthropicResetSettlement {
233
240
  code: string;
234
241
  resetsLeft: number | null;
242
+ /** When the canonical (first) settlement was journaled, not when this caller arrived. */
243
+ settledAt: number;
244
+ /** When the operation was first journaled, before its first claim was sent. */
245
+ claimedAt: number;
246
+ /** Windows the canonical settlement reported cleared. */
247
+ cleared: string[];
235
248
  }
236
249
 
237
250
  /**
@@ -241,7 +254,7 @@ export interface AnthropicResetSettlement {
241
254
  * throws, because an answer with no journal entry cannot be reported as durable.
242
255
  */
243
256
  export function settleAnthropicResetOperation(
244
- settlement: { operationId: string; code: string; resetsLeft: number | null },
257
+ settlement: { operationId: string; code: string; resetsLeft: number | null; cleared?: readonly string[] },
245
258
  options: AnthropicResetLedgerOptions = {},
246
259
  ): AnthropicResetSettlement {
247
260
  const now = options.now ?? Date.now();
@@ -250,14 +263,23 @@ export function settleAnthropicResetOperation(
250
263
  const journal = readJournal(filePath);
251
264
  const existing = journal.operations[settlement.operationId];
252
265
  if (!existing) throw new AnthropicResetLedgerError("unavailable");
253
- if (existing.status === "settled") return { code: existing.code ?? "unavailable", resetsLeft: existing.resetsLeft ?? null };
266
+ if (existing.status === "settled") {
267
+ return {
268
+ code: existing.code ?? "unavailable", resetsLeft: existing.resetsLeft ?? null,
269
+ settledAt: existing.updatedAt, claimedAt: existing.createdAt, cleared: existing.cleared ?? [],
270
+ };
271
+ }
254
272
  existing.status = "settled";
255
273
  existing.code = settlement.code;
256
274
  existing.resetsLeft = settlement.resetsLeft;
275
+ existing.cleared = [...(settlement.cleared ?? [])];
257
276
  existing.leaseUntil = 0;
258
277
  existing.updatedAt = now;
259
278
  writeJournal(filePath, journal, now);
260
- return { code: settlement.code, resetsLeft: settlement.resetsLeft };
279
+ return {
280
+ code: settlement.code, resetsLeft: settlement.resetsLeft, settledAt: now,
281
+ claimedAt: existing.createdAt, cleared: [...existing.cleared],
282
+ };
261
283
  });
262
284
  }
263
285
 
@@ -0,0 +1,102 @@
1
+ import { getConfigDir } from "../config/paths";
2
+ import { ANTIGRAVITY_MODEL_ALIASES, canonicalAntigravityUsageModel, type AntigravityAvailableModel } from "./antigravity-models";
3
+
4
+ /**
5
+ * Per-account Antigravity model rosters, taken from each account's own live
6
+ * `:fetchAvailableModels` discovery.
7
+ *
8
+ * Google gates models per account, not per client. Measured 2026-10-14: one account lists
9
+ * `claude-sonnet-4-6` / `claude-opus-4-6-thinking` and answers 404 "Requested entity was not
10
+ * found" for every `claude-{opus,sonnet}-5-5-*` id, while other accounts list only the 5.5
11
+ * families. Gemini 4 Argon is likewise Ultra / Enterprise only. Google documents Claude 5.5 as
12
+ * needing a directly billed, non-trial Pro or Ultra plan and rolls it out gradually, so the static
13
+ * lineup is a superset and only discovery says what one account can actually call.
14
+ *
15
+ * The catalog already exposes only the ACTIVE account's discovered rows (an authoritative live
16
+ * roster drops static rows it did not return). This store keeps every account's last roster so
17
+ * that account selection can prefer an account that lists the requested model, instead of
18
+ * landing a 5.5 request on a 4.6-only account. Absence of a roster is "unknown", never "no":
19
+ * an account that was never active during discovery stays fully usable.
20
+ */
21
+
22
+ /** A roster older than this is treated as unknown; entitlements change with plan and rollout. */
23
+ export const ANTIGRAVITY_ACCOUNT_ROSTER_TTL_MS = 6 * 60 * 60 * 1000;
24
+
25
+ interface AccountRoster {
26
+ readonly ids: ReadonlySet<string>;
27
+ readonly modelIds: readonly string[];
28
+ readonly observedAt: number;
29
+ }
30
+
31
+ const rosters = new Map<string, AccountRoster>();
32
+
33
+ function rosterKey(providerName: string, accountId: string): string {
34
+ return `${getConfigDir()}\0${providerName}\0${accountId}`;
35
+ }
36
+
37
+ /** Record one account's discovered roster: picker ids, representative wire ids, and tier wire ids. */
38
+ export function recordAntigravityAccountRoster(
39
+ providerName: string,
40
+ accountId: string,
41
+ models: readonly AntigravityAvailableModel[],
42
+ now = Date.now(),
43
+ ): void {
44
+ if (!providerName || !accountId) return;
45
+ const ids = new Set<string>();
46
+ for (const model of models) {
47
+ ids.add(model.id);
48
+ ids.add(model.wireModelId);
49
+ for (const wire of Object.values(model.effortWireModelIds ?? {})) if (wire) ids.add(wire);
50
+ }
51
+ rosters.set(rosterKey(providerName, accountId), {
52
+ ids,
53
+ modelIds: models.map(model => model.id),
54
+ observedAt: now,
55
+ });
56
+ }
57
+
58
+ function freshRoster(providerName: string, accountId: string, now: number): AccountRoster | undefined {
59
+ const roster = rosters.get(rosterKey(providerName, accountId));
60
+ if (!roster || now - roster.observedAt > ANTIGRAVITY_ACCOUNT_ROSTER_TTL_MS) return undefined;
61
+ return roster;
62
+ }
63
+
64
+ /** The picker ids one account's last discovery returned, or `undefined` when unknown. */
65
+ export function antigravityAccountRosterModelIds(
66
+ providerName: string,
67
+ accountId: string,
68
+ now = Date.now(),
69
+ ): readonly string[] | undefined {
70
+ return freshRoster(providerName, accountId, now)?.modelIds;
71
+ }
72
+
73
+ /**
74
+ * Whether one account's roster lists `modelId`: `true` / `false` from its last discovery,
75
+ * `undefined` when that account has no fresh roster. A picker id, one of its tier wire ids, or a
76
+ * compatibility alias of either all count as listed.
77
+ */
78
+ export function antigravityAccountSupportsModel(
79
+ providerName: string,
80
+ accountId: string,
81
+ modelId: string,
82
+ now = Date.now(),
83
+ ): boolean | undefined {
84
+ const roster = freshRoster(providerName, accountId, now);
85
+ if (!roster) return undefined;
86
+ const id = modelId.trim();
87
+ if (!id) return undefined;
88
+ if (roster.ids.has(id)) return true;
89
+ const canonical = canonicalAntigravityUsageModel(id);
90
+ if (roster.ids.has(canonical)) return true;
91
+ const alias = Object.hasOwn(ANTIGRAVITY_MODEL_ALIASES, id) ? ANTIGRAVITY_MODEL_ALIASES[id] : undefined;
92
+ return alias !== undefined && roster.ids.has(alias);
93
+ }
94
+
95
+ /** Forget rosters (one account, or all). For account removal and tests. */
96
+ export function clearAntigravityAccountRosters(providerName?: string, accountId?: string): void {
97
+ if (providerName && accountId) {
98
+ rosters.delete(rosterKey(providerName, accountId));
99
+ return;
100
+ }
101
+ rosters.clear();
102
+ }
@@ -17,6 +17,21 @@ import { readAntigravityWireSnapshot, writeAntigravityWireSnapshot, type Antigra
17
17
  /** Current Antigravity Flash generation. */
18
18
  const GEMINI_FLASH_CURRENT = "gemini-3.8-flash";
19
19
 
20
+ /**
21
+ * Gemini 4 Argon. Like 3.8 it publishes one wire id per tier (`-low/-medium/-high`) and no
22
+ * `-tiered` row, verified 2026-10-14 against live CCA. CCA serves these ids only to the CLI
23
+ * User-Agent (see antigravityUserAgentForWireModel), and only to AI Ultra / Enterprise accounts.
24
+ */
25
+ const GEMINI_ARGON = "gemini-4-argon";
26
+
27
+ /**
28
+ * Claude 5.5 on Antigravity: one wire id per tier (`-low/-medium/-high`), each 1,000,000 tokens
29
+ * with image input, observed in live CCA discovery on 2026-10-03
30
+ * (devlog/_fin/261003_release_lane_c). They ride the IDE User-Agent like every non-Argon model.
31
+ */
32
+ const CLAUDE_OPUS_55 = "claude-opus-5-5";
33
+ const CLAUDE_SONNET_55 = "claude-sonnet-5-5";
34
+
20
35
  /**
21
36
  * Previous Flash generation — still served, still picker-visible.
22
37
  *
@@ -75,6 +90,15 @@ const ANTIGRAVITY_WIRE_MODELS = [
75
90
  ];
76
91
 
77
92
  const ANTIGRAVITY_PICKER_MODEL_BY_WIRE_ID: Record<string, string> = {
93
+ "claude-opus-5-5-low": CLAUDE_OPUS_55,
94
+ "claude-opus-5-5-medium": CLAUDE_OPUS_55,
95
+ "claude-opus-5-5-high": CLAUDE_OPUS_55,
96
+ "claude-sonnet-5-5-low": CLAUDE_SONNET_55,
97
+ "claude-sonnet-5-5-medium": CLAUDE_SONNET_55,
98
+ "claude-sonnet-5-5-high": CLAUDE_SONNET_55,
99
+ "gemini-4-argon-low": GEMINI_ARGON,
100
+ "gemini-4-argon-medium": GEMINI_ARGON,
101
+ "gemini-4-argon-high": GEMINI_ARGON,
78
102
  "gemini-3.8-flash-low": "gemini-3.8-flash",
79
103
  "gemini-3.8-flash-medium": "gemini-3.8-flash",
80
104
  "gemini-3.8-flash-high": "gemini-3.8-flash",
@@ -164,8 +188,11 @@ function collapsesIntoKnownPickerModel(candidateId: string): boolean {
164
188
  // Claude Opus: effort → thinkingConfig.thinkingLevel (CLIProxyAPI proven pattern).
165
189
  export const ANTIGRAVITY_MODEL_EFFORTS: Record<string, string[]> = {
166
190
  // No `minimal`: Google documents it as an error for this generation, and CCA exposes only
167
- // the three tiers.
191
+ // the three tiers. Argon documents the same ladder (`minimal` is an HTTP 400).
192
+ [GEMINI_ARGON]: ["low", "medium", "high"],
168
193
  "gemini-3.8-flash": ["low", "medium", "high"],
194
+ [CLAUDE_OPUS_55]: ["low", "medium", "high"],
195
+ [CLAUDE_SONNET_55]: ["low", "medium", "high"],
169
196
  "gemini-3.7-flash": ["low", "medium", "high"],
170
197
  "gemini-3.1-pro": ["low", "high"],
171
198
  "claude-sonnet-4-6": ["low", "medium", "high", "max"],
@@ -175,7 +202,23 @@ export const ANTIGRAVITY_MODEL_EFFORTS: Record<string, string[]> = {
175
202
  // ── Effort → wire model map for Gemini base models ──
176
203
  const ANTIGRAVITY_EFFORT_WIRE_MAP: Record<string, Record<string, string>> = {
177
204
  // 3.8 publishes one wire id per tier and no `-tiered` row, so its efforts ride the suffix.
178
- // This is the 3.6 shape, not the 3.7 one.
205
+ // This is the 3.6 shape, not the 3.7 one. Argon has the same shape.
206
+ [GEMINI_ARGON]: {
207
+ low: "gemini-4-argon-low",
208
+ medium: "gemini-4-argon-medium",
209
+ high: "gemini-4-argon-high",
210
+ },
211
+ // Claude 5.5 publishes the same per-tier suffix family.
212
+ [CLAUDE_OPUS_55]: {
213
+ low: "claude-opus-5-5-low",
214
+ medium: "claude-opus-5-5-medium",
215
+ high: "claude-opus-5-5-high",
216
+ },
217
+ [CLAUDE_SONNET_55]: {
218
+ low: "claude-sonnet-5-5-low",
219
+ medium: "claude-sonnet-5-5-medium",
220
+ high: "claude-sonnet-5-5-high",
221
+ },
179
222
  "gemini-3.8-flash": {
180
223
  low: "gemini-3.8-flash-low",
181
224
  medium: "gemini-3.8-flash-medium",
@@ -198,7 +241,7 @@ const ANTIGRAVITY_EFFORT_WIRE_MAP: Record<string, Record<string, string>> = {
198
241
  * `gemini-3.1-pro` is deliberately absent: its `high` rung is `gemini-pro-agent`, which carries
199
242
  * no tier suffix, so there the level is the only thing naming the effort.
200
243
  */
201
- const ANTIGRAVITY_SUFFIX_TIER_MODELS = new Set(["gemini-3.8-flash"]);
244
+ const ANTIGRAVITY_SUFFIX_TIER_MODELS = new Set([GEMINI_ARGON, "gemini-3.8-flash", CLAUDE_OPUS_55, CLAUDE_SONNET_55]);
202
245
 
203
246
  function completeDiscoveredEffortWireModelIds(
204
247
  pickerId: string,
@@ -223,7 +266,11 @@ function completeDiscoveredEffortWireModelIds(
223
266
  // ── Default effort per Gemini base model ──
224
267
  const ANTIGRAVITY_DEFAULT_EFFORT: Record<string, string> = {
225
268
  // Google's documented thinking_level default, and the tier CCA marks `recommended`.
269
+ [GEMINI_ARGON]: "medium",
226
270
  "gemini-3.8-flash": "medium",
271
+ // Matches the discovered-family default (medium), so static and live resolution agree.
272
+ [CLAUDE_OPUS_55]: "medium",
273
+ [CLAUDE_SONNET_55]: "medium",
227
274
  "gemini-3.1-pro": "high",
228
275
  };
229
276
 
@@ -285,9 +332,25 @@ export const ANTIGRAVITY_MODEL_ALIASES: Record<string, string> = {
285
332
  ...ANTIGRAVITY_COMPATIBILITY_MODEL_ALIASES,
286
333
  };
287
334
 
288
- // Picker-visible: collapsed base models only.
335
+ // Static picker lineup: collapsed base models only, exactly "3.8 / 4 / 5.5 / 5.5" (2026-10-14).
336
+ // Anything else reaches the picker only when live discovery returns it.
289
337
  export const ANTIGRAVITY_MODELS = [
290
338
  GEMINI_FLASH_CURRENT,
339
+ GEMINI_ARGON,
340
+ CLAUDE_OPUS_55,
341
+ CLAUDE_SONNET_55,
342
+ ];
343
+
344
+ /**
345
+ * Every base id the routing and discovery rules recognise: the static lineup plus models that
346
+ * left it. Removing a model from the picker must not change how a saved selection routes or how
347
+ * a discovered row collapses, so these legacy bases stay known here. Live discovery on
348
+ * 2026-10-14 no longer returns gemini-3.7-flash(-tiered) or the gemini-3.1-pro wire ids (both
349
+ * 404), but still returns the Claude 4.6 rows and gpt-oss-120b-medium, which therefore keep
350
+ * appearing through discovery.
351
+ */
352
+ export const ANTIGRAVITY_KNOWN_PICKER_MODELS: readonly string[] = [
353
+ ...ANTIGRAVITY_MODELS,
291
354
  GEMINI_FLASH_PREVIOUS,
292
355
  "gemini-3.1-pro",
293
356
  "gemini-3.1-flash-image",
@@ -297,11 +360,20 @@ export const ANTIGRAVITY_MODELS = [
297
360
  ];
298
361
 
299
362
  function isKnownAntigravityPickerModelId(value: string): boolean {
300
- return isValidModelDiscoveryModelId(value) && ANTIGRAVITY_MODELS.includes(value);
363
+ return isValidModelDiscoveryModelId(value) && ANTIGRAVITY_KNOWN_PICKER_MODELS.includes(value);
301
364
  }
302
365
 
303
366
  // Context windows from the upstream `:fetchAvailableModels` maxTokens per model.
304
367
  const ANTIGRAVITY_WIRE_MODEL_CONTEXT_WINDOWS: Record<string, number> = {
368
+ "claude-opus-5-5-low": 1_000_000,
369
+ "claude-opus-5-5-medium": 1_000_000,
370
+ "claude-opus-5-5-high": 1_000_000,
371
+ "claude-sonnet-5-5-low": 1_000_000,
372
+ "claude-sonnet-5-5-medium": 1_000_000,
373
+ "claude-sonnet-5-5-high": 1_000_000,
374
+ "gemini-4-argon-low": 1_048_576,
375
+ "gemini-4-argon-medium": 1_048_576,
376
+ "gemini-4-argon-high": 1_048_576,
305
377
  "gemini-3.8-flash-low": 1_048_576,
306
378
  "gemini-3.8-flash-medium": 1_048_576,
307
379
  "gemini-3.8-flash-high": 1_048_576,
@@ -316,7 +388,10 @@ const ANTIGRAVITY_WIRE_MODEL_CONTEXT_WINDOWS: Record<string, number> = {
316
388
 
317
389
  export const ANTIGRAVITY_MODEL_CONTEXT_WINDOWS: Record<string, number> = {
318
390
  // Collapsed base IDs — explicit entries for the picker.
391
+ [GEMINI_ARGON]: 1_048_576,
319
392
  "gemini-3.8-flash": 1_048_576,
393
+ [CLAUDE_OPUS_55]: 1_000_000,
394
+ [CLAUDE_SONNET_55]: 1_000_000,
320
395
  "gemini-3.7-flash": 1_048_576,
321
396
  "gemini-3.1-pro": 1_048_576,
322
397
  // Wire IDs and aliases via derivation.
@@ -330,6 +405,9 @@ export const ANTIGRAVITY_MODEL_CONTEXT_WINDOWS: Record<string, number> = {
330
405
  };
331
406
 
332
407
  export const ANTIGRAVITY_MODEL_INPUT_MODALITIES: Record<string, string[]> = {
408
+ [GEMINI_ARGON]: ["text", "image"],
409
+ [CLAUDE_OPUS_55]: ["text", "image"],
410
+ [CLAUDE_SONNET_55]: ["text", "image"],
333
411
  // Google documents 3.7 Flash as also accepting video, audio and PDF, but this proxy
334
412
  // carries only text and image parts (`OcxImageContent`, src/types.ts) and the Codex
335
413
  // catalog normalizes `input_modalities` against a closed enum. Advertising a modality
@@ -641,7 +719,7 @@ export function resolveAntigravityWireModelId(modelId: string, baseUrl?: string)
641
719
  * IS the effort, and sending both creates a contradictory request.
642
720
  */
643
721
  export function isAntigravitySuffixModelId(modelId: string): boolean {
644
- return !(ANTIGRAVITY_MODELS as string[]).includes(modelId);
722
+ return !ANTIGRAVITY_KNOWN_PICKER_MODELS.includes(modelId);
645
723
  }
646
724
 
647
725
  /** The reasoning tier a retired Flash id used to encode, if it is one. */
@@ -753,7 +831,7 @@ export function resolveAntigravityEffortWireModel(
753
831
  // summary aggregation consults this reverse map so one call model = one usage row.
754
832
  const ANTIGRAVITY_USAGE_BASE_BY_ID: Record<string, string> = (() => {
755
833
  const rev: Record<string, string> = {};
756
- for (const base of ANTIGRAVITY_MODELS) rev[base] = base;
834
+ for (const base of ANTIGRAVITY_KNOWN_PICKER_MODELS) rev[base] = base;
757
835
  for (const [base, effortMap] of Object.entries(ANTIGRAVITY_EFFORT_WIRE_MAP)) {
758
836
  rev[base] = base;
759
837
  for (const wire of Object.values(effortMap)) rev[wire] = base;