@iislee/opencodex 2.11.0 → 2.35.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 (733) hide show
  1. package/AGENTS_INSTALL.md +109 -0
  2. package/README.md +117 -22
  3. package/bin/ocx.mjs +245 -88
  4. package/bin/package-main.mjs +1 -1
  5. package/gui/dist/assets/index-Be1sDpWw.css +1 -0
  6. package/gui/dist/assets/index-D4OKgUV4.js +114 -0
  7. package/gui/dist/index.html +2 -2
  8. package/gui/dist/provider-icons/alibaba-color.svg +1 -1
  9. package/gui/dist/provider-icons/antigravity-color.svg +1 -1
  10. package/gui/dist/provider-icons/claude-color.svg +1 -1
  11. package/gui/dist/provider-icons/cline-color.svg +16 -0
  12. package/gui/dist/provider-icons/cloudflare-ai-gateway-color.svg +1 -1
  13. package/gui/dist/provider-icons/commandcode-color.svg +1 -0
  14. package/gui/dist/provider-icons/copilot-color.svg +1 -1
  15. package/gui/dist/provider-icons/cursor-color.svg +1 -1
  16. package/gui/dist/provider-icons/deepseek-color.svg +1 -1
  17. package/gui/dist/provider-icons/firepass-color.svg +1 -1
  18. package/gui/dist/provider-icons/fireworks-color.svg +1 -1
  19. package/gui/dist/provider-icons/gemini-color.svg +1 -1
  20. package/gui/dist/provider-icons/github-copilot-color.svg +1 -1
  21. package/gui/dist/provider-icons/gitlab-duo-color.svg +1 -1
  22. package/gui/dist/provider-icons/grok.svg +1 -1
  23. package/gui/dist/provider-icons/groq-color.svg +1 -1
  24. package/gui/dist/provider-icons/huggingface-color.svg +1 -1
  25. package/gui/dist/provider-icons/kimi-color.svg +1 -1
  26. package/gui/dist/provider-icons/kiro-color.svg +2 -2
  27. package/gui/dist/provider-icons/lm-studio-color.svg +1 -1
  28. package/gui/dist/provider-icons/mistral-color.svg +1 -1
  29. package/gui/dist/provider-icons/moonshot-color.svg +1 -1
  30. package/gui/dist/provider-icons/nvidia-color.svg +1 -1
  31. package/gui/dist/provider-icons/ollama-color.svg +1 -1
  32. package/gui/dist/provider-icons/openai.svg +1 -1
  33. package/gui/dist/provider-icons/opencode.svg +2 -1
  34. package/gui/dist/provider-icons/openrouter-color.svg +1 -1
  35. package/gui/dist/provider-icons/pi.svg +2 -2
  36. package/gui/dist/provider-icons/qianfan-color.svg +1 -1
  37. package/gui/dist/provider-icons/qwen-portal-color.svg +1 -1
  38. package/gui/dist/provider-icons/vercel-ai-gateway-color.svg +1 -1
  39. package/gui/dist/provider-icons/vllm-color.svg +1 -1
  40. package/gui/dist/provider-icons/xiaomi-color.svg +1 -1
  41. package/package.json +21 -10
  42. package/src/adapters/anthropic-output-schema.ts +137 -0
  43. package/src/adapters/anthropic.ts +475 -62
  44. package/src/adapters/base.ts +82 -7
  45. package/src/adapters/client-fingerprint.ts +18 -12
  46. package/src/adapters/cline-pass-deepseek-v4-tool-replay.ts +69 -0
  47. package/src/adapters/command-code.ts +637 -0
  48. package/src/adapters/cursor/call-id.ts +44 -0
  49. package/src/adapters/cursor/catalog.ts +541 -0
  50. package/src/adapters/cursor/checkpoint-store.ts +308 -0
  51. package/src/adapters/cursor/cursor-errors.ts +144 -6
  52. package/src/adapters/cursor/discovery.ts +122 -14
  53. package/src/adapters/cursor/effort-map.ts +106 -4
  54. package/src/adapters/cursor/envelope-echo.ts +290 -0
  55. package/src/adapters/cursor/framing.ts +39 -0
  56. package/src/adapters/cursor/h2-pool.ts +123 -0
  57. package/src/adapters/cursor/http1-bidi.ts +361 -0
  58. package/src/adapters/cursor/images.ts +704 -0
  59. package/src/adapters/cursor/live-models.ts +180 -59
  60. package/src/adapters/cursor/live-transport.ts +623 -170
  61. package/src/adapters/cursor/message-mapper.ts +4 -1
  62. package/src/adapters/cursor/native-exec-common.ts +23 -2
  63. package/src/adapters/cursor/native-exec-desktop.ts +23 -0
  64. package/src/adapters/cursor/native-exec-fs.ts +10 -7
  65. package/src/adapters/cursor/native-exec-network.ts +1 -1
  66. package/src/adapters/cursor/native-exec-shell.ts +5 -3
  67. package/src/adapters/cursor/native-exec.ts +121 -14
  68. package/src/adapters/cursor/protobuf-events.ts +829 -11
  69. package/src/adapters/cursor/protobuf-request.ts +447 -75
  70. package/src/adapters/cursor/request-builder.ts +271 -35
  71. package/src/adapters/cursor/tool-definitions.ts +252 -12
  72. package/src/adapters/cursor/tool-result-normalize.ts +115 -0
  73. package/src/adapters/cursor/transport.ts +22 -0
  74. package/src/adapters/cursor/types.ts +48 -1
  75. package/src/adapters/cursor.ts +336 -39
  76. package/src/adapters/exec-tool-result-normalize.ts +99 -0
  77. package/src/adapters/google-antigravity-replay.ts +713 -41
  78. package/src/adapters/google-antigravity-wire.ts +48 -10
  79. package/src/adapters/google-errors.ts +44 -12
  80. package/src/adapters/google-http.ts +38 -10
  81. package/src/adapters/google-tool-schema.ts +80 -15
  82. package/src/adapters/google-truncation.ts +11 -0
  83. package/src/adapters/google.ts +742 -86
  84. package/src/adapters/identity.ts +39 -6
  85. package/src/adapters/image.ts +1 -1
  86. package/src/adapters/kiro-constants.ts +27 -0
  87. package/src/adapters/kiro-errors.ts +11 -0
  88. package/src/adapters/kiro-events.ts +19 -1
  89. package/src/adapters/kiro-thinking.ts +18 -2
  90. package/src/adapters/kiro-tools.ts +65 -17
  91. package/src/adapters/kiro.ts +270 -73
  92. package/src/adapters/mimo-free.ts +17 -0
  93. package/src/adapters/openai-chat-url.ts +11 -0
  94. package/src/adapters/openai-chat.ts +1377 -344
  95. package/src/adapters/openai-responses-url.ts +16 -0
  96. package/src/adapters/openai-responses.ts +1164 -64
  97. package/src/adapters/registry.ts +175 -0
  98. package/src/adapters/responses-tool-schema.ts +67 -0
  99. package/src/adapters/run-turn-queue.ts +36 -1
  100. package/src/adapters/tool-call-id.ts +119 -0
  101. package/src/adapters/tool-catalog-nudge.ts +105 -21
  102. package/src/adapters/xai-tool-schema.ts +436 -0
  103. package/src/adapters/xai-web-search.ts +186 -0
  104. package/src/bridge.ts +512 -92
  105. package/src/chat/inbound.ts +57 -20
  106. package/src/chat/outbound.ts +97 -33
  107. package/src/claude/agents-inject.ts +40 -10
  108. package/src/claude/context-windows.ts +37 -14
  109. package/src/claude/desktop-3p.ts +243 -9
  110. package/src/claude/gateway-cache.ts +41 -4
  111. package/src/claude/inbound.ts +72 -3
  112. package/src/claude/model-info.ts +38 -15
  113. package/src/claude/outbound.ts +88 -21
  114. package/src/cli/access.ts +46 -3
  115. package/src/cli/account-api.ts +150 -19
  116. package/src/cli/account-auth.ts +33 -6
  117. package/src/cli/account-catalog-refresh.ts +14 -0
  118. package/src/cli/account-extended.ts +648 -28
  119. package/src/cli/account-main.ts +317 -0
  120. package/src/cli/account.ts +97 -19
  121. package/src/cli/agent.ts +53 -2
  122. package/src/cli/alias.ts +66 -0
  123. package/src/cli/capabilities-command.ts +94 -0
  124. package/src/cli/capabilities.ts +496 -0
  125. package/src/cli/claude-agent-startup-sync.ts +73 -0
  126. package/src/cli/claude-desktop.ts +60 -15
  127. package/src/cli/claude.ts +129 -35
  128. package/src/cli/codex-log-guard-doctor.ts +103 -0
  129. package/src/cli/codex-shim-autorestore.ts +2 -0
  130. package/src/cli/codex-shim-readiness.ts +76 -0
  131. package/src/cli/combo.ts +8 -0
  132. package/src/cli/config-command.ts +74 -10
  133. package/src/cli/dispatch.ts +821 -0
  134. package/src/cli/doctor.ts +440 -56
  135. package/src/cli/ensure-desired-integrations.ts +152 -0
  136. package/src/cli/export-command.ts +46 -20
  137. package/src/cli/help.ts +34 -274
  138. package/src/cli/index.ts +417 -528
  139. package/src/cli/init.ts +5 -17
  140. package/src/cli/inspect.ts +230 -0
  141. package/src/cli/integrations.ts +120 -2
  142. package/src/cli/lab.ts +607 -0
  143. package/src/cli/launcher-context.ts +77 -0
  144. package/src/cli/minimax.ts +497 -0
  145. package/src/cli/models-runtime.ts +130 -2
  146. package/src/cli/models.ts +118 -16
  147. package/src/cli/observe.ts +123 -11
  148. package/src/cli/opencode.ts +4 -2
  149. package/src/cli/provider-runtime.ts +34 -3
  150. package/src/cli/provider.ts +25 -3
  151. package/src/cli/ready.ts +301 -0
  152. package/src/cli/registry.ts +476 -0
  153. package/src/cli/root.ts +86 -0
  154. package/src/cli/route-policy.ts +92 -0
  155. package/src/cli/runtime-api.ts +57 -10
  156. package/src/cli/star-prompt.ts +71 -15
  157. package/src/cli/status.ts +51 -4
  158. package/src/cli/storage.ts +234 -0
  159. package/src/cli/system-command.ts +16 -0
  160. package/src/cli/system-restart-client.ts +146 -0
  161. package/src/cli/tray-proxy.ts +153 -6
  162. package/src/cli/usage-report.ts +184 -0
  163. package/src/cli/v2.ts +137 -18
  164. package/src/cli/version-skew.ts +46 -0
  165. package/src/cli.ts +1 -1
  166. package/src/clients/config-export.ts +1442 -23
  167. package/src/clients/effective-status.ts +30 -2
  168. package/src/clients/probes/cline.ts +81 -0
  169. package/src/codex/account-label.ts +35 -1
  170. package/src/codex/account-lifecycle.ts +130 -13
  171. package/src/codex/account-namespaces.ts +49 -3
  172. package/src/codex/account-pause.ts +2 -1
  173. package/src/codex/account-priority.ts +84 -0
  174. package/src/codex/account-store.ts +29 -2
  175. package/src/codex/account-usability.ts +25 -2
  176. package/src/codex/admission.ts +256 -0
  177. package/src/codex/affinity-debug.ts +162 -0
  178. package/src/codex/app-server-processes.ts +570 -107
  179. package/src/codex/app-server-restart-service.ts +232 -0
  180. package/src/codex/auth-api.ts +893 -246
  181. package/src/codex/auth-collision.ts +5 -3
  182. package/src/codex/auth-context.ts +348 -32
  183. package/src/codex/autostart-health.ts +24 -1
  184. package/src/codex/catalog/account-models.ts +67 -0
  185. package/src/codex/catalog/aggregation.ts +86 -10
  186. package/src/codex/catalog/bundled.ts +331 -33
  187. package/src/codex/catalog/effort.ts +138 -32
  188. package/src/codex/catalog/filesystem-evidence.ts +302 -0
  189. package/src/codex/catalog/kinds.ts +2 -0
  190. package/src/codex/catalog/metadata.ts +555 -45
  191. package/src/codex/catalog/native-models.ts +75 -0
  192. package/src/codex/catalog/parsing.ts +269 -30
  193. package/src/codex/catalog/provider-fetch.ts +1626 -144
  194. package/src/codex/catalog/sync.ts +1576 -187
  195. package/src/codex/catalog-admission.ts +199 -0
  196. package/src/codex/catalog-refresh-status.ts +105 -0
  197. package/src/codex/catalog-write-serialization.ts +242 -0
  198. package/src/codex/catalog.ts +6 -3
  199. package/src/codex/codex-write-lock.ts +386 -0
  200. package/src/codex/convergence-types.ts +614 -0
  201. package/src/codex/convergence.ts +676 -0
  202. package/src/codex/coordinator-doctor.ts +332 -0
  203. package/src/codex/custom-model-catalog-migration.ts +176 -0
  204. package/src/codex/desired-state.ts +230 -0
  205. package/src/codex/desktop-app-restart.ts +355 -0
  206. package/src/codex/features.ts +636 -39
  207. package/src/codex/generation.ts +202 -0
  208. package/src/codex/history-job.ts +436 -0
  209. package/src/codex/history-lock.ts +242 -0
  210. package/src/codex/history-manifest.ts +112 -0
  211. package/src/codex/history-migration-guardian.ts +30 -24
  212. package/src/codex/history-provider.ts +1016 -235
  213. package/src/codex/history-transition.ts +105 -0
  214. package/src/codex/history-worker.ts +223 -0
  215. package/src/codex/inject-coordination.ts +373 -0
  216. package/src/codex/inject.ts +1114 -152
  217. package/src/codex/injected-marker.ts +37 -3
  218. package/src/codex/integration-record.ts +266 -0
  219. package/src/codex/internal/catalog-writer.ts +203 -0
  220. package/src/codex/internal/history-writer.ts +81 -0
  221. package/src/codex/journal.ts +66 -4
  222. package/src/codex/log-guard/inspect.ts +524 -0
  223. package/src/codex/log-guard/lock.ts +150 -0
  224. package/src/codex/log-guard/maintenance.ts +403 -0
  225. package/src/codex/log-guard/path-safety.ts +88 -0
  226. package/src/codex/log-guard/policy.ts +44 -0
  227. package/src/codex/log-guard/processes.ts +205 -0
  228. package/src/codex/log-guard/protection.ts +489 -0
  229. package/src/codex/log-guard/sqlite-errors.ts +9 -0
  230. package/src/codex/main-account-cache.ts +24 -0
  231. package/src/codex/main-account.ts +29 -1
  232. package/src/codex/management-convergence.ts +167 -0
  233. package/src/codex/model-cache.ts +56 -10
  234. package/src/codex/model-entitlements.ts +360 -0
  235. package/src/codex/native-main-admission.ts +47 -0
  236. package/src/codex/native-main-auth-temp.ts +187 -0
  237. package/src/codex/native-main-claim.ts +178 -0
  238. package/src/codex/native-main-lock-file.ts +162 -0
  239. package/src/codex/native-main-owner.ts +329 -0
  240. package/src/codex/native-profile-api.ts +247 -0
  241. package/src/codex/native-profile-manager.ts +1531 -0
  242. package/src/codex/native-profile-processes.ts +121 -0
  243. package/src/codex/native-profile-recovery.ts +99 -0
  244. package/src/codex/native-profile-stage-store.ts +387 -0
  245. package/src/codex/native-profile-startup.ts +622 -0
  246. package/src/codex/native-profile-store.ts +855 -0
  247. package/src/codex/native-profile-types.ts +120 -0
  248. package/src/codex/native-residue.ts +675 -0
  249. package/src/codex/paths.ts +80 -1
  250. package/src/codex/plan-from-token.ts +140 -0
  251. package/src/codex/plan.ts +40 -0
  252. package/src/codex/plugins-doctor.ts +1 -1
  253. package/src/codex/pool-rotation.ts +74 -4
  254. package/src/codex/project-config-warnings.ts +20 -6
  255. package/src/codex/prompt-journal.ts +352 -0
  256. package/src/codex/prompt-layers.ts +1313 -0
  257. package/src/codex/prompt-lock.ts +143 -0
  258. package/src/codex/prompt-text-probe.ts +238 -0
  259. package/src/codex/quota-rejection.ts +298 -0
  260. package/src/codex/quota.ts +264 -21
  261. package/src/codex/refresh.ts +11 -2
  262. package/src/codex/reset-credit-recovery.ts +1044 -0
  263. package/src/codex/routing.ts +514 -94
  264. package/src/codex/runtime.ts +159 -38
  265. package/src/codex/shim.ts +1065 -31
  266. package/src/codex/subagent-model-fallback.ts +437 -43
  267. package/src/codex/sync.ts +191 -2
  268. package/src/codex/transition-state.ts +720 -0
  269. package/src/codex/upstream-host-health.ts +368 -0
  270. package/src/codex/user-identity.ts +557 -0
  271. package/src/codex/warmup.ts +187 -81
  272. package/src/codex/write-coordination.ts +114 -0
  273. package/src/combos/failover.ts +47 -0
  274. package/src/combos/index.ts +4 -0
  275. package/src/combos/request.ts +32 -0
  276. package/src/combos/types.ts +91 -9
  277. package/src/compatibility/index.ts +26 -0
  278. package/src/compatibility/manifest.ts +253 -0
  279. package/src/compatibility/openai-responses.ts +121 -0
  280. package/src/config/atomic-write.ts +219 -0
  281. package/src/config/paths.ts +40 -0
  282. package/src/config/process-state.ts +309 -0
  283. package/src/config/provider-name.ts +24 -0
  284. package/src/config/provider-validation.ts +177 -0
  285. package/src/config/rebase-provenance.ts +68 -0
  286. package/src/config.ts +1713 -709
  287. package/src/generated/compatibility-version.json +3324 -0
  288. package/src/generated/{jawcode-model-metadata.ts → model-metadata.ts} +19 -17
  289. package/src/grok/inject.ts +16 -5
  290. package/src/grok/inspect.ts +45 -0
  291. package/src/grok/sync.ts +2 -2
  292. package/src/images/loop.ts +157 -33
  293. package/src/images/plan.ts +24 -13
  294. package/src/integrations/config-io.ts +269 -0
  295. package/src/integrations/journal.ts +315 -0
  296. package/src/integrations/merge.ts +135 -0
  297. package/src/integrations/mutation-flight.ts +71 -0
  298. package/src/integrations/native/ownership-preflight.ts +202 -0
  299. package/src/integrations/omp-yaml-source.ts +358 -0
  300. package/src/integrations/owned-refresh.ts +74 -0
  301. package/src/integrations/ownership-policy.ts +160 -0
  302. package/src/integrations/ownership.ts +155 -0
  303. package/src/integrations/registry.ts +166 -0
  304. package/src/integrations/serialize.ts +314 -0
  305. package/src/integrations/state.ts +433 -0
  306. package/src/integrations/store.ts +103 -0
  307. package/src/integrations/writer-lock.ts +98 -0
  308. package/src/integrations/writer.ts +715 -0
  309. package/src/lab/artifacts/sanitize.ts +586 -0
  310. package/src/lab/artifacts/secure-fs.ts +475 -0
  311. package/src/lab/artifacts/store.ts +310 -0
  312. package/src/lab/automation/budgets.ts +78 -0
  313. package/src/lab/automation/config-persistence.ts +256 -0
  314. package/src/lab/automation/constants.ts +39 -0
  315. package/src/lab/automation/cooldown.ts +103 -0
  316. package/src/lab/automation/dispatch.ts +211 -0
  317. package/src/lab/automation/index.ts +13 -0
  318. package/src/lab/automation/orchestrator.ts +499 -0
  319. package/src/lab/automation/persistence.ts +512 -0
  320. package/src/lab/automation/planner.ts +371 -0
  321. package/src/lab/automation/policy.ts +136 -0
  322. package/src/lab/automation/queue.ts +191 -0
  323. package/src/lab/automation/recovery.ts +24 -0
  324. package/src/lab/automation/route-context.ts +21 -0
  325. package/src/lab/automation/run-key.ts +44 -0
  326. package/src/lab/automation/runs-query.ts +34 -0
  327. package/src/lab/automation/types.ts +160 -0
  328. package/src/lab/conformance/assertion.ts +325 -0
  329. package/src/lab/conformance/digest.ts +22 -0
  330. package/src/lab/conformance/executor.ts +741 -0
  331. package/src/lab/conformance/fixture-provider.ts +27 -0
  332. package/src/lab/conformance/fixtures/live-v1-cases.json +175 -0
  333. package/src/lab/conformance/fixtures/protocol-v1-cases.json +461 -0
  334. package/src/lab/conformance/harness-budget.ts +47 -0
  335. package/src/lab/conformance/index.ts +5 -0
  336. package/src/lab/conformance/jcs.ts +64 -0
  337. package/src/lab/conformance/json-pointer.ts +39 -0
  338. package/src/lab/conformance/manifest.ts +180 -0
  339. package/src/lab/conformance/mcp-stub.ts +179 -0
  340. package/src/lab/conformance/negative-controls.ts +164 -0
  341. package/src/lab/conformance/observation.ts +355 -0
  342. package/src/lab/conformance/runner.ts +68 -0
  343. package/src/lab/conformance/sse-normalize.ts +59 -0
  344. package/src/lab/conformance/suite-manifest.ts +78 -0
  345. package/src/lab/conformance/types.ts +214 -0
  346. package/src/lab/constants.ts +126 -0
  347. package/src/lab/digest.ts +64 -0
  348. package/src/lab/events/errors.ts +9 -0
  349. package/src/lab/events/limits.ts +117 -0
  350. package/src/lab/events/types.ts +229 -0
  351. package/src/lab/events/validate.ts +781 -0
  352. package/src/lab/fabric/constants.ts +40 -0
  353. package/src/lab/fabric/executor.ts +492 -0
  354. package/src/lab/fabric/index.ts +80 -0
  355. package/src/lab/fabric/manifest.ts +222 -0
  356. package/src/lab/fabric/observe.ts +489 -0
  357. package/src/lab/fabric/patch.ts +79 -0
  358. package/src/lab/fabric/producer-child.ts +139 -0
  359. package/src/lab/fabric/producer-isolate.ts +276 -0
  360. package/src/lab/fabric/producer-protocol.ts +61 -0
  361. package/src/lab/fabric/scratch.ts +439 -0
  362. package/src/lab/fabric/subject.ts +106 -0
  363. package/src/lab/fabric/types.ts +134 -0
  364. package/src/lab/fabric/verifier.ts +98 -0
  365. package/src/lab/index.ts +54 -0
  366. package/src/lab/ledger/artifact-refs.ts +127 -0
  367. package/src/lab/ledger/invalidation.ts +136 -0
  368. package/src/lab/ledger/purge.ts +310 -0
  369. package/src/lab/ledger/store.ts +532 -0
  370. package/src/lab/live/credential-lease.ts +53 -0
  371. package/src/lab/live/destination.ts +155 -0
  372. package/src/lab/live/executor.ts +336 -0
  373. package/src/lab/live/inert-tools.ts +56 -0
  374. package/src/lab/live/manifest.ts +85 -0
  375. package/src/lab/live/mcp-loopback.ts +57 -0
  376. package/src/lab/live/runner.ts +19 -0
  377. package/src/lab/live/sandbox.ts +61 -0
  378. package/src/lab/live/suite-manifest.ts +41 -0
  379. package/src/lab/live/transport.ts +118 -0
  380. package/src/lab/live/types.ts +197 -0
  381. package/src/lab/observe/from-conformance.ts +301 -0
  382. package/src/lab/observe/from-live.ts +117 -0
  383. package/src/lab/paths.ts +153 -0
  384. package/src/lab/projection/rebuild.ts +495 -0
  385. package/src/lab/projection/schema.ts +135 -0
  386. package/src/lab/projection/verdicts.ts +474 -0
  387. package/src/lab/projection/verification.ts +412 -0
  388. package/src/lab/public/bundle.ts +217 -0
  389. package/src/lab/public/community-authority.ts +175 -0
  390. package/src/lab/public/community-files.ts +29 -0
  391. package/src/lab/public/community.ts +479 -0
  392. package/src/lab/public/file-safety.ts +155 -0
  393. package/src/lab/public/ids.ts +26 -0
  394. package/src/lab/public/index.ts +16 -0
  395. package/src/lab/public/mutation-lock.ts +424 -0
  396. package/src/lab/public/operator.ts +353 -0
  397. package/src/lab/public/origin-purge.ts +79 -0
  398. package/src/lab/public/origin.ts +203 -0
  399. package/src/lab/public/privacy.ts +143 -0
  400. package/src/lab/public/private-file.ts +261 -0
  401. package/src/lab/public/project.ts +124 -0
  402. package/src/lab/public/purge-test-fault.ts +21 -0
  403. package/src/lab/public/purge.ts +223 -0
  404. package/src/lab/public/registry.ts +44 -0
  405. package/src/lab/public/revocation.ts +252 -0
  406. package/src/lab/public/signature.ts +243 -0
  407. package/src/lab/public/storage.ts +105 -0
  408. package/src/lab/public/strict-json.ts +206 -0
  409. package/src/lab/public/time.ts +26 -0
  410. package/src/lab/public/types.ts +172 -0
  411. package/src/lab/public/validate.ts +391 -0
  412. package/src/lab/query/catalog.ts +101 -0
  413. package/src/lab/query/connection.ts +107 -0
  414. package/src/lab/query/constants.ts +4 -0
  415. package/src/lab/query/cursor.ts +132 -0
  416. package/src/lab/query/dto-map.ts +277 -0
  417. package/src/lab/query/errors.ts +22 -0
  418. package/src/lab/query/freshness.ts +53 -0
  419. package/src/lab/query/index.ts +45 -0
  420. package/src/lab/query/latest-observation.ts +59 -0
  421. package/src/lab/query/passive-production.ts +159 -0
  422. package/src/lab/query/queries.ts +444 -0
  423. package/src/lab/query/types.ts +266 -0
  424. package/src/lab/subject/behavior-fingerprint.ts +77 -0
  425. package/src/lab/subject/installation-salt.ts +112 -0
  426. package/src/lab/subject/protocol-subject.ts +80 -0
  427. package/src/lab/subject/route-subject.ts +74 -0
  428. package/src/lib/admin-secrets.ts +24 -0
  429. package/src/lib/app-owned-memory-stores.ts +22 -0
  430. package/src/lib/bounded-body.ts +167 -11
  431. package/src/lib/bun-runtime.ts +125 -12
  432. package/src/lib/bun-stream-caps.ts +13 -9
  433. package/src/lib/codex-restart-contract.ts +120 -0
  434. package/src/lib/config-ownership.ts +26 -2
  435. package/src/lib/destination-policy.ts +65 -1
  436. package/src/lib/errors.ts +80 -5
  437. package/src/lib/fabric-task-execution-authority.ts +7 -0
  438. package/src/lib/fabric-task-host.ts +29 -0
  439. package/src/lib/lab-activation.ts +223 -0
  440. package/src/lib/lab-live-execution-authority.ts +13 -0
  441. package/src/lib/lab-live-host.ts +30 -0
  442. package/src/lib/lab-live-pinned-sender.ts +56 -0
  443. package/src/lib/lab-live-route-production.ts +130 -0
  444. package/src/lib/lab-passive-linker-registration.ts +26 -0
  445. package/src/lib/local-management-attestation.ts +51 -0
  446. package/src/lib/local-management-capability.ts +100 -0
  447. package/src/lib/local-provider-reload-contract.ts +100 -0
  448. package/src/lib/optional-shutdown-hooks.ts +57 -0
  449. package/src/lib/package-tree-integrity.ts +101 -0
  450. package/src/lib/pinned-http.ts +145 -26
  451. package/src/lib/process-control.ts +6 -2
  452. package/src/lib/provider-outbound.ts +49 -9
  453. package/src/lib/redact.ts +419 -3
  454. package/src/lib/self-launch-argv.ts +15 -0
  455. package/src/lib/server-resource-ownership.ts +71 -0
  456. package/src/lib/service-secrets.ts +15 -0
  457. package/src/lib/shadow-call.ts +35 -4
  458. package/src/lib/sse-decoder.ts +41 -0
  459. package/src/lib/state-store-registrations.ts +10 -2
  460. package/src/lib/system-restart-contract.ts +73 -0
  461. package/src/lib/token-estimate.ts +19 -2
  462. package/src/lib/tool-argument-integers.ts +253 -0
  463. package/src/lib/translator-budget.ts +44 -0
  464. package/src/lib/upstream-http-version.ts +57 -0
  465. package/src/lib/upstream-reachability.ts +95 -0
  466. package/src/lib/upstream-retry.ts +156 -3
  467. package/src/lib/windows-atomic-replace.ts +155 -0
  468. package/src/lib/windows-elevation.ts +70 -2
  469. package/src/lib/windows-secret-acl.ts +409 -69
  470. package/src/lib/windows-service-wrappers.ts +72 -0
  471. package/src/lib/windows-text.ts +106 -0
  472. package/src/lib/windows-user-principal.ts +341 -0
  473. package/src/lib/winsw.ts +33 -5
  474. package/src/oauth/account-import/google-antigravity-adapter.ts +74 -0
  475. package/src/oauth/account-import/index.ts +15 -0
  476. package/src/oauth/account-import/parser.ts +83 -0
  477. package/src/oauth/account-import/registry.ts +18 -0
  478. package/src/oauth/account-import/service.ts +75 -0
  479. package/src/oauth/account-import/types.ts +91 -0
  480. package/src/oauth/anthropic.ts +12 -1
  481. package/src/oauth/callback-server.ts +30 -4
  482. package/src/oauth/chatgpt.ts +12 -1
  483. package/src/oauth/cline.ts +203 -0
  484. package/src/oauth/command-code.ts +239 -0
  485. package/src/oauth/cursor.ts +46 -5
  486. package/src/oauth/generic-account-failover.ts +231 -0
  487. package/src/oauth/google-antigravity.ts +35 -3
  488. package/src/oauth/health.ts +20 -12
  489. package/src/oauth/index.ts +462 -71
  490. package/src/oauth/key-providers.ts +25 -0
  491. package/src/oauth/kimi.ts +25 -3
  492. package/src/oauth/kiro.ts +95 -6
  493. package/src/oauth/local-token-detect.ts +13 -2
  494. package/src/oauth/log.ts +3 -1
  495. package/src/oauth/login-cli.ts +88 -28
  496. package/src/oauth/nous.ts +798 -0
  497. package/src/oauth/open-browser-choice.ts +26 -0
  498. package/src/oauth/store.ts +133 -26
  499. package/src/oauth/token-guardian.ts +9 -3
  500. package/src/oauth/types.ts +15 -0
  501. package/src/pi/models.ts +2 -2
  502. package/src/providers/alibaba-region-migration.ts +1 -1
  503. package/src/providers/antigravity-models.ts +521 -31
  504. package/src/providers/auto-compact-budget.ts +65 -0
  505. package/src/providers/base-url-choices.ts +10 -0
  506. package/src/providers/codex-capacity.ts +292 -0
  507. package/src/providers/command-code-efforts.ts +176 -0
  508. package/src/providers/context-cap.ts +26 -8
  509. package/src/providers/cursor-pool.ts +72 -0
  510. package/src/providers/default-aliases.ts +65 -0
  511. package/src/providers/derive.ts +281 -6
  512. package/src/providers/fastwire.ts +507 -0
  513. package/src/providers/free-directory.ts +10 -7
  514. package/src/providers/google-vertex-location.ts +14 -0
  515. package/src/providers/key-failover.ts +71 -3
  516. package/src/providers/label.ts +35 -2
  517. package/src/providers/model-discovery-limits.ts +16 -0
  518. package/src/providers/model-discovery.ts +115 -22
  519. package/src/providers/model-presets.ts +119 -0
  520. package/src/providers/model-rename-migration.ts +255 -0
  521. package/src/providers/model-rename-startup.ts +28 -0
  522. package/src/providers/new-model-policy.ts +146 -0
  523. package/src/providers/openai-sidecar.ts +72 -4
  524. package/src/providers/openai-tier-startup.ts +31 -2
  525. package/src/providers/openai-tiers.ts +119 -4
  526. package/src/providers/openai-virtual-models.ts +1 -0
  527. package/src/providers/opencode-zen-rate-limit.ts +102 -0
  528. package/src/providers/provider-id-rewrite.ts +30 -0
  529. package/src/providers/quota.ts +1379 -40
  530. package/src/providers/registry.ts +1545 -112
  531. package/src/providers/request-pacing.ts +310 -0
  532. package/src/providers/service-tier.ts +277 -0
  533. package/src/providers/slug-codec.ts +94 -6
  534. package/src/providers/static-model-discovery.ts +86 -0
  535. package/src/providers/xai-responses-opt-in.ts +15 -0
  536. package/src/providers/xai-transport.ts +32 -4
  537. package/src/reasoning-effort.ts +68 -3
  538. package/src/responses/apply-patch-envelope.ts +63 -0
  539. package/src/responses/code-mode-helper-compat.ts +50 -0
  540. package/src/responses/compaction.ts +26 -1
  541. package/src/responses/custom-tool-compat.ts +384 -0
  542. package/src/responses/hosted-tool-policy.ts +9 -0
  543. package/src/responses/namespace-tool-compat.ts +435 -0
  544. package/src/responses/parser.ts +225 -38
  545. package/src/responses/provider-continuation.ts +98 -0
  546. package/src/responses/provider-opaque-metadata.ts +73 -0
  547. package/src/responses/reasoning-envelope.ts +9 -1
  548. package/src/responses/reasoning-replay-cache.ts +504 -0
  549. package/src/responses/schema.ts +12 -2
  550. package/src/responses/spill-store.ts +75 -10
  551. package/src/responses/state.ts +659 -31
  552. package/src/responses/thought-signature-replay.ts +364 -0
  553. package/src/responses/tool-search-compat.ts +301 -0
  554. package/src/responses/truncated-stop-reason.ts +60 -0
  555. package/src/responses/turn-termination.ts +107 -0
  556. package/src/router.ts +405 -31
  557. package/src/routing/analytics.ts +379 -0
  558. package/src/routing/capability.ts +244 -0
  559. package/src/routing/compatibility/assemble.ts +73 -0
  560. package/src/routing/compatibility/behavior.ts +278 -0
  561. package/src/routing/compatibility/catalog.ts +99 -0
  562. package/src/routing/compatibility/endpoint.ts +52 -0
  563. package/src/routing/compatibility/lab-evidence-provider.ts +130 -0
  564. package/src/routing/compatibility/policy.ts +181 -0
  565. package/src/routing/compatibility/provider-slot.ts +56 -0
  566. package/src/routing/compatibility/reader.ts +110 -0
  567. package/src/routing/compatibility/subject.ts +191 -0
  568. package/src/routing/compatibility/types.ts +64 -0
  569. package/src/routing/compatibility/version.ts +104 -0
  570. package/src/routing/cost.ts +77 -0
  571. package/src/routing/evaluator.ts +495 -0
  572. package/src/routing/health.ts +412 -0
  573. package/src/routing/history/cursor.ts +43 -0
  574. package/src/routing/history/indexer.ts +605 -0
  575. package/src/routing/history/schema.ts +72 -0
  576. package/src/routing/profile-namespace.ts +15 -0
  577. package/src/routing/profile.ts +547 -0
  578. package/src/routing/quota.ts +155 -0
  579. package/src/routing/request-evidence.ts +45 -0
  580. package/src/routing/trace.ts +776 -0
  581. package/src/server/adapter-resolve.ts +2 -29
  582. package/src/server/auth-cors.ts +289 -48
  583. package/src/server/background-lifecycle.ts +182 -0
  584. package/src/server/chat-completions.ts +154 -72
  585. package/src/server/chat-native-sse.ts +331 -0
  586. package/src/server/chat-native.ts +445 -0
  587. package/src/server/claude-messages.ts +160 -44
  588. package/src/server/direct-local-http.ts +347 -0
  589. package/src/server/effort-policy.ts +18 -0
  590. package/src/server/github-copilot-responses-repair.ts +338 -0
  591. package/src/server/gui-static.ts +39 -10
  592. package/src/server/images.ts +94 -12
  593. package/src/server/index.ts +1021 -190
  594. package/src/server/lifecycle.ts +336 -14
  595. package/src/server/live.ts +136 -17
  596. package/src/server/local-management-read-client.ts +90 -0
  597. package/src/server/local-provider-reload-client.ts +137 -0
  598. package/src/server/management/agent-settings-routes.ts +433 -116
  599. package/src/server/management/api-key-usage.ts +31 -5
  600. package/src/server/management/body.ts +6 -0
  601. package/src/server/management/codex-prompt-routes.ts +570 -0
  602. package/src/server/management/combo-routes.ts +64 -25
  603. package/src/server/management/config-routes.ts +511 -51
  604. package/src/server/management/context.ts +89 -2
  605. package/src/server/management/integration-routes.ts +498 -0
  606. package/src/server/management/lab-automation-routes.ts +206 -0
  607. package/src/server/management/lab-routes.ts +563 -0
  608. package/src/server/management/logs-usage-routes.ts +127 -52
  609. package/src/server/management/model-routes.ts +461 -133
  610. package/src/server/management/model-rows.ts +167 -0
  611. package/src/server/management/native-integration-routes.ts +770 -0
  612. package/src/server/management/oauth-account-routes.ts +90 -8
  613. package/src/server/management/provider-capability-config.ts +48 -0
  614. package/src/server/management/provider-routes.ts +836 -159
  615. package/src/server/management/request-history-routes.ts +191 -0
  616. package/src/server/management/route-registry.ts +332 -0
  617. package/src/server/management/routing-analytics-routes.ts +74 -0
  618. package/src/server/management/routing-profile-routes.ts +380 -0
  619. package/src/server/management/shared.ts +27 -11
  620. package/src/server/management/sidebar-routes.ts +47 -31
  621. package/src/server/management/storage-log-guard-routes.ts +186 -0
  622. package/src/server/management/sync-response.ts +69 -0
  623. package/src/server/management/system-restart.ts +289 -33
  624. package/src/server/management/system-routes.ts +77 -0
  625. package/src/server/management/usage-summary-cache.ts +9 -1
  626. package/src/server/management/vision-sidecar-options.ts +167 -0
  627. package/src/server/management/web-search-sidecar-options.ts +120 -0
  628. package/src/server/management-api.ts +126 -23
  629. package/src/server/management-auth.ts +220 -5
  630. package/src/server/passive-route-linker.ts +66 -0
  631. package/src/server/port-reclaim.ts +1 -1
  632. package/src/server/ports.ts +41 -1
  633. package/src/server/proxy-liveness.ts +161 -10
  634. package/src/server/readiness.ts +99 -0
  635. package/src/server/relay-eager.ts +221 -54
  636. package/src/server/relay.ts +479 -87
  637. package/src/server/request-decompress.ts +113 -6
  638. package/src/server/request-log-conversation.ts +51 -0
  639. package/src/server/request-log.ts +298 -31
  640. package/src/server/responses/agent-task-recovery-cache.ts +143 -0
  641. package/src/server/responses/agent-task-recovery.ts +465 -0
  642. package/src/server/responses/codex-auth-error.ts +55 -0
  643. package/src/server/responses/collaboration.ts +239 -36
  644. package/src/server/responses/combo-stream-preflight.ts +178 -0
  645. package/src/server/responses/compact.ts +444 -72
  646. package/src/server/responses/core.ts +3579 -436
  647. package/src/server/responses/empty-completion-guard.ts +333 -0
  648. package/src/server/responses/encrypted-payload.ts +62 -39
  649. package/src/server/responses/fetch-helpers.ts +89 -99
  650. package/src/server/responses/input-admission.ts +187 -0
  651. package/src/server/responses/pacing-overload.ts +13 -0
  652. package/src/server/responses/passthrough-error.ts +33 -9
  653. package/src/server/responses/policy-fallback.ts +178 -0
  654. package/src/server/responses/responses-field-backfill.ts +343 -0
  655. package/src/server/responses/terminal-guard.ts +26 -5
  656. package/src/server/responses/upstream-error.ts +5 -0
  657. package/src/server/responses/ws-upstream.ts +381 -0
  658. package/src/server/responses-custom-tool-repair.ts +363 -0
  659. package/src/server/responses-item-id-repair.ts +54 -6
  660. package/src/server/responses-json-events.ts +90 -0
  661. package/src/server/responses-model-rewrite.ts +29 -0
  662. package/src/server/responses-reasoning-summary-rewrite.ts +178 -0
  663. package/src/server/responses-snapshot-repair.ts +621 -0
  664. package/src/server/responses-terminal-repair.ts +363 -0
  665. package/src/server/responses-tool-search-repair.ts +267 -0
  666. package/src/server/responses-undeclared-tool-guard.ts +376 -0
  667. package/src/server/responses.ts +18 -2
  668. package/src/server/search.ts +78 -13
  669. package/src/server/sse-frame-buffer.ts +319 -0
  670. package/src/server/sse-payload-rewrite.ts +110 -22
  671. package/src/server/startup-action-control.ts +8 -1
  672. package/src/server/startup-health-cache.ts +19 -1
  673. package/src/server/system-env.ts +80 -9
  674. package/src/server/ws-bridge.ts +53 -40
  675. package/src/service-manager-probe.ts +892 -0
  676. package/src/service.ts +1153 -94
  677. package/src/sidecar/auth.ts +92 -0
  678. package/src/sidecar/candidates.ts +83 -0
  679. package/src/storage/cleanup.ts +2 -2
  680. package/src/storage/policy-job.ts +14 -4
  681. package/src/storage/policy.ts +88 -23
  682. package/src/storage/scanner.ts +1 -1
  683. package/src/storage/worker-lifecycle.ts +14 -14
  684. package/src/tray/windows-tray.ps1 +83 -9
  685. package/src/tray/windows.ts +43 -16
  686. package/src/types/accounts.ts +37 -0
  687. package/src/types/config.ts +911 -0
  688. package/src/types/provider.ts +614 -0
  689. package/src/types/request.ts +397 -0
  690. package/src/types/tools.ts +236 -0
  691. package/src/types/wire.ts +80 -0
  692. package/src/types.ts +107 -1237
  693. package/src/update/index.ts +37 -23
  694. package/src/update/job.ts +445 -68
  695. package/src/update/notify.ts +12 -6
  696. package/src/update/npm-cache-preflight.d.mts +47 -0
  697. package/src/update/npm-cache-preflight.mjs +201 -0
  698. package/src/update/transactional-install.d.mts +22 -0
  699. package/src/update/transactional-install.mjs +266 -0
  700. package/src/usage/cost.ts +0 -0
  701. package/src/usage/expected-prices.ts +287 -16
  702. package/src/usage/log.ts +666 -50
  703. package/src/usage/summary.ts +388 -17
  704. package/src/usage/user-cost-overlay-reconciler.ts +313 -0
  705. package/src/usage/user-cost-overlays.ts +314 -0
  706. package/src/vision/anthropic-describe.ts +10 -6
  707. package/src/vision/backends.ts +97 -0
  708. package/src/vision/describe.ts +27 -16
  709. package/src/vision/eligibility.ts +250 -0
  710. package/src/vision/index.ts +238 -24
  711. package/src/vision/reasoning.ts +55 -0
  712. package/src/vision/routed-describe.ts +175 -0
  713. package/src/vision/timeout-bounds.ts +9 -0
  714. package/src/web-search/anthropic-executor.ts +13 -7
  715. package/src/web-search/backends.ts +108 -0
  716. package/src/web-search/exa-executor.ts +119 -0
  717. package/src/web-search/executor.ts +21 -6
  718. package/src/web-search/gemini-executor.ts +141 -0
  719. package/src/web-search/index.ts +150 -15
  720. package/src/web-search/loop.ts +284 -54
  721. package/src/web-search/parse.ts +125 -30
  722. package/src/web-search/sources.ts +60 -0
  723. package/src/web-search/xai-executor.ts +219 -0
  724. package/gui/dist/assets/index-DTpMHS4F.js +0 -67
  725. package/gui/dist/assets/index-ZNVDE3C7.css +0 -1
  726. package/gui/dist/provider-icons/antigravity.svg +0 -1
  727. package/gui/dist/provider-icons/claude.svg +0 -1
  728. package/gui/dist/provider-icons/copilot.svg +0 -1
  729. package/gui/dist/provider-icons/cursor.svg +0 -2
  730. package/gui/dist/provider-icons/gemini.svg +0 -1
  731. package/gui/dist/provider-icons/grok-color.svg +0 -1
  732. package/gui/dist/provider-icons/kiro.svg +0 -14
  733. package/src/cli/internal-dispatch.ts +0 -20
@@ -1,19 +1,24 @@
1
1
  import type { KiroOAuthMetadata, OAuthController, OAuthCredentials } from "./types";
2
2
  import { parseCallbackInput } from "./callback-server";
3
3
  import type { OcxConfig, OcxProviderConfig, RefreshPolicy } from "../types";
4
+ import { maskEmail } from "../lib/privacy";
4
5
  import { loadConfig, resolveEnvValue, saveConfig } from "../config";
5
6
  import { KiroTokenRefreshError, environmentKiroRoutingMetadata, loginKiro, refreshKiroToken, settleKiroLoginTransaction } from "./kiro";
6
- import { getAccountCredential, getAccountSet, removeAccount, saveAccountCredential, saveCredential, setActiveAccount, getCredential, credentialGeneration, createOAuthRefreshIntentLock, mergeAccountCredential, markAccountNeedsReauthIfGeneration, readOAuthRefreshIntent, writeOAuthRefreshIntent, markOAuthRefreshIntentStaleOwner, clearOAuthRefreshIntent, OAuthMutationBusyError } from "./store";
7
+ import { getAccountCredential, getAccountSet, removeAccount, saveAccountCredential, saveCredential, setActiveAccount, getCredential, credentialGeneration, createOAuthRefreshIntentLock, mergeAccountCredential, markAccountNeedsReauthIfGeneration, readOAuthRefreshIntent, writeOAuthRefreshIntent, markOAuthRefreshIntentStaleOwner, clearOAuthRefreshIntent, normalizeAuthStoreBuffer, OAuthMutationBusyError } from "./store";
7
8
  import { loginXai, refreshXaiToken, XAI_LOCAL_CLI_DETACH_WARNING, XaiTokenRequestError } from "./xai";
8
9
  import { ANTHROPIC_OAUTH_BETA, AnthropicTokenError, loginAnthropic, refreshAnthropicToken } from "./anthropic";
9
10
  import { loginKimi, refreshKimiToken } from "./kimi";
11
+ import { loginNous, NousTokenError, refreshNousToken, clearNousRefreshIntent, RefreshIntentIOError } from "./nous";
10
12
  import { loginChatGPT, refreshChatGPTToken } from "./chatgpt";
11
13
  import { loginAntigravity, refreshAntigravityToken } from "./google-antigravity";
12
14
  import { loginCursor, refreshCursorToken } from "./cursor";
13
15
  import { loginGithubCopilot, refreshGithubCopilotToken, validateCopilotApiBaseUrl } from "./github-copilot";
16
+ import { loginCommandCode, refreshCommandCodeToken } from "./command-code";
17
+ import { loginCline, refreshClineToken } from "./cline";
18
+ import { ANTIGRAVITY_REQUEST_UA } from "../adapters/google-antigravity-wire";
14
19
  import { deriveOAuthDefaultModel, deriveOAuthProviderConfig } from "../providers/derive";
15
20
  import { apiKeyPoolEntryId, sanitizeApiKeyValue } from "../providers/api-keys";
16
- import { effectiveGoogleMode, getProviderRegistryEntry, providerMatchesRegistryTransport } from "../providers/registry";
21
+ import { effectiveGoogleMode, getProviderRegistryEntry, mergeRegistryStaticHeaders, providerMatchesRegistryTransport } from "../providers/registry";
17
22
  import { resolveProviderModelDiscoveryUrl } from "../providers/model-discovery";
18
23
  import { resolveProviderTransport } from "../providers/xai-transport";
19
24
  import { detectClaudeCodeToken, detectGrokCliToken, hasComparableGrokIdentity, isSameGrokIdentity, shouldAdoptGrokGeneration } from "./local-token-detect";
@@ -51,10 +56,35 @@ export interface OAuthAccessSnapshot {
51
56
  accountId: string;
52
57
  generation: string;
53
58
  accessToken: string;
59
+ /** Cloud Code Assist project selected during Antigravity login. */
60
+ projectId?: string;
54
61
  /** Safe request-routing subset; refresh-only Kiro client secrets never leave the credential store. */
55
- kiro?: Pick<KiroOAuthMetadata, "profileArn" | "apiRegion" | "ssoRegion">;
62
+ kiro?: Pick<KiroOAuthMetadata, "profileArn" | "apiRegion" | "ssoRegion" | "authType">;
63
+ /**
64
+ * Allowlisted GitHub Copilot API origin belonging to THIS account.
65
+ *
66
+ * Copilot pins its bearer to an account-scoped regional host, and the initial route already
67
+ * pairs the two (`core.ts` resolves transport with `getOAuthCredentialApiBaseUrl`). Account
68
+ * failover must carry the pairing across the rotation; without it, account B's token is sent to
69
+ * account A's origin (#2568d).
70
+ */
71
+ apiBaseUrl?: string;
56
72
  }
57
73
 
74
+ export interface ObservedOAuthAccessSnapshot extends OAuthAccessSnapshot {
75
+ /** Retained for callers that predate `apiBaseUrl` moving onto the base snapshot. */
76
+ apiBaseUrl?: string;
77
+ }
78
+
79
+ export type OAuthActiveTokenObservation =
80
+ | { readonly kind: "available"; readonly snapshot: ObservedOAuthAccessSnapshot }
81
+ | { readonly kind: "missing" }
82
+ | { readonly kind: "malformed" }
83
+ | { readonly kind: "needs-reauth" }
84
+ | { readonly kind: "expired" }
85
+ | { readonly kind: "near-expiry" }
86
+ | { readonly kind: "unsupported" };
87
+
58
88
  const MAX_OAUTH_TOKEN_REFRESH_FLIGHTS = 32;
59
89
  const OAUTH_TOKEN_REFRESH_FLIGHT_STALE_MS = 120_000;
60
90
  interface OAuthRefreshFlightEvidence { flightId: string; dispatched: boolean }
@@ -145,6 +175,14 @@ function oauthDefaultModel(id: string): string {
145
175
  }
146
176
 
147
177
  export const OAUTH_PROVIDERS: Record<string, OAuthProviderDef> = {
178
+ "command-code": {
179
+ // Add-account/reauth must not reimport the current local CLI credential.
180
+ login: (ctrl, opts) => loginCommandCode(ctrl, { importLocal: opts?.forceLogin ? "off" : "fallback" }),
181
+ refresh: refreshCommandCodeToken,
182
+ providerConfig: oauthConfig("command-code"),
183
+ defaultModel: oauthDefaultModel("command-code"),
184
+ defaultRefreshPolicy: "disabled",
185
+ },
148
186
  xai: {
149
187
  // forceLogin skips the local grok-cli import so a SECOND account can be chosen in the browser.
150
188
  login: (ctrl, opts) => loginXai(ctrl, { importLocal: opts?.forceLogin ? "off" : "fallback" }),
@@ -167,6 +205,22 @@ export const OAUTH_PROVIDERS: Record<string, OAuthProviderDef> = {
167
205
  providerConfig: oauthConfig("kimi"),
168
206
  defaultModel: oauthDefaultModel("kimi"),
169
207
  },
208
+ nous: {
209
+ // Nous Portal device-grant login (RFC 8628) against portal.nousresearch.com.
210
+ // The access token is the per-request inference JWT (scope inference:invoke).
211
+ // Refresh tokens are single-use and rotated server-side on every refresh:
212
+ // keep background refresh lazy-only (the default) so concurrent refreshes
213
+ // cannot trip the Portal's token-reuse revocation.
214
+ login: (ctrl) => loginNous(ctrl),
215
+ refresh: (rt, signal) => refreshNousToken(rt, signal),
216
+ providerConfig: oauthConfig("nous"),
217
+ defaultModel: oauthDefaultModel("nous"),
218
+ // Single-use rotating refresh tokens must never be background-refreshed
219
+ // proactively: concurrent refreshes would trip the Portal's reuse
220
+ // revocation. Anchor the lazy-only default explicitly so it cannot be
221
+ // silently overridden to proactive.
222
+ defaultRefreshPolicy: "lazy-only",
223
+ },
170
224
  kiro: {
171
225
  login: (ctrl, opts) => loginKiro(ctrl, { forceLogin: opts?.forceLogin }),
172
226
  refresh: (rt, signal, credential) => refreshKiroToken(rt, signal, credential),
@@ -180,7 +234,7 @@ export const OAUTH_PROVIDERS: Record<string, OAuthProviderDef> = {
180
234
  defaultModel: oauthDefaultModel("google-antigravity"),
181
235
  },
182
236
  cursor: {
183
- login: (ctrl) => loginCursor(ctrl),
237
+ login: (ctrl, opts) => loginCursor(ctrl, undefined, { forceLogin: opts?.forceLogin }),
184
238
  refresh: refreshCursorToken,
185
239
  providerConfig: oauthConfig("cursor"),
186
240
  defaultModel: oauthDefaultModel("cursor"),
@@ -193,6 +247,12 @@ export const OAUTH_PROVIDERS: Record<string, OAuthProviderDef> = {
193
247
  // Unofficial Copilot bridge — keep proactive traffic lazy-only (no background guardian spam).
194
248
  defaultRefreshPolicy: "lazy-only",
195
249
  },
250
+ cline: {
251
+ login: (ctrl, opts) => loginCline(ctrl, { importLocal: opts?.forceLogin ? "off" : "fallback", forceLogin: opts?.forceLogin }),
252
+ refresh: refreshClineToken,
253
+ providerConfig: oauthConfig("cline"),
254
+ defaultModel: oauthDefaultModel("cline"),
255
+ },
196
256
  chatgpt: {
197
257
  login: loginChatGPT,
198
258
  refresh: (rt) => refreshChatGPTToken(rt),
@@ -248,35 +308,138 @@ export class UnsupportedOAuthProviderError extends Error {
248
308
  }
249
309
 
250
310
  export class OAuthLoginRequiredError extends Error {
311
+ readonly provider: string;
312
+
251
313
  constructor(provider: string) {
252
314
  super(`Not logged in to ${provider}. Run: ocx login ${provider}`);
253
315
  this.name = "OAuthLoginRequiredError";
316
+ this.provider = provider;
317
+ }
318
+ }
319
+
320
+ export class OAuthProviderPublicationError extends Error {
321
+ constructor() {
322
+ super("OAuth credential was saved, but the provider entry was not written. Resolve the account namespace collision, then retry login.");
323
+ this.name = "OAuthProviderPublicationError";
324
+ }
325
+ }
326
+
327
+ export class OAuthReauthIdentityMismatchError extends Error {
328
+ constructor() {
329
+ super("Signed-in account does not match the selected account. Sign in with the same account.");
330
+ this.name = "OAuthReauthIdentityMismatchError";
331
+ }
332
+ }
333
+
334
+ export class OAuthReauthIdentityUnverifiedError extends Error {
335
+ constructor() {
336
+ super("Could not verify signed-in account identity for reauth.");
337
+ this.name = "OAuthReauthIdentityUnverifiedError";
254
338
  }
255
339
  }
256
340
 
341
+ class OAuthLoginSupersededError extends Error {
342
+ constructor() {
343
+ super("OAuth login was superseded before credential persistence");
344
+ this.name = "OAuthLoginSupersededError";
345
+ }
346
+ }
347
+
348
+ /** Project arbitrary OAuth failures onto the small, stable public error vocabulary. */
349
+ export function publicOAuthAuthenticationErrorMessage(error: unknown): string {
350
+ if (error instanceof OAuthMutationBusyError) {
351
+ return error.message === "OAuth mutation queue wait timed out"
352
+ ? "OAuth mutation queue wait timed out"
353
+ : "OAuth mutation queue is busy";
354
+ }
355
+ if (
356
+ (error instanceof OAuthLoginRequiredError && isOAuthProvider(error.provider))
357
+ || error instanceof OAuthProviderPublicationError
358
+ // Reauth identity outcomes carry fixed, account-free remediation text. Dropping them to the
359
+ // generic message hides WHICH failure the user must fix (sign in with the selected account).
360
+ || error instanceof OAuthReauthIdentityMismatchError
361
+ || error instanceof OAuthReauthIdentityUnverifiedError
362
+ || error instanceof OAuthTokenRefreshBusyError
363
+ || error instanceof OAuthTokenRefreshStaleError
364
+ ) return error.message;
365
+ return "OAuth authentication failed. Check the OpenCodex account status and retry.";
366
+ }
367
+
257
368
  function accessSnapshot(provider: string, accountId: string, cred: OAuthCredentials): OAuthAccessSnapshot {
369
+ // Derived, not read back: a stored `authType` is trusted when present, but a credential imported
370
+ // before the field existed still routes correctly because the client pair implies SSO OIDC.
371
+ const kiroAuthType = cred.kiro?.authType
372
+ ?? (cred.kiro?.clientId && cred.kiro?.clientSecret ? "aws_sso_oidc" as const : undefined);
258
373
  const storedKiroRouting = {
259
374
  ...(cred.kiro?.profileArn ? { profileArn: cred.kiro.profileArn } : {}),
260
375
  ...(cred.kiro?.apiRegion ? { apiRegion: cred.kiro.apiRegion } : {}),
261
376
  ...(cred.kiro?.ssoRegion ? { ssoRegion: cred.kiro.ssoRegion } : {}),
262
377
  };
378
+ // `authType` is a property OF the account, not routing the environment can substitute for, so it
379
+ // is merged after the environment fallback decision rather than counting as stored routing.
380
+ // Folding it into `storedKiroRouting` would make a client-pair-only credential look non-empty
381
+ // and silently disable `environmentKiroRoutingMetadata()` for it.
382
+ const kiroAuthTypeRouting = kiroAuthType ? { authType: kiroAuthType } : {};
383
+ // Validated here, not at the call site: an unvalidated origin from a legacy or crafted
384
+ // credential must never travel with a bearer, and dropping it makes the transport fall back to
385
+ // the canonical host rather than to whatever the previous account was using.
386
+ const copilotApiBaseUrl = provider === "github-copilot"
387
+ ? validateCopilotApiBaseUrl(cred.apiBaseUrl)
388
+ : undefined;
263
389
  return {
264
390
  provider,
265
391
  accountId,
266
392
  generation: credentialGeneration(cred),
267
393
  accessToken: cred.access,
394
+ ...(cred.projectId ? { projectId: cred.projectId } : {}),
395
+ ...(copilotApiBaseUrl ? { apiBaseUrl: copilotApiBaseUrl } : {}),
268
396
  // Stored account metadata remains authoritative. Metadata-less legacy/environment credentials
269
397
  // may use explicit environment routing, but never borrow the currently signed-in local CLI account.
270
398
  ...(provider === "kiro"
271
399
  ? {
272
- kiro: Object.keys(storedKiroRouting).length > 0
273
- ? storedKiroRouting
274
- : environmentKiroRoutingMetadata() ?? {},
400
+ kiro: {
401
+ ...(Object.keys(storedKiroRouting).length > 0
402
+ ? storedKiroRouting
403
+ : environmentKiroRoutingMetadata() ?? {}),
404
+ ...kiroAuthTypeRouting,
405
+ },
275
406
  }
276
407
  : {}),
277
408
  };
278
409
  }
279
410
 
411
+ /**
412
+ * Observe the active OAuth token from an auth-store buffer supplied by its filesystem owner.
413
+ * Missing, malformed, reauth-required, and expiring credentials are typed no-token outcomes;
414
+ * this path never refreshes, locks, hardens, backs up, or persists credentials.
415
+ */
416
+ export function observeActiveOAuthAccessToken(
417
+ provider: string,
418
+ authStoreBuffer: Uint8Array | null,
419
+ now = Date.now(),
420
+ ): OAuthActiveTokenObservation {
421
+ const authStore = normalizeAuthStoreBuffer(authStoreBuffer);
422
+ if (authStore.kind === "absent") return { kind: "missing" };
423
+ if (authStore.kind === "malformed") return { kind: "malformed" };
424
+ if (!isOAuthProvider(provider)) return { kind: "unsupported" };
425
+
426
+ const accountSet = authStore.store[provider];
427
+ const account = accountSet?.accounts.find(candidate => candidate.id === accountSet.activeAccountId);
428
+ if (!account) return { kind: "missing" };
429
+ if (account.needsReauth) return { kind: "needs-reauth" };
430
+ if (account.credential.expires <= now) return { kind: "expired" };
431
+ if (account.credential.expires <= now + REFRESH_SKEW_MS) return { kind: "near-expiry" };
432
+
433
+ const apiBaseUrl = validateCopilotApiBaseUrl(account.credential.apiBaseUrl);
434
+ return {
435
+ kind: "available",
436
+ snapshot: {
437
+ ...accessSnapshot(provider, account.id, account.credential),
438
+ ...(apiBaseUrl ? { apiBaseUrl } : {}),
439
+ },
440
+ };
441
+ }
442
+
280
443
  async function resolveAccessSnapshotForAccount(
281
444
  provider: string,
282
445
  accountId: string,
@@ -339,7 +502,7 @@ export async function getValidAccessTokenSnapshot(provider: string): Promise<OAu
339
502
  }
340
503
 
341
504
  /** Providers whose upstream-401 replay path may force a snapshot refresh. */
342
- const FORCE_REFRESH_PROVIDERS = new Set(["xai", "github-copilot", "kiro"]);
505
+ const FORCE_REFRESH_PROVIDERS = new Set(["xai", "github-copilot", "kiro", "cline"]);
343
506
 
344
507
  export async function forceRefreshOAuthAccessSnapshot(
345
508
  rejected: OAuthAccessSnapshot,
@@ -362,6 +525,22 @@ export async function getValidAccessTokenForAccount(provider: string, accountId:
362
525
  return (await resolveAccessSnapshotForAccount(provider, accountId)).accessToken;
363
526
  }
364
527
 
528
+ /**
529
+ * Account-scoped resolver returning the FULL snapshot, not just the bearer.
530
+ *
531
+ * A rotator that swaps only the token silently mixes credential generations: Antigravity pairs
532
+ * an account-matched `projectId` with its token (see the pairing comment in
533
+ * server/responses/core.ts), Kiro carries routing metadata, and Copilot's observed snapshot
534
+ * carries an account-specific API origin. Reading those back from "whichever account is active"
535
+ * after a rotation is exactly the mixing this returns in one piece to prevent (#2568).
536
+ */
537
+ export async function getValidAccessSnapshotForAccount(
538
+ provider: string,
539
+ accountId: string,
540
+ ): Promise<OAuthAccessSnapshot> {
541
+ return resolveAccessSnapshotForAccount(provider, accountId);
542
+ }
543
+
365
544
  /** Terminal refresh failures (revoked/rotated-away grants) — retrying cannot succeed. */
366
545
  function isTerminalRefreshError(err: unknown): boolean {
367
546
  const msg = (err instanceof Error ? err.message : String(err)).toLowerCase();
@@ -376,6 +555,11 @@ function terminal(error:unknown):boolean{
376
555
  if(error instanceof XaiTokenRequestError)return ["invalid_grant","refresh_token_reused","revoked_token"].includes(error.oauthError??"");
377
556
  if(error instanceof AnthropicTokenError)return (error.httpStatus===400||error.httpStatus===401)&&["invalid_grant","refresh_token_reused","revoked","revoked_token","refresh_token_revoked"].includes(error.oauthError??"");
378
557
  if(error instanceof KiroTokenRefreshError)return (error.httpStatus===400||error.httpStatus===401)&&error.oauthError!==undefined;
558
+ if(error instanceof NousTokenError)return error.terminal===true||["invalid_grant","refresh_token_reused","revoked","revoked_token","expired_token"].includes(error.oauthError??"");
559
+ // Local durable-write/read/cleanup failures are operational, not credential
560
+ // death: the provider credential was never rejected or consumed. Never mark
561
+ // the account needsReauth for broken local persistence infrastructure.
562
+ if (error instanceof RefreshIntentIOError) return false;
379
563
  return isTerminalRefreshError(error);
380
564
  }
381
565
  function authoritative(stored:OAuthCredentials,active:boolean,now:()=>number):OAuthCredentials{if(stored.source!=="local-cli")return stored;const disk=detectGrokCliToken();if(!disk)return stored;const allowed=isSameGrokIdentity(stored,disk)||(active&&!hasComparableGrokIdentity(stored,disk));return allowed&&shouldAdoptGrokGeneration(stored,disk,now(),REFRESH_SKEW_MS)?disk:stored;}
@@ -399,6 +583,47 @@ function newerClaudeCredential(stored: OAuthCredentials, now: number): OAuthCred
399
583
  return credentialGeneration(disk) !== credentialGeneration(stored) ? disk : undefined;
400
584
  }
401
585
 
586
+ /**
587
+ * Preserve an already-rotated Nous refresh token (RT-B) after a terminal refresh
588
+ * error (e.g. the returned access JWT lacked `inference:invoke`). The unusable
589
+ * access token is NOT persisted as valid: the recovery credential carries an
590
+ * empty access placeholder with a past expiry so it can never be routed, and the
591
+ * account is marked needsReauth by the caller. Generation-safe: a concurrent
592
+ * newer write wins and is never overwritten.
593
+ */
594
+ async function preserveNousRotatedRefresh(
595
+ provider: string,
596
+ accountId: string,
597
+ rotatedRefresh: string,
598
+ expectedGeneration: string,
599
+ previous: OAuthCredentials,
600
+ ): Promise<{ kind: "persisted"; generation: string } | { kind: "superseded" } | { kind: "failed" }> {
601
+ try {
602
+ const recovery: OAuthCredentials = {
603
+ refresh: rotatedRefresh,
604
+ // Never persist the unusable access token: an empty placeholder with a
605
+ // past expiry can never be observed as a valid credential.
606
+ access: "",
607
+ expires: 0,
608
+ ...(previous.accountId ? { accountId: previous.accountId } : {}),
609
+ ...(previous.email ? { email: previous.email } : {}),
610
+ ...(previous.source ? { source: previous.source } : {}),
611
+ };
612
+ const outcome = await mergeAccountCredential(provider, accountId, recovery, { expectedGeneration });
613
+ if (outcome.superseded) return { kind: "superseded" };
614
+ // Return the exact generation this write produced, so the caller never
615
+ // re-reads the store (a concurrent writer could otherwise supply a different
616
+ // credential generation and be marked needsReauth by mistake).
617
+ return { kind: "persisted", generation: credentialGeneration(recovery) };
618
+ } catch (error) {
619
+ // A store-mutation busy outcome is transient and retryable; surface it
620
+ // unchanged so the caller can retry rather than treating it as a permanent
621
+ // RT-B persistence failure.
622
+ if (error instanceof OAuthMutationBusyError) throw error;
623
+ return { kind: "failed" };
624
+ }
625
+ }
626
+
402
627
  export async function refreshAnthropicAccountWithLock(
403
628
  provider: string,
404
629
  accountId: string,
@@ -512,10 +737,69 @@ export async function refreshGenericAccountWithLock(
512
737
  throw new OAuthLoginRequiredError(provider);
513
738
  }
514
739
  logOAuthEvent("OAuth credentials rotated and persisted", { provider, accountId });
740
+ // Best-effort bookkeeping cleanup: the rotated credential is already
741
+ // durably persisted. A failure to unlink the old-token intent file
742
+ // (EACCES/EPERM/EBUSY/EROFS) must not turn a committed rotation into a
743
+ // failed refresh. The stale intent keys the OLD token, which is no longer
744
+ // stored, so leaving it behind blocks nothing and is safe. It must also
745
+ // never route through the generic refresh error path (no needsReauth).
746
+ if (provider === "nous") {
747
+ try {
748
+ clearNousRefreshIntent(stored.refresh);
749
+ } catch (cleanupErr) {
750
+ logOAuthEvent("OAuth refresh intent cleanup failed (non-fatal)", {
751
+ provider,
752
+ accountId,
753
+ cause: cleanupErr instanceof Error ? cleanupErr.message : String(cleanupErr),
754
+ });
755
+ }
756
+ }
515
757
  return fresh.access;
516
758
  } catch (error) {
517
759
  if (error instanceof OAuthMutationBusyError) throw error;
518
760
  if (!terminal(error)) throw error;
761
+ // Nous-specific failure-atomicity: a terminal refresh error that carries
762
+ // an already-issued rotated refresh token (e.g. the access JWT lacked the
763
+ // required `inference:invoke` scope) means the server consumed RT-A and
764
+ // issued RT-B. RT-B must be preserved generation-safely BEFORE forcing
765
+ // reauthentication; discarding it would lose the only usable refresh
766
+ // material and force a full re-auth for no reason.
767
+ if (provider === "nous" && error instanceof NousTokenError) {
768
+ const rotated = error.getRotatedRefresh();
769
+ if (rotated !== undefined && rotated !== stored.refresh) {
770
+ const outcome = await preserveNousRotatedRefresh(
771
+ provider,
772
+ accountId,
773
+ rotated,
774
+ generation,
775
+ stored,
776
+ );
777
+ if (outcome.kind === "persisted") {
778
+ // RT-A's intent is cleared only after RT-B is durably persisted;
779
+ // cleanup itself stays best-effort (a stale RT-A intent keys a token
780
+ // that is no longer stored).
781
+ try {
782
+ clearNousRefreshIntent(stored.refresh);
783
+ } catch (cleanupErr) {
784
+ logOAuthEvent("OAuth refresh intent cleanup failed (non-fatal)", {
785
+ provider,
786
+ accountId,
787
+ cause: cleanupErr instanceof Error ? cleanupErr.message : String(cleanupErr),
788
+ });
789
+ }
790
+ // Mark exactly the generation this write produced — never a
791
+ // credential written by a concurrent login between the merge and
792
+ // this step.
793
+ await markAccountNeedsReauthIfGeneration(provider, accountId, outcome.generation, writerGeneration);
794
+ } else {
795
+ // RT-B persistence failed or a newer generation superseded it:
796
+ // never clear RT-A's intent (RT-A was consumed), and mark the old
797
+ // generation needsReauth (a no-op if a newer generation won).
798
+ await markAccountNeedsReauthIfGeneration(provider, accountId, generation, writerGeneration);
799
+ }
800
+ throw new OAuthLoginRequiredError(provider);
801
+ }
802
+ }
519
803
  await markAccountNeedsReauthIfGeneration(provider, accountId, generation, writerGeneration);
520
804
  throw new OAuthLoginRequiredError(provider);
521
805
  }
@@ -572,31 +856,63 @@ function modelDiscoveryTransportSeed(providerName: string, prov: OcxProviderConf
572
856
  }
573
857
 
574
858
  /**
575
- * Provider-correct `GET /models` request (URL + headers), so both model-listing paths fetch the
859
+ * Provider-correct model-discovery request (URL + headers), so both model-listing paths fetch the
576
860
  * LIVE catalog correctly per adapter. Anthropic is the special case: its endpoint is `/v1/models`
577
861
  * (not `/models`), it needs `anthropic-version`, and it authenticates with `x-api-key` by default
578
862
  * (or `Authorization: Bearer` when `apiKeyTransport = "bearer"`), plus the OAuth beta for oauth
579
863
  * mode — not a bare Bearer. Google (ai-studio mode)
580
864
  * is the other special case: `x-goog-api-key` + `/v1beta/models`, returning `{ models: [...] }`.
581
865
  * The catalog authority gate intentionally degrades that non-OpenAI shape to stale/static data.
582
- * Everyone else uses the OpenAI-style `/models` + Bearer with a `{ data: [{ id, owned_by? }] }`
583
- * response.
866
+ * Antigravity uses its CCA `:fetchAvailableModels` RPC; everyone else uses the OpenAI-style
867
+ * `/models` + Bearer with a `{ data: [{ id, owned_by? }] }` response.
584
868
  */
585
- export function buildModelsRequest(prov: OcxProviderConfig, apiKey: string | undefined, providerName = ""): { url: string; headers: Record<string, string> } {
869
+ export interface ModelsRequestObservedAuth {
870
+ readonly oauthApiBaseUrl?: string;
871
+ }
872
+
873
+ export function buildModelsRequest(
874
+ prov: OcxProviderConfig,
875
+ apiKey: string | undefined,
876
+ providerName = "",
877
+ observedAuth?: ModelsRequestObservedAuth,
878
+ ): { method?: "POST"; url: string; headers: Record<string, string> } {
586
879
  const transportSeed = modelDiscoveryTransportSeed(providerName, prov);
880
+ const copilotApiBaseUrl = observedAuth === undefined
881
+ ? (providerName === "github-copilot" ? getOAuthCredentialApiBaseUrl(providerName) : undefined)
882
+ : observedAuth.oauthApiBaseUrl;
587
883
  const effectiveProvider = resolveProviderTransport(
588
884
  providerName,
589
885
  transportSeed,
590
886
  undefined,
591
- providerName === "github-copilot" ? getOAuthCredentialApiBaseUrl(providerName) : undefined,
887
+ copilotApiBaseUrl,
592
888
  );
593
- const headers: Record<string, string> = { ...(effectiveProvider.headers ?? {}) };
889
+ // Model discovery is an upstream request like any other, so it carries the same registry
890
+ // static headers the inference path does. Without this a provider is identified correctly
891
+ // when it answers a completion but anonymously when it lists its own models, which is the
892
+ // kind of split fingerprint an upstream rate limiter reads as two different clients.
893
+ const registryStaticHeaders = providerMatchesRegistryTransport(providerName, effectiveProvider)
894
+ ? getProviderRegistryEntry(providerName)?.staticHeaders
895
+ : undefined;
896
+ const headers: Record<string, string> = {
897
+ ...(mergeRegistryStaticHeaders(registryStaticHeaders, effectiveProvider.headers) ?? {}),
898
+ };
594
899
  const discoveryUrl = (defaultUrl: string): string => resolveProviderModelDiscoveryUrl(
595
900
  providerName,
596
901
  prov,
597
902
  effectiveProvider.baseUrl,
598
903
  defaultUrl,
599
904
  );
905
+ if (effectiveGoogleMode(providerName, effectiveProvider) === "cloud-code-assist") {
906
+ headers.Accept = "application/json";
907
+ headers["Content-Type"] = "application/json";
908
+ headers["User-Agent"] = ANTIGRAVITY_REQUEST_UA;
909
+ if (apiKey) headers.Authorization = `Bearer ${apiKey}`;
910
+ return {
911
+ method: "POST",
912
+ url: discoveryUrl(`${effectiveProvider.baseUrl.replace(/\/+$/, "")}/v1internal:fetchAvailableModels`),
913
+ headers,
914
+ };
915
+ }
600
916
  if (effectiveGoogleMode(providerName, effectiveProvider) === "ai-studio") {
601
917
  // Generative Language API: API key goes in x-goog-api-key (never Authorization: Bearer),
602
918
  // models live under /v1beta (v1 misses preview models), and pageSize maxes at 1000 —
@@ -630,9 +946,7 @@ export function buildModelsRequest(prov: OcxProviderConfig, apiKey: string | und
630
946
  *
631
947
  * Only touches providers that are registry-managed AND still `authMode: "oauth"`. Preset fields
632
948
  * are refreshed, while the registry's `liveModels` default is normally filled only when no value
633
- * is stored. Antigravity has one versioned exception below because its old GUI-generated `true`
634
- * cannot be distinguished from a hand-written pre-migration `true`. Persists + returns true when
635
- * anything changed.
949
+ * is stored. Persists + returns true when anything changed.
636
950
  */
637
951
  function cloneProviderField(value: unknown): unknown {
638
952
  if (Array.isArray(value)) return [...value];
@@ -659,31 +973,62 @@ const OAUTH_RECONCILE_FIELDS: (keyof OcxProviderConfig)[] = [
659
973
  "autoToolChoiceOnlyModels",
660
974
  "preserveReasoningContentModels",
661
975
  ];
976
+ // `requiresReasoningPlaceholderModels` is deliberately NOT reconciled here: no
977
+ // OAuth preset seeds it, so the delete-when-preset-undefined branch would wipe
978
+ // an explicit user opt-out (`[]`) on every startup. Registry seeds still reach
979
+ // existing rows through enrichProviderFromRegistry, which is fill-only and
980
+ // preserves explicit saved values.
662
981
 
663
982
  const GOOGLE_ANTIGRAVITY_PROVIDER = "google-antigravity";
664
- const GOOGLE_ANTIGRAVITY_STATIC_CATALOG_VERSION = 1 as const;
983
+ const GOOGLE_ANTIGRAVITY_LIVE_DISCOVERY_VERSION = 2 as const;
984
+
985
+ /** Only migrate the three-model experimental seed; an operator's later `liveModels: false` wins. */
986
+ function isLegacyCommandCodeStaticCatalog(provider: OcxProviderConfig): boolean {
987
+ return provider.liveModels === false
988
+ && provider.defaultModel === "deepseek-v4-flash"
989
+ && JSON.stringify(provider.models) === JSON.stringify(["deepseek-v4-flash", "kimi-k3", "glm-5.2"]);
990
+ }
991
+
992
+ function isLegacyAntigravityStaticCatalog(provider: OcxProviderConfig): boolean {
993
+ // A fingerprint of the shape version 1 actually shipped, NOT of the current registry.
994
+ // These literals must stay frozen as the model list moves on: matching them is how we
995
+ // know the row is the untouched v1 seed rather than a user's own selection.
996
+ return provider.liveModels === false
997
+ && provider.adapter === "google"
998
+ && provider.baseUrl === "https://daily-cloudcode-pa.googleapis.com"
999
+ && provider.authMode === "oauth"
1000
+ && provider.googleMode === "cloud-code-assist"
1001
+ && provider.defaultModel === "gemini-3.6-flash"
1002
+ && JSON.stringify(provider.models) === JSON.stringify([
1003
+ "gemini-3.6-flash",
1004
+ "gemini-3.1-pro",
1005
+ "gemini-3.1-flash-image",
1006
+ "claude-sonnet-4-6",
1007
+ "claude-opus-4-6-thinking",
1008
+ "gpt-oss-120b-medium",
1009
+ ]);
1010
+ }
1011
+
1012
+ /** Promote only the versioned canonical static seed; unmarked `liveModels: false` remains user intent. */
1013
+ function migrateLegacyAntigravityStaticCatalog(config: OcxConfig): boolean {
1014
+ if (config.googleAntigravityStaticCatalogVersion !== 1) return false;
1015
+ const provider = config.providers[GOOGLE_ANTIGRAVITY_PROVIDER];
1016
+ if (provider && isLegacyAntigravityStaticCatalog(provider)) provider.liveModels = true;
1017
+ config.googleAntigravityStaticCatalogVersion = GOOGLE_ANTIGRAVITY_LIVE_DISCOVERY_VERSION;
1018
+ return true;
1019
+ }
665
1020
 
666
1021
  export function reconcileOAuthProviders(config: OcxConfig): boolean {
667
- let changed = false;
668
- const migrateAntigravityStaticCatalog =
669
- config.googleAntigravityStaticCatalogVersion !== GOOGLE_ANTIGRAVITY_STATIC_CATALOG_VERSION;
1022
+ let changed = migrateLegacyAntigravityStaticCatalog(config);
670
1023
  for (const [name, prov] of Object.entries(config.providers)) {
671
1024
  const def = OAUTH_PROVIDERS[name];
672
- // Normalize the canonical row before the OAuth-only reconciliation guard. The old GUI and a
673
- // manual edit both persist the same bare `true`, with no source metadata, so every ambiguous
674
- // pre-marker value is reset once. A deliberate live-discovery choice can be re-enabled after
675
- // the marker and is then preserved. Do this before the guard so omitted/non-OAuth authMode
676
- // rows do not get stamped without actually receiving the new static default.
677
- if (name === GOOGLE_ANTIGRAVITY_PROVIDER && migrateAntigravityStaticCatalog && prov.liveModels !== false) {
678
- prov.liveModels = false;
1025
+ if (name === "command-code" && isLegacyCommandCodeStaticCatalog(prov)) {
1026
+ // The former experimental preset was the exact three-model seed above. It was not a user
1027
+ // choice to disable discovery, so promote only that shape to the account live catalog.
1028
+ prov.liveModels = true;
679
1029
  changed = true;
680
1030
  }
681
- // During the one-time Antigravity static-catalog migration, also refresh preset catalog
682
- // fields when authMode is omitted or non-oauth. Otherwise liveModels flips to static while
683
- // a stale models[] remains the published catalog forever.
684
- const migrateAntigravityCatalogFields =
685
- name === GOOGLE_ANTIGRAVITY_PROVIDER && migrateAntigravityStaticCatalog;
686
- if (!def || (prov.authMode !== "oauth" && !migrateAntigravityCatalogFields)) continue;
1031
+ if (!def || prov.authMode !== "oauth") continue;
687
1032
  const preset = def.providerConfig;
688
1033
  for (const field of OAUTH_RECONCILE_FIELDS) {
689
1034
  if (JSON.stringify(prov[field]) === JSON.stringify(preset[field])) continue;
@@ -694,23 +1039,19 @@ export function reconcileOAuthProviders(config: OcxConfig): boolean {
694
1039
  }
695
1040
  changed = true;
696
1041
  }
697
- // Before this marker existed, the GUI materialized an omitted `liveModels` as `true` on any
698
- // settings save. Since persisted values have no provenance, the pre-guard normalization above
699
- // intentionally resets all pre-marker `true` values once. Later choices are version-bounded.
700
1042
  if (prov.liveModels === undefined && preset.liveModels !== undefined) {
701
1043
  prov.liveModels = preset.liveModels;
702
1044
  changed = true;
703
1045
  }
704
1046
  // Heal a defaultModel that no longer exists in the refreshed list (e.g. a deprecated snapshot).
705
- if (prov.defaultModel && preset.defaultModel && !(prov.models ?? []).includes(prov.defaultModel)) {
1047
+ // Skip providers without a static preset `models` list: for live-discovery providers
1048
+ // (e.g. command-code OAuth) the account-scoped catalog is not enumerable here, so any
1049
+ // persisted defaultModel is a user selection and must not be overwritten by the seed.
1050
+ if (prov.defaultModel && preset.defaultModel && preset.models && preset.models.length > 0 && !(prov.models ?? []).includes(prov.defaultModel)) {
706
1051
  prov.defaultModel = preset.defaultModel;
707
1052
  changed = true;
708
1053
  }
709
1054
  }
710
- if (migrateAntigravityStaticCatalog) {
711
- config.googleAntigravityStaticCatalogVersion = GOOGLE_ANTIGRAVITY_STATIC_CATALOG_VERSION;
712
- changed = true;
713
- }
714
1055
  if (changed) saveConfig(config);
715
1056
  return changed;
716
1057
  }
@@ -767,19 +1108,36 @@ export function upsertOAuthProvider(config: OcxConfig, provider: string): void {
767
1108
  if (provider === "chatgpt") return;
768
1109
  const def = OAUTH_PROVIDERS[provider];
769
1110
  if (!def) return;
1111
+ if (provider === GOOGLE_ANTIGRAVITY_PROVIDER) migrateLegacyAntigravityStaticCatalog(config);
770
1112
  const namespaceCollision = codexAccountNamespaceProviderCollisionError(config.codexAccountNamespaces, provider);
771
1113
  if (namespaceCollision) throw new Error(namespaceCollision);
772
1114
  const existing = config.providers[provider];
773
1115
  const next: OcxProviderConfig = { ...def.providerConfig };
774
- // `liveModels` is a user-facing provider toggle. A registry default seeds new rows, but an
775
- // explicit post-migration choice must survive re-login and the latest-config upsert. Old GUI
776
- // saves and manual edits left identical pre-marker `true` values, so that ambiguous state is
777
- // reset once; users who deliberately forced discovery can re-enable it after migration.
778
- const preserveExistingLiveModels = provider !== GOOGLE_ANTIGRAVITY_PROVIDER
779
- || config.googleAntigravityStaticCatalogVersion === GOOGLE_ANTIGRAVITY_STATIC_CATALOG_VERSION;
780
- if (preserveExistingLiveModels && typeof existing?.liveModels === "boolean") {
1116
+ // `liveModels` is a user-facing provider toggle. Preserve either explicit setting across login;
1117
+ // Antigravity's CCA discovery now uses its real RPC, so legacy `true` remains a valid choice.
1118
+ if (typeof existing?.liveModels === "boolean" && !isLegacyCommandCodeStaticCatalog(existing)) {
781
1119
  next.liveModels = existing.liveModels;
782
1120
  }
1121
+ // The Command Code protocol-version pin is an operator compatibility control. A re-login,
1122
+ // add-account, or reauth rebuilds the row from the preset, which has no version; carry the
1123
+ // existing pin so authentication changes do not silently revert the documented control.
1124
+ if (existing?.commandCodeVersion !== undefined) {
1125
+ next.commandCodeVersion = existing.commandCodeVersion;
1126
+ }
1127
+ // User-configured price overlays are operator data, not preset state; a
1128
+ // re-login, add-account, or reauth must not silently drop them from the
1129
+ // Logs/Usage estimates.
1130
+ if (existing?.modelCosts !== undefined) {
1131
+ next.modelCosts = existing.modelCosts;
1132
+ }
1133
+ // The per-provider account-failover opt-out is operator intent about SPENDING, and the login
1134
+ // path is exactly where losing it does damage: adding a second account both rebuilds this row
1135
+ // from the preset and creates the 2-account quorum that turns presence-driven rotation on
1136
+ // (#2568d). Dropping the opt-out here would enable the thing the operator switched off, at the
1137
+ // moment they were doing something unrelated.
1138
+ if (existing?.oauthAccountFailover !== undefined) {
1139
+ next.oauthAccountFailover = existing.oauthAccountFailover;
1140
+ }
783
1141
  if (existing && getProviderRegistryEntry(provider)?.allowKeyAuthOverride === true) {
784
1142
  // Shared sanitizeApiKeyValue trim / no-CRLF checks from api-key pool writes.
785
1143
  let storedApiKey = sanitizeApiKeyValue(existing.apiKey);
@@ -803,9 +1161,6 @@ export function upsertOAuthProvider(config: OcxConfig, provider: string): void {
803
1161
  }
804
1162
  }
805
1163
  config.providers[provider] = next;
806
- if (provider === GOOGLE_ANTIGRAVITY_PROVIDER) {
807
- config.googleAntigravityStaticCatalogVersion = GOOGLE_ANTIGRAVITY_STATIC_CATALOG_VERSION;
808
- }
809
1164
  }
810
1165
 
811
1166
  interface RunLoginDeps {
@@ -816,6 +1171,7 @@ interface RunLoginDeps {
816
1171
  settleKiroLoginTransaction?: typeof settleKiroLoginTransaction;
817
1172
  removeAccount?: typeof removeAccount;
818
1173
  setActiveAccount?: typeof setActiveAccount;
1174
+ assertCurrentOwner?: () => void;
819
1175
  }
820
1176
 
821
1177
  /** Roll back only accounts created by this forced login, preserving concurrent refreshes of others. */
@@ -865,6 +1221,7 @@ export async function runLogin(
865
1221
  const cred: OAuthCredentials = rawCred.source ? rawCred : { ...rawCred, source: "oauth" };
866
1222
  const settleKiroTransaction = deps.settleKiroLoginTransaction ?? settleKiroLoginTransaction;
867
1223
  try {
1224
+ deps.assertCurrentOwner?.();
868
1225
  // Validate the provider row before credential persistence. A namespace claimed during the
869
1226
  // credential write is handled again below before the latest row is re-upserted.
870
1227
  if (provider !== "chatgpt") {
@@ -875,7 +1232,7 @@ export async function runLogin(
875
1232
  const existing = getAccountCredential(provider, opts.reauthAccountId);
876
1233
  if (!existing) throw new Error(`Unknown account for reauth: ${opts.reauthAccountId}`);
877
1234
  if (!existing.accountId && !existing.email) {
878
- throw new Error("Could not verify signed-in account identity for reauth.");
1235
+ throw new OAuthReauthIdentityUnverifiedError();
879
1236
  }
880
1237
  const identityMatches = existing.accountId && cred.accountId
881
1238
  ? existing.accountId === cred.accountId
@@ -883,12 +1240,15 @@ export async function runLogin(
883
1240
  ? existing.email.toLowerCase() === cred.email.toLowerCase()
884
1241
  : false;
885
1242
  if (!identityMatches) {
886
- throw new Error("Signed-in account does not match the selected account. Sign in with the same account.");
1243
+ throw new OAuthReauthIdentityMismatchError();
887
1244
  }
888
- await (deps.saveAccountCredential ?? saveAccountCredential)(provider, opts.reauthAccountId, cred);
1245
+ await (deps.saveAccountCredential ?? saveAccountCredential)(provider, opts.reauthAccountId, cred, {
1246
+ assertBeforePersist: deps.assertCurrentOwner,
1247
+ });
889
1248
  } else {
890
1249
  await (deps.saveCredential ?? saveCredential)(provider, cred, {
891
- preserveIdentityless: provider === "kiro" && opts?.forceLogin === true,
1250
+ preserveIdentityless: opts?.forceLogin === true,
1251
+ assertBeforePersist: deps.assertCurrentOwner,
892
1252
  });
893
1253
  }
894
1254
  if (provider !== "chatgpt") {
@@ -900,10 +1260,7 @@ export async function runLogin(
900
1260
  provider,
901
1261
  );
902
1262
  if (lateCollision) {
903
- throw new Error(
904
- `${lateCollision}. The credential for "${provider}" was saved, but the provider entry was not written. `
905
- + "Rename the account selector, then re-run the login.",
906
- );
1263
+ throw new OAuthProviderPublicationError();
907
1264
  }
908
1265
  upsertOAuthProvider(latestConfig, provider);
909
1266
  saveLatestConfig(latestConfig);
@@ -933,11 +1290,15 @@ export async function runLogin(
933
1290
  settleKiroTransaction(rawCred, true);
934
1291
  if (provider !== "chatgpt") {
935
1292
  try {
1293
+ const { clearModelCache } = await import("../codex/model-cache");
1294
+ const { clearGatherRoutedModelsInflight } = await import("../codex/catalog");
1295
+ clearModelCache(provider);
1296
+ clearGatherRoutedModelsInflight();
936
1297
  const { clearAccountQuotaCache, clearProviderQuotaCache } = await import("../providers/quota");
937
1298
  clearProviderQuotaCache();
938
1299
  clearAccountQuotaCache(provider);
939
1300
  } catch {
940
- // Quota module may be unavailable in tightly scoped unit tests.
1301
+ // Optional state modules may be unavailable in tightly scoped unit tests.
941
1302
  }
942
1303
  }
943
1304
  return cred;
@@ -954,6 +1315,7 @@ export async function runLogin(
954
1315
  */
955
1316
  const loginState = new Map<string, { error?: string; done: boolean }>();
956
1317
  const loginAbort = new Map<string, AbortController>();
1318
+ const kiroLoginSettling = new Set<string>();
957
1319
 
958
1320
  /** Pending paste for a login in progress: either a waiter or a stashed early submission. */
959
1321
  interface ManualCodeSlot {
@@ -1036,7 +1398,11 @@ export function submitManualLoginCode(provider: string, input: string): { ok: tr
1036
1398
  // protected. Early posts (flow not yet waiting, no expectedState) are stashed and
1037
1399
  // re-validated by the callback loop.
1038
1400
  const parsed = parseCallbackInput(trimmed);
1039
- if (!parsed.code) return { ok: false, error: "no authorization code found in input" };
1401
+ // Command Code's manual fallback accepts a pasted JSON callback payload
1402
+ // (`{ apiKey, state, ... }`) which has no `code` param. Let it through the
1403
+ // shared gate so the provider-specific parser can validate it.
1404
+ const isCommandCodeJson = provider === "command-code" && trimmed.startsWith("{") && !parsed.code;
1405
+ if (!parsed.code && !isCommandCodeJson) return { ok: false, error: "no authorization code found in input" };
1040
1406
  if (parsed.kind !== "raw" && slot.expectedState !== undefined) {
1041
1407
  if (parsed.state === undefined) return { ok: false, error: "redirect URL is missing the state parameter" };
1042
1408
  if (parsed.state !== slot.expectedState) return { ok: false, error: "state mismatch — paste the redirect URL from THIS login attempt" };
@@ -1058,19 +1424,27 @@ export function getLoginStatus(provider: string): { loggedIn: boolean; email?: s
1058
1424
  const cred = getCredential(provider);
1059
1425
  const st = loginState.get(provider);
1060
1426
  const set = getAccountSet(provider);
1061
- // Full email on purpose: local dashboard/CLI account management needs to distinguish
1062
- // multiauth slots (masked local-parts collide). Tokens stay out of this surface.
1427
+ // Masked emails on purpose: status DTOs are shared with GUI/CLI surfaces that
1428
+ // must not leak full local-parts. Tokens stay out of this surface entirely.
1063
1429
  const accounts: OAuthAccountSummary[] | undefined = set?.accounts.map(a => ({
1064
1430
  id: a.id,
1065
1431
  ...(a.alias ? { alias: a.alias } : {}),
1066
- email: a.credential.email ?? undefined,
1432
+ email: maskEmail(a.credential.email) ?? undefined,
1067
1433
  active: a.id === set.activeAccountId,
1068
1434
  ...(a.needsReauth ? { needsReauth: true } : {}),
1069
1435
  expiresAt: a.credential.expires,
1070
1436
  }));
1437
+
1438
+ // A stored credential counts as "logged in" when it exists and is not marked for
1439
+ // re-authentication. An expired access token with a valid refresh token is still
1440
+ // logged in: request resolution refreshes expired/near-expiry credentials lazily.
1441
+ // Invalid/unknown local-import expiries are handled at parse/adoption time
1442
+ // (local-token-detect.ts), never by over-reporting login state here.
1443
+ const activeNeedsReauth = set?.accounts
1444
+ .find(a => a.id === set.activeAccountId)?.needsReauth === true;
1071
1445
  return {
1072
- loggedIn: !!cred,
1073
- email: cred?.email ?? undefined,
1446
+ loggedIn: !!cred && !activeNeedsReauth,
1447
+ email: maskEmail(cred?.email) ?? undefined,
1074
1448
  source: cred?.source,
1075
1449
  error: st?.error,
1076
1450
  done: st?.done ?? false,
@@ -1112,13 +1486,14 @@ export async function startLoginFlow(
1112
1486
  const def = OAUTH_PROVIDERS[provider];
1113
1487
  if (!def) throw new UnsupportedOAuthProviderError(provider);
1114
1488
  const existing = loginState.get(provider);
1115
- if (existing && !existing.done) {
1489
+ if ((existing && !existing.done) || (provider === "kiro" && kiroLoginSettling.has(provider))) {
1116
1490
  throw new Error(`A login for ${provider} is already in progress`);
1117
1491
  }
1118
1492
  clearManualCodeSlot(provider);
1119
1493
  loginState.set(provider, { done: false });
1120
1494
  const abort = new AbortController();
1121
1495
  loginAbort.set(provider, abort);
1496
+ if (provider === "kiro") kiroLoginSettling.add(provider);
1122
1497
  return new Promise((resolve, reject) => {
1123
1498
  let urlResolved = false;
1124
1499
  const ctrl: OAuthController = {
@@ -1131,7 +1506,16 @@ export async function startLoginFlow(
1131
1506
  onManualCodeInput: (expectedState?: string) => waitForManualLoginCode(provider, abort.signal, expectedState),
1132
1507
  signal: abort.signal,
1133
1508
  };
1509
+ const abandonIfNotOwner = (error?: unknown): boolean => {
1510
+ if (loginAbort.get(provider) === abort) return false;
1511
+ if (!urlResolved) reject(error ?? new Error("OAuth login was superseded"));
1512
+ return true;
1513
+ };
1134
1514
  const settle = async (error?: unknown): Promise<void> => {
1515
+ // Cancellation deletes this controller and records its own terminal result. A late provider
1516
+ // rejection (or an older flow settling after a replacement starts) must not overwrite that
1517
+ // state or delete the replacement flow's controller/manual-code slot.
1518
+ if (abandonIfNotOwner(error)) return;
1135
1519
  let finalError = error;
1136
1520
  try {
1137
1521
  await lifecycle?.onSettled?.();
@@ -1140,6 +1524,7 @@ export async function startLoginFlow(
1140
1524
  // runtime config. For an already-failed login, keep the original recovery error.
1141
1525
  if (finalError === undefined) finalError = settleError;
1142
1526
  }
1527
+ if (abandonIfNotOwner(finalError)) return;
1143
1528
  if (finalError === undefined) {
1144
1529
  loginAbort.delete(provider);
1145
1530
  clearManualCodeSlot(provider);
@@ -1153,22 +1538,28 @@ export async function startLoginFlow(
1153
1538
  const e = finalError;
1154
1539
  loginAbort.delete(provider);
1155
1540
  clearManualCodeSlot(provider);
1156
- const msg = e instanceof Error ? e.message : String(e);
1541
+ const msg = publicOAuthAuthenticationErrorMessage(e);
1157
1542
  loginState.set(provider, { done: true, error: msg });
1158
1543
  if (!urlResolved) reject(e);
1159
1544
  };
1160
1545
  // Background: runLogin persists the credential + provider entry to disk. The lifecycle hook
1161
1546
  // lets a long-lived server config adopt that settled state before clients observe done=true.
1162
- void runLogin(provider, ctrl, opts).then(
1547
+ const assertCurrentOwner = (): void => {
1548
+ if (loginAbort.get(provider) !== abort) throw new OAuthLoginSupersededError();
1549
+ };
1550
+ void runLogin(provider, ctrl, opts, { assertCurrentOwner }).then(
1163
1551
  () => settle(),
1164
1552
  (e: unknown) => settle(e),
1165
1553
  ).catch((e: unknown) => {
1166
1554
  // settle catches lifecycle failures, so this is only a defensive promise-boundary guard.
1555
+ if (abandonIfNotOwner(e)) return;
1167
1556
  loginAbort.delete(provider);
1168
1557
  clearManualCodeSlot(provider);
1169
- const msg = e instanceof Error ? e.message : String(e);
1558
+ const msg = publicOAuthAuthenticationErrorMessage(e);
1170
1559
  loginState.set(provider, { done: true, error: msg });
1171
1560
  if (!urlResolved) reject(e);
1561
+ }).finally(() => {
1562
+ if (provider === "kiro") kiroLoginSettling.delete(provider);
1172
1563
  });
1173
1564
  });
1174
1565
  }