@yansigit/opencodex 2.31.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 (826) hide show
  1. package/AGENTS_INSTALL.md +109 -0
  2. package/LICENSE +21 -0
  3. package/README.md +303 -0
  4. package/assets/architecture.png +0 -0
  5. package/assets/banner.png +0 -0
  6. package/assets/claude-code-models.gif +0 -0
  7. package/assets/codex-app-picker.png +0 -0
  8. package/bin/ocx.mjs +587 -0
  9. package/bin/package-main.mjs +9 -0
  10. package/gui/dist/assets/index-BNJ7r4Gd.js +102 -0
  11. package/gui/dist/assets/index-CGoDO3uO.css +1 -0
  12. package/gui/dist/favicon.png +0 -0
  13. package/gui/dist/icons.svg +24 -0
  14. package/gui/dist/index.html +25 -0
  15. package/gui/dist/logo.png +0 -0
  16. package/gui/dist/provider-icons/alibaba-color.svg +1 -0
  17. package/gui/dist/provider-icons/antigravity-color.svg +1 -0
  18. package/gui/dist/provider-icons/claude-color.svg +1 -0
  19. package/gui/dist/provider-icons/cline-color.svg +16 -0
  20. package/gui/dist/provider-icons/cloudflare-ai-gateway-color.svg +1 -0
  21. package/gui/dist/provider-icons/commandcode-color.svg +1 -0
  22. package/gui/dist/provider-icons/copilot-color.svg +1 -0
  23. package/gui/dist/provider-icons/cursor-color.svg +2 -0
  24. package/gui/dist/provider-icons/deepseek-color.svg +1 -0
  25. package/gui/dist/provider-icons/discord.svg +1 -0
  26. package/gui/dist/provider-icons/firepass-color.svg +1 -0
  27. package/gui/dist/provider-icons/fireworks-color.svg +1 -0
  28. package/gui/dist/provider-icons/gemini-color.svg +1 -0
  29. package/gui/dist/provider-icons/github-copilot-color.svg +1 -0
  30. package/gui/dist/provider-icons/gitlab-duo-color.svg +1 -0
  31. package/gui/dist/provider-icons/grok.svg +1 -0
  32. package/gui/dist/provider-icons/groq-color.svg +1 -0
  33. package/gui/dist/provider-icons/huggingface-color.svg +1 -0
  34. package/gui/dist/provider-icons/kimi-color.svg +1 -0
  35. package/gui/dist/provider-icons/kiro-color.svg +15 -0
  36. package/gui/dist/provider-icons/lm-studio-color.svg +1 -0
  37. package/gui/dist/provider-icons/mistral-color.svg +1 -0
  38. package/gui/dist/provider-icons/moonshot-color.svg +1 -0
  39. package/gui/dist/provider-icons/nvidia-color.svg +1 -0
  40. package/gui/dist/provider-icons/ollama-color.svg +1 -0
  41. package/gui/dist/provider-icons/openai.svg +1 -0
  42. package/gui/dist/provider-icons/opencode.svg +2 -0
  43. package/gui/dist/provider-icons/openrouter-color.svg +1 -0
  44. package/gui/dist/provider-icons/pi.svg +21 -0
  45. package/gui/dist/provider-icons/qianfan-color.svg +1 -0
  46. package/gui/dist/provider-icons/qwen-portal-color.svg +1 -0
  47. package/gui/dist/provider-icons/telegram.svg +1 -0
  48. package/gui/dist/provider-icons/vercel-ai-gateway-color.svg +1 -0
  49. package/gui/dist/provider-icons/vllm-color.svg +1 -0
  50. package/gui/dist/provider-icons/xiaomi-color.svg +1 -0
  51. package/package.json +108 -0
  52. package/src/AGENTS.md +28 -0
  53. package/src/adapters/anthropic-image-guard.ts +251 -0
  54. package/src/adapters/anthropic-image-normalize.ts +518 -0
  55. package/src/adapters/anthropic-output-schema.ts +137 -0
  56. package/src/adapters/anthropic.ts +1327 -0
  57. package/src/adapters/azure.ts +36 -0
  58. package/src/adapters/base.ts +121 -0
  59. package/src/adapters/client-fingerprint.ts +65 -0
  60. package/src/adapters/cline-pass-deepseek-v4-tool-replay.ts +69 -0
  61. package/src/adapters/command-code.ts +601 -0
  62. package/src/adapters/cursor/arg-codec.ts +38 -0
  63. package/src/adapters/cursor/arg-normalize.ts +104 -0
  64. package/src/adapters/cursor/checkpoint-store.ts +303 -0
  65. package/src/adapters/cursor/cursor-errors.ts +288 -0
  66. package/src/adapters/cursor/discovery.ts +333 -0
  67. package/src/adapters/cursor/effort-map.ts +151 -0
  68. package/src/adapters/cursor/exec-policy.ts +88 -0
  69. package/src/adapters/cursor/framing.ts +250 -0
  70. package/src/adapters/cursor/gen/agent_pb.ts +15274 -0
  71. package/src/adapters/cursor/h2-pool.ts +123 -0
  72. package/src/adapters/cursor/http1-bidi.ts +361 -0
  73. package/src/adapters/cursor/images.ts +704 -0
  74. package/src/adapters/cursor/kv-store.ts +52 -0
  75. package/src/adapters/cursor/live-models.ts +269 -0
  76. package/src/adapters/cursor/live-smoke-gate.ts +41 -0
  77. package/src/adapters/cursor/live-transport.ts +1653 -0
  78. package/src/adapters/cursor/mcp-config.ts +42 -0
  79. package/src/adapters/cursor/mcp-manager.ts +333 -0
  80. package/src/adapters/cursor/message-mapper.ts +49 -0
  81. package/src/adapters/cursor/native-exec-common.ts +76 -0
  82. package/src/adapters/cursor/native-exec-desktop.ts +184 -0
  83. package/src/adapters/cursor/native-exec-fs.ts +332 -0
  84. package/src/adapters/cursor/native-exec-mcp.ts +153 -0
  85. package/src/adapters/cursor/native-exec-network.ts +43 -0
  86. package/src/adapters/cursor/native-exec-shell.ts +547 -0
  87. package/src/adapters/cursor/native-exec-tools.ts +118 -0
  88. package/src/adapters/cursor/native-exec.ts +663 -0
  89. package/src/adapters/cursor/protobuf-events.ts +1381 -0
  90. package/src/adapters/cursor/protobuf-request.ts +1032 -0
  91. package/src/adapters/cursor/request-builder.ts +461 -0
  92. package/src/adapters/cursor/thread-continuity.ts +67 -0
  93. package/src/adapters/cursor/tool-definitions.ts +735 -0
  94. package/src/adapters/cursor/tool-result-normalize.ts +92 -0
  95. package/src/adapters/cursor/transport-retry.ts +132 -0
  96. package/src/adapters/cursor/transport.ts +79 -0
  97. package/src/adapters/cursor/types.ts +79 -0
  98. package/src/adapters/cursor.ts +322 -0
  99. package/src/adapters/google-antigravity-hosts.ts +48 -0
  100. package/src/adapters/google-antigravity-replay.ts +827 -0
  101. package/src/adapters/google-antigravity-tools.ts +105 -0
  102. package/src/adapters/google-antigravity-wire.ts +141 -0
  103. package/src/adapters/google-errors.ts +92 -0
  104. package/src/adapters/google-http.ts +460 -0
  105. package/src/adapters/google-tool-schema.ts +238 -0
  106. package/src/adapters/google-truncation.ts +24 -0
  107. package/src/adapters/google-wire-compiler.ts +232 -0
  108. package/src/adapters/google.ts +1377 -0
  109. package/src/adapters/identity.ts +77 -0
  110. package/src/adapters/image.ts +23 -0
  111. package/src/adapters/kiro-constants.ts +16 -0
  112. package/src/adapters/kiro-errors.ts +208 -0
  113. package/src/adapters/kiro-events.ts +197 -0
  114. package/src/adapters/kiro-images.ts +129 -0
  115. package/src/adapters/kiro-retry.ts +312 -0
  116. package/src/adapters/kiro-thinking.ts +112 -0
  117. package/src/adapters/kiro-tool-fallback.ts +36 -0
  118. package/src/adapters/kiro-tools.ts +224 -0
  119. package/src/adapters/kiro-truncation.ts +33 -0
  120. package/src/adapters/kiro-wire.ts +129 -0
  121. package/src/adapters/kiro.ts +1936 -0
  122. package/src/adapters/mimo-free.ts +280 -0
  123. package/src/adapters/openai-chat-url.ts +11 -0
  124. package/src/adapters/openai-chat.ts +1980 -0
  125. package/src/adapters/openai-responses-url.ts +16 -0
  126. package/src/adapters/openai-responses.ts +1911 -0
  127. package/src/adapters/registry.ts +175 -0
  128. package/src/adapters/responses-tool-schema.ts +67 -0
  129. package/src/adapters/run-turn-queue.ts +114 -0
  130. package/src/adapters/tool-call-id.ts +119 -0
  131. package/src/adapters/tool-catalog-nudge.ts +154 -0
  132. package/src/adapters/upstream-http-error.ts +48 -0
  133. package/src/adapters/xai-web-search.ts +185 -0
  134. package/src/bridge.ts +1986 -0
  135. package/src/chat/inbound.ts +319 -0
  136. package/src/chat/outbound.ts +821 -0
  137. package/src/claude/agents-inject.ts +266 -0
  138. package/src/claude/alias.ts +149 -0
  139. package/src/claude/auth-detect.ts +229 -0
  140. package/src/claude/auth-mode-migration.ts +32 -0
  141. package/src/claude/auth-mode.ts +62 -0
  142. package/src/claude/context-windows.ts +205 -0
  143. package/src/claude/desktop-3p-guard.ts +35 -0
  144. package/src/claude/desktop-3p-paths.ts +84 -0
  145. package/src/claude/desktop-3p.ts +615 -0
  146. package/src/claude/desktop-health.ts +26 -0
  147. package/src/claude/desktop-profile.ts +263 -0
  148. package/src/claude/gateway-cache.ts +107 -0
  149. package/src/claude/inbound-debug.ts +163 -0
  150. package/src/claude/inbound.ts +578 -0
  151. package/src/claude/model-info.ts +174 -0
  152. package/src/claude/outbound.ts +926 -0
  153. package/src/cli/access.ts +108 -0
  154. package/src/cli/account-api.ts +302 -0
  155. package/src/cli/account-auth.ts +250 -0
  156. package/src/cli/account-catalog-refresh.ts +14 -0
  157. package/src/cli/account-extended.ts +737 -0
  158. package/src/cli/account-main.ts +317 -0
  159. package/src/cli/account.ts +299 -0
  160. package/src/cli/agent-driven.ts +70 -0
  161. package/src/cli/agent.ts +290 -0
  162. package/src/cli/catalog-prewarm.ts +27 -0
  163. package/src/cli/claude-agent-startup-sync.ts +73 -0
  164. package/src/cli/claude-desktop.ts +213 -0
  165. package/src/cli/claude.ts +355 -0
  166. package/src/cli/codex-log-guard-doctor.ts +103 -0
  167. package/src/cli/codex-shim-autorestore.ts +47 -0
  168. package/src/cli/codex-shim-readiness.ts +76 -0
  169. package/src/cli/combo.ts +127 -0
  170. package/src/cli/config-command.ts +209 -0
  171. package/src/cli/debug.ts +228 -0
  172. package/src/cli/dispatch.ts +585 -0
  173. package/src/cli/doctor.ts +1202 -0
  174. package/src/cli/ensure-desired-integrations.ts +152 -0
  175. package/src/cli/export-command.ts +213 -0
  176. package/src/cli/help.ts +101 -0
  177. package/src/cli/index.ts +973 -0
  178. package/src/cli/init.ts +211 -0
  179. package/src/cli/integrations.ts +260 -0
  180. package/src/cli/interactive-confirm.ts +133 -0
  181. package/src/cli/lab.ts +607 -0
  182. package/src/cli/launcher-context.ts +77 -0
  183. package/src/cli/minimax.ts +497 -0
  184. package/src/cli/models-runtime.ts +245 -0
  185. package/src/cli/models.ts +422 -0
  186. package/src/cli/observe.ts +206 -0
  187. package/src/cli/opencode.ts +588 -0
  188. package/src/cli/provider-replit.ts +232 -0
  189. package/src/cli/provider-runtime.ts +179 -0
  190. package/src/cli/provider.ts +492 -0
  191. package/src/cli/ready.ts +301 -0
  192. package/src/cli/registry.ts +422 -0
  193. package/src/cli/replit-gateway-key-input.ts +138 -0
  194. package/src/cli/root.ts +86 -0
  195. package/src/cli/route-policy.ts +92 -0
  196. package/src/cli/runtime-api.ts +328 -0
  197. package/src/cli/star-prompt.ts +211 -0
  198. package/src/cli/status-oauth.ts +78 -0
  199. package/src/cli/status.ts +328 -0
  200. package/src/cli/system-command.ts +112 -0
  201. package/src/cli/system-restart-client.ts +146 -0
  202. package/src/cli/tray-proxy.ts +199 -0
  203. package/src/cli/v2.ts +268 -0
  204. package/src/cli.ts +10 -0
  205. package/src/clients/config-export.ts +1704 -0
  206. package/src/codex/account-id.ts +34 -0
  207. package/src/codex/account-label.ts +47 -0
  208. package/src/codex/account-lifecycle.ts +172 -0
  209. package/src/codex/account-namespace-match.ts +63 -0
  210. package/src/codex/account-namespaces.ts +195 -0
  211. package/src/codex/account-pause.ts +20 -0
  212. package/src/codex/account-priority.ts +83 -0
  213. package/src/codex/account-runtime-state.ts +31 -0
  214. package/src/codex/account-store.ts +544 -0
  215. package/src/codex/account-usability.ts +43 -0
  216. package/src/codex/admission.ts +256 -0
  217. package/src/codex/affinity-debug.ts +162 -0
  218. package/src/codex/agent-roles-sync.ts +225 -0
  219. package/src/codex/agent-roles.ts +238 -0
  220. package/src/codex/app-server-processes.ts +1143 -0
  221. package/src/codex/app-server-restart-service.ts +232 -0
  222. package/src/codex/auth-api.ts +2147 -0
  223. package/src/codex/auth-collision.ts +109 -0
  224. package/src/codex/auth-context.ts +665 -0
  225. package/src/codex/autostart-health.ts +156 -0
  226. package/src/codex/catalog/account-models.ts +67 -0
  227. package/src/codex/catalog/aggregation.ts +436 -0
  228. package/src/codex/catalog/bundled.ts +549 -0
  229. package/src/codex/catalog/effort.ts +446 -0
  230. package/src/codex/catalog/filesystem-evidence.ts +302 -0
  231. package/src/codex/catalog/kinds.ts +2 -0
  232. package/src/codex/catalog/metadata.ts +664 -0
  233. package/src/codex/catalog/native-models.ts +72 -0
  234. package/src/codex/catalog/parsing.ts +650 -0
  235. package/src/codex/catalog/provider-fetch.ts +2064 -0
  236. package/src/codex/catalog/sync.ts +1883 -0
  237. package/src/codex/catalog-admission.ts +199 -0
  238. package/src/codex/catalog-refresh-status.ts +105 -0
  239. package/src/codex/catalog-write-serialization.ts +242 -0
  240. package/src/codex/catalog.ts +14 -0
  241. package/src/codex/codex-write-lock.ts +384 -0
  242. package/src/codex/convergence-types.ts +614 -0
  243. package/src/codex/convergence.ts +651 -0
  244. package/src/codex/coordinator-doctor.ts +332 -0
  245. package/src/codex/custom-model-catalog-migration.ts +176 -0
  246. package/src/codex/data/upstream-models.json +830 -0
  247. package/src/codex/desired-state.ts +230 -0
  248. package/src/codex/exec-invocation.ts +22 -0
  249. package/src/codex/features.ts +1566 -0
  250. package/src/codex/generation.ts +202 -0
  251. package/src/codex/history-job.ts +407 -0
  252. package/src/codex/history-lock.ts +242 -0
  253. package/src/codex/history-migration-guardian.ts +108 -0
  254. package/src/codex/history-provider.ts +979 -0
  255. package/src/codex/history-transition.ts +105 -0
  256. package/src/codex/history-worker.ts +220 -0
  257. package/src/codex/home.ts +206 -0
  258. package/src/codex/inject-coordination.ts +290 -0
  259. package/src/codex/inject.ts +1733 -0
  260. package/src/codex/injected-marker.ts +106 -0
  261. package/src/codex/integration-record.ts +266 -0
  262. package/src/codex/internal/catalog-writer.ts +203 -0
  263. package/src/codex/internal/history-writer.ts +80 -0
  264. package/src/codex/journal.ts +225 -0
  265. package/src/codex/log-guard/inspect.ts +506 -0
  266. package/src/codex/log-guard/lock.ts +150 -0
  267. package/src/codex/log-guard/maintenance.ts +403 -0
  268. package/src/codex/log-guard/path-safety.ts +88 -0
  269. package/src/codex/log-guard/policy.ts +44 -0
  270. package/src/codex/log-guard/processes.ts +205 -0
  271. package/src/codex/log-guard/protection.ts +489 -0
  272. package/src/codex/log-guard/sqlite-errors.ts +9 -0
  273. package/src/codex/main-account-cache.ts +56 -0
  274. package/src/codex/main-account.ts +68 -0
  275. package/src/codex/management-convergence.ts +167 -0
  276. package/src/codex/model-cache.ts +273 -0
  277. package/src/codex/model-entitlements.ts +353 -0
  278. package/src/codex/native-main-admission.ts +47 -0
  279. package/src/codex/native-main-auth-temp.ts +187 -0
  280. package/src/codex/native-main-claim.ts +178 -0
  281. package/src/codex/native-main-lock-file.ts +162 -0
  282. package/src/codex/native-main-owner.ts +329 -0
  283. package/src/codex/native-profile-api.ts +247 -0
  284. package/src/codex/native-profile-manager.ts +1531 -0
  285. package/src/codex/native-profile-processes.ts +121 -0
  286. package/src/codex/native-profile-recovery.ts +99 -0
  287. package/src/codex/native-profile-stage-store.ts +387 -0
  288. package/src/codex/native-profile-startup.ts +492 -0
  289. package/src/codex/native-profile-store.ts +855 -0
  290. package/src/codex/native-profile-types.ts +120 -0
  291. package/src/codex/native-residue.ts +682 -0
  292. package/src/codex/paths.ts +144 -0
  293. package/src/codex/plan-from-token.ts +140 -0
  294. package/src/codex/plan.ts +40 -0
  295. package/src/codex/plugins-doctor.ts +242 -0
  296. package/src/codex/pool-rotation.ts +295 -0
  297. package/src/codex/project-config-warnings.ts +425 -0
  298. package/src/codex/prompt-journal.ts +352 -0
  299. package/src/codex/prompt-layers.ts +967 -0
  300. package/src/codex/prompt-lock.ts +143 -0
  301. package/src/codex/quota-rejection.ts +298 -0
  302. package/src/codex/quota.ts +573 -0
  303. package/src/codex/refresh.ts +62 -0
  304. package/src/codex/reset-credit-recovery.ts +1044 -0
  305. package/src/codex/routing.ts +1888 -0
  306. package/src/codex/runtime.ts +659 -0
  307. package/src/codex/shim.ts +2170 -0
  308. package/src/codex/subagent-defaults.ts +550 -0
  309. package/src/codex/subagent-model-fallback.ts +784 -0
  310. package/src/codex/sync.ts +319 -0
  311. package/src/codex/transition-state.ts +612 -0
  312. package/src/codex/upstream-host-health.ts +368 -0
  313. package/src/codex/user-identity.ts +557 -0
  314. package/src/codex/warmup.ts +298 -0
  315. package/src/codex/websocket-registry.ts +100 -0
  316. package/src/codex/write-coordination.ts +114 -0
  317. package/src/combos/failover.ts +160 -0
  318. package/src/combos/index.ts +45 -0
  319. package/src/combos/request.ts +94 -0
  320. package/src/combos/resolve.ts +232 -0
  321. package/src/combos/types.ts +398 -0
  322. package/src/config/provider-name.ts +24 -0
  323. package/src/config.ts +4041 -0
  324. package/src/fork/register.ts +3 -0
  325. package/src/generated/compatibility-version.json +3116 -0
  326. package/src/generated/model-metadata.ts +106 -0
  327. package/src/github/star-state.ts +203 -0
  328. package/src/grok/inject.ts +530 -0
  329. package/src/grok/inspect.ts +45 -0
  330. package/src/grok/status.ts +121 -0
  331. package/src/grok/sync.ts +66 -0
  332. package/src/images/artifacts.ts +516 -0
  333. package/src/images/fulfill-video.ts +163 -0
  334. package/src/images/fulfill.ts +149 -0
  335. package/src/images/index.ts +4 -0
  336. package/src/images/loop.ts +955 -0
  337. package/src/images/plan.ts +143 -0
  338. package/src/images/synthetic-tool.ts +133 -0
  339. package/src/images/types.ts +41 -0
  340. package/src/images/xai-client.ts +141 -0
  341. package/src/images/xai-video-client.ts +163 -0
  342. package/src/index.ts +22 -0
  343. package/src/integrations/config-io.ts +269 -0
  344. package/src/integrations/journal.ts +315 -0
  345. package/src/integrations/merge.ts +135 -0
  346. package/src/integrations/mutation-flight.ts +71 -0
  347. package/src/integrations/native/ownership-preflight.ts +202 -0
  348. package/src/integrations/omp-yaml-source.ts +358 -0
  349. package/src/integrations/owned-refresh.ts +74 -0
  350. package/src/integrations/ownership.ts +111 -0
  351. package/src/integrations/registry.ts +159 -0
  352. package/src/integrations/serialize.ts +314 -0
  353. package/src/integrations/state.ts +361 -0
  354. package/src/integrations/store.ts +103 -0
  355. package/src/integrations/writer-lock.ts +98 -0
  356. package/src/integrations/writer.ts +691 -0
  357. package/src/lab/artifacts/sanitize.ts +586 -0
  358. package/src/lab/artifacts/secure-fs.ts +475 -0
  359. package/src/lab/artifacts/store.ts +310 -0
  360. package/src/lab/automation/budgets.ts +78 -0
  361. package/src/lab/automation/config-persistence.ts +256 -0
  362. package/src/lab/automation/constants.ts +39 -0
  363. package/src/lab/automation/cooldown.ts +103 -0
  364. package/src/lab/automation/dispatch.ts +211 -0
  365. package/src/lab/automation/index.ts +13 -0
  366. package/src/lab/automation/orchestrator.ts +499 -0
  367. package/src/lab/automation/persistence.ts +512 -0
  368. package/src/lab/automation/planner.ts +371 -0
  369. package/src/lab/automation/policy.ts +136 -0
  370. package/src/lab/automation/queue.ts +191 -0
  371. package/src/lab/automation/recovery.ts +24 -0
  372. package/src/lab/automation/route-context.ts +21 -0
  373. package/src/lab/automation/run-key.ts +44 -0
  374. package/src/lab/automation/runs-query.ts +34 -0
  375. package/src/lab/automation/types.ts +160 -0
  376. package/src/lab/conformance/assertion.ts +325 -0
  377. package/src/lab/conformance/digest.ts +22 -0
  378. package/src/lab/conformance/executor.ts +741 -0
  379. package/src/lab/conformance/fixture-provider.ts +27 -0
  380. package/src/lab/conformance/fixtures/live-v1-cases.json +175 -0
  381. package/src/lab/conformance/fixtures/protocol-v1-cases.json +461 -0
  382. package/src/lab/conformance/harness-budget.ts +47 -0
  383. package/src/lab/conformance/index.ts +5 -0
  384. package/src/lab/conformance/jcs.ts +64 -0
  385. package/src/lab/conformance/json-pointer.ts +39 -0
  386. package/src/lab/conformance/manifest.ts +180 -0
  387. package/src/lab/conformance/mcp-stub.ts +179 -0
  388. package/src/lab/conformance/negative-controls.ts +164 -0
  389. package/src/lab/conformance/observation.ts +355 -0
  390. package/src/lab/conformance/runner.ts +68 -0
  391. package/src/lab/conformance/sse-normalize.ts +59 -0
  392. package/src/lab/conformance/suite-manifest.ts +78 -0
  393. package/src/lab/conformance/types.ts +214 -0
  394. package/src/lab/constants.ts +126 -0
  395. package/src/lab/digest.ts +64 -0
  396. package/src/lab/events/errors.ts +9 -0
  397. package/src/lab/events/limits.ts +117 -0
  398. package/src/lab/events/types.ts +229 -0
  399. package/src/lab/events/validate.ts +781 -0
  400. package/src/lab/fabric/constants.ts +40 -0
  401. package/src/lab/fabric/executor.ts +492 -0
  402. package/src/lab/fabric/index.ts +80 -0
  403. package/src/lab/fabric/manifest.ts +222 -0
  404. package/src/lab/fabric/observe.ts +489 -0
  405. package/src/lab/fabric/patch.ts +79 -0
  406. package/src/lab/fabric/producer-child.ts +139 -0
  407. package/src/lab/fabric/producer-isolate.ts +276 -0
  408. package/src/lab/fabric/producer-protocol.ts +61 -0
  409. package/src/lab/fabric/scratch.ts +439 -0
  410. package/src/lab/fabric/subject.ts +106 -0
  411. package/src/lab/fabric/types.ts +134 -0
  412. package/src/lab/fabric/verifier.ts +98 -0
  413. package/src/lab/index.ts +54 -0
  414. package/src/lab/ledger/artifact-refs.ts +127 -0
  415. package/src/lab/ledger/invalidation.ts +136 -0
  416. package/src/lab/ledger/purge.ts +310 -0
  417. package/src/lab/ledger/store.ts +532 -0
  418. package/src/lab/live/credential-lease.ts +53 -0
  419. package/src/lab/live/destination.ts +155 -0
  420. package/src/lab/live/executor.ts +336 -0
  421. package/src/lab/live/inert-tools.ts +56 -0
  422. package/src/lab/live/manifest.ts +85 -0
  423. package/src/lab/live/mcp-loopback.ts +57 -0
  424. package/src/lab/live/runner.ts +19 -0
  425. package/src/lab/live/sandbox.ts +61 -0
  426. package/src/lab/live/suite-manifest.ts +41 -0
  427. package/src/lab/live/transport.ts +118 -0
  428. package/src/lab/live/types.ts +197 -0
  429. package/src/lab/observe/from-conformance.ts +301 -0
  430. package/src/lab/observe/from-live.ts +117 -0
  431. package/src/lab/paths.ts +153 -0
  432. package/src/lab/projection/rebuild.ts +495 -0
  433. package/src/lab/projection/schema.ts +135 -0
  434. package/src/lab/projection/verdicts.ts +474 -0
  435. package/src/lab/projection/verification.ts +412 -0
  436. package/src/lab/public/bundle.ts +217 -0
  437. package/src/lab/public/community-authority.ts +175 -0
  438. package/src/lab/public/community-files.ts +29 -0
  439. package/src/lab/public/community.ts +479 -0
  440. package/src/lab/public/file-safety.ts +155 -0
  441. package/src/lab/public/ids.ts +26 -0
  442. package/src/lab/public/index.ts +16 -0
  443. package/src/lab/public/mutation-lock.ts +424 -0
  444. package/src/lab/public/operator.ts +353 -0
  445. package/src/lab/public/origin-purge.ts +79 -0
  446. package/src/lab/public/origin.ts +203 -0
  447. package/src/lab/public/privacy.ts +143 -0
  448. package/src/lab/public/private-file.ts +261 -0
  449. package/src/lab/public/project.ts +124 -0
  450. package/src/lab/public/purge-test-fault.ts +21 -0
  451. package/src/lab/public/purge.ts +223 -0
  452. package/src/lab/public/registry.ts +44 -0
  453. package/src/lab/public/revocation.ts +252 -0
  454. package/src/lab/public/signature.ts +243 -0
  455. package/src/lab/public/storage.ts +105 -0
  456. package/src/lab/public/strict-json.ts +206 -0
  457. package/src/lab/public/time.ts +26 -0
  458. package/src/lab/public/types.ts +172 -0
  459. package/src/lab/public/validate.ts +391 -0
  460. package/src/lab/query/catalog.ts +101 -0
  461. package/src/lab/query/connection.ts +107 -0
  462. package/src/lab/query/constants.ts +4 -0
  463. package/src/lab/query/cursor.ts +132 -0
  464. package/src/lab/query/dto-map.ts +277 -0
  465. package/src/lab/query/errors.ts +22 -0
  466. package/src/lab/query/freshness.ts +53 -0
  467. package/src/lab/query/index.ts +45 -0
  468. package/src/lab/query/latest-observation.ts +59 -0
  469. package/src/lab/query/passive-production.ts +159 -0
  470. package/src/lab/query/queries.ts +444 -0
  471. package/src/lab/query/types.ts +266 -0
  472. package/src/lab/subject/behavior-fingerprint.ts +77 -0
  473. package/src/lab/subject/installation-salt.ts +112 -0
  474. package/src/lab/subject/protocol-subject.ts +80 -0
  475. package/src/lab/subject/route-subject.ts +74 -0
  476. package/src/lib/abort.ts +146 -0
  477. package/src/lib/admin-secrets.ts +25 -0
  478. package/src/lib/admission.ts +83 -0
  479. package/src/lib/app-owned-memory-stores.ts +195 -0
  480. package/src/lib/app-owned-memory.ts +265 -0
  481. package/src/lib/bounded-body.ts +346 -0
  482. package/src/lib/bun-binary-validator.d.mts +3 -0
  483. package/src/lib/bun-binary-validator.mjs +18 -0
  484. package/src/lib/bun-runtime.ts +184 -0
  485. package/src/lib/bun-stream-caps.ts +130 -0
  486. package/src/lib/codex-restart-contract.ts +120 -0
  487. package/src/lib/config-ownership.ts +364 -0
  488. package/src/lib/crash-guard.ts +344 -0
  489. package/src/lib/debug-log-buffer.ts +83 -0
  490. package/src/lib/debug-settings.ts +108 -0
  491. package/src/lib/debug.ts +31 -0
  492. package/src/lib/destination-policy.ts +380 -0
  493. package/src/lib/errors.ts +406 -0
  494. package/src/lib/eventstream-decoder.ts +253 -0
  495. package/src/lib/fabric-task-execution-authority.ts +7 -0
  496. package/src/lib/fabric-task-host.ts +29 -0
  497. package/src/lib/gcp-adc.ts +341 -0
  498. package/src/lib/injection-debug-log.ts +58 -0
  499. package/src/lib/lab-activation.ts +223 -0
  500. package/src/lib/lab-live-execution-authority.ts +13 -0
  501. package/src/lib/lab-live-host.ts +30 -0
  502. package/src/lib/lab-live-pinned-sender.ts +56 -0
  503. package/src/lib/lab-live-route-production.ts +130 -0
  504. package/src/lib/lab-passive-linker-registration.ts +26 -0
  505. package/src/lib/local-management-attestation.ts +51 -0
  506. package/src/lib/local-management-capability.ts +100 -0
  507. package/src/lib/local-provider-reload-contract.ts +100 -0
  508. package/src/lib/open-url.ts +25 -0
  509. package/src/lib/optional-shutdown-hooks.ts +57 -0
  510. package/src/lib/pinned-http.ts +270 -0
  511. package/src/lib/privacy.ts +20 -0
  512. package/src/lib/process-control.ts +168 -0
  513. package/src/lib/provider-outbound.ts +210 -0
  514. package/src/lib/provider-url.ts +14 -0
  515. package/src/lib/proxy-env.ts +18 -0
  516. package/src/lib/redact.ts +521 -0
  517. package/src/lib/retry-after.ts +55 -0
  518. package/src/lib/self-launch-argv.ts +15 -0
  519. package/src/lib/server-resource-ownership.ts +71 -0
  520. package/src/lib/service-secrets.ts +25 -0
  521. package/src/lib/shadow-call.ts +61 -0
  522. package/src/lib/sidecar-tracker.ts +52 -0
  523. package/src/lib/sse-decoder.ts +364 -0
  524. package/src/lib/state-store-registrations.ts +119 -0
  525. package/src/lib/state-store-sweeper.ts +184 -0
  526. package/src/lib/system-restart-contract.ts +73 -0
  527. package/src/lib/test-home-guard.ts +90 -0
  528. package/src/lib/token-estimate.ts +86 -0
  529. package/src/lib/tool-argument-integers.ts +202 -0
  530. package/src/lib/translator-budget.ts +400 -0
  531. package/src/lib/upstream-http-version.ts +57 -0
  532. package/src/lib/upstream-reachability.ts +95 -0
  533. package/src/lib/upstream-retry.ts +392 -0
  534. package/src/lib/win-exec.ts +115 -0
  535. package/src/lib/win-paths.ts +68 -0
  536. package/src/lib/windows-atomic-replace.ts +156 -0
  537. package/src/lib/windows-elevation.ts +773 -0
  538. package/src/lib/windows-secret-acl.ts +854 -0
  539. package/src/lib/windows-service-wrappers.ts +72 -0
  540. package/src/lib/windows-text.ts +106 -0
  541. package/src/lib/windows-user-principal.ts +341 -0
  542. package/src/lib/winsw.ts +403 -0
  543. package/src/oauth/account-import/google-antigravity-adapter.ts +74 -0
  544. package/src/oauth/account-import/index.ts +15 -0
  545. package/src/oauth/account-import/parser.ts +83 -0
  546. package/src/oauth/account-import/registry.ts +18 -0
  547. package/src/oauth/account-import/service.ts +75 -0
  548. package/src/oauth/account-import/types.ts +91 -0
  549. package/src/oauth/anthropic-routing.ts +594 -0
  550. package/src/oauth/anthropic.ts +188 -0
  551. package/src/oauth/antigravity-routing.ts +151 -0
  552. package/src/oauth/callback-server.ts +300 -0
  553. package/src/oauth/chatgpt.ts +161 -0
  554. package/src/oauth/command-code.ts +239 -0
  555. package/src/oauth/cursor.ts +252 -0
  556. package/src/oauth/github-copilot.ts +428 -0
  557. package/src/oauth/google-antigravity.ts +262 -0
  558. package/src/oauth/health.ts +407 -0
  559. package/src/oauth/index.ts +1504 -0
  560. package/src/oauth/key-providers.ts +124 -0
  561. package/src/oauth/kimi.ts +227 -0
  562. package/src/oauth/kiro-credentials.ts +726 -0
  563. package/src/oauth/kiro.ts +621 -0
  564. package/src/oauth/local-token-detect.ts +130 -0
  565. package/src/oauth/log.ts +50 -0
  566. package/src/oauth/login-cli.ts +223 -0
  567. package/src/oauth/nous.ts +798 -0
  568. package/src/oauth/pkce.ts +15 -0
  569. package/src/oauth/store.ts +728 -0
  570. package/src/oauth/token-guardian.ts +309 -0
  571. package/src/oauth/types.ts +62 -0
  572. package/src/oauth/xai.ts +241 -0
  573. package/src/providers/alibaba-region-backup.ts +75 -0
  574. package/src/providers/alibaba-region-migration.ts +156 -0
  575. package/src/providers/alibaba-region-startup.ts +36 -0
  576. package/src/providers/antigravity-models.ts +695 -0
  577. package/src/providers/antigravity-quota.ts +216 -0
  578. package/src/providers/api-keys.ts +140 -0
  579. package/src/providers/base-url-choices.ts +74 -0
  580. package/src/providers/codex-capacity.ts +292 -0
  581. package/src/providers/command-code-efforts.ts +144 -0
  582. package/src/providers/context-cap.ts +82 -0
  583. package/src/providers/cursor-pool.ts +72 -0
  584. package/src/providers/derive.ts +586 -0
  585. package/src/providers/fastwire.ts +501 -0
  586. package/src/providers/free-directory.ts +187 -0
  587. package/src/providers/github-copilot-transport.ts +56 -0
  588. package/src/providers/google-vertex-location.ts +14 -0
  589. package/src/providers/key-failover.ts +271 -0
  590. package/src/providers/kiro-models.ts +67 -0
  591. package/src/providers/label.ts +19 -0
  592. package/src/providers/model-discovery-limits.ts +16 -0
  593. package/src/providers/model-discovery.ts +449 -0
  594. package/src/providers/model-rename-migration.ts +255 -0
  595. package/src/providers/model-rename-startup.ts +28 -0
  596. package/src/providers/openai-sidecar.ts +243 -0
  597. package/src/providers/openai-tier-startup.ts +56 -0
  598. package/src/providers/openai-tiers.ts +423 -0
  599. package/src/providers/openai-virtual-models.ts +83 -0
  600. package/src/providers/opencode-zen-rate-limit.ts +102 -0
  601. package/src/providers/openrouter-routing.ts +102 -0
  602. package/src/providers/provider-id-rewrite.ts +185 -0
  603. package/src/providers/quota.ts +2345 -0
  604. package/src/providers/registry.ts +2918 -0
  605. package/src/providers/replit/constants.ts +27 -0
  606. package/src/providers/replit/derive.ts +85 -0
  607. package/src/providers/replit/headers.ts +28 -0
  608. package/src/providers/replit/origin.ts +55 -0
  609. package/src/providers/replit/pair-install-response.ts +72 -0
  610. package/src/providers/replit/probe.ts +199 -0
  611. package/src/providers/replit/setup.ts +350 -0
  612. package/src/providers/request-pacing.ts +310 -0
  613. package/src/providers/service-tier.ts +277 -0
  614. package/src/providers/slug-codec.ts +103 -0
  615. package/src/providers/static-model-discovery.ts +86 -0
  616. package/src/providers/xai-responses-opt-in.ts +15 -0
  617. package/src/providers/xai-transport.ts +148 -0
  618. package/src/reasoning-effort.ts +183 -0
  619. package/src/responses/compaction.ts +142 -0
  620. package/src/responses/custom-tool-compat.ts +266 -0
  621. package/src/responses/hosted-tool-policy.ts +9 -0
  622. package/src/responses/namespace-tool-compat.ts +355 -0
  623. package/src/responses/parser.ts +838 -0
  624. package/src/responses/provider-continuation.ts +98 -0
  625. package/src/responses/provider-opaque-metadata.ts +73 -0
  626. package/src/responses/reasoning-envelope.ts +60 -0
  627. package/src/responses/reasoning-replay-cache.ts +426 -0
  628. package/src/responses/schema.ts +165 -0
  629. package/src/responses/spill-store.ts +459 -0
  630. package/src/responses/state.ts +1433 -0
  631. package/src/responses/thought-signature-replay.ts +347 -0
  632. package/src/responses/tool-groups.ts +19 -0
  633. package/src/responses/tool-search-compat.ts +301 -0
  634. package/src/responses/truncated-stop-reason.ts +60 -0
  635. package/src/router.ts +761 -0
  636. package/src/routing/analytics.ts +378 -0
  637. package/src/routing/capability.ts +244 -0
  638. package/src/routing/compatibility/assemble.ts +73 -0
  639. package/src/routing/compatibility/behavior.ts +278 -0
  640. package/src/routing/compatibility/catalog.ts +99 -0
  641. package/src/routing/compatibility/endpoint.ts +52 -0
  642. package/src/routing/compatibility/lab-evidence-provider.ts +130 -0
  643. package/src/routing/compatibility/policy.ts +181 -0
  644. package/src/routing/compatibility/provider-slot.ts +56 -0
  645. package/src/routing/compatibility/reader.ts +110 -0
  646. package/src/routing/compatibility/subject.ts +191 -0
  647. package/src/routing/compatibility/types.ts +64 -0
  648. package/src/routing/compatibility/version.ts +104 -0
  649. package/src/routing/cost.ts +77 -0
  650. package/src/routing/evaluator.ts +495 -0
  651. package/src/routing/health.ts +412 -0
  652. package/src/routing/history/cursor.ts +43 -0
  653. package/src/routing/history/indexer.ts +605 -0
  654. package/src/routing/history/schema.ts +72 -0
  655. package/src/routing/profile-namespace.ts +15 -0
  656. package/src/routing/profile.ts +547 -0
  657. package/src/routing/quota.ts +145 -0
  658. package/src/routing/request-evidence.ts +45 -0
  659. package/src/routing/trace.ts +776 -0
  660. package/src/server/adapter-resolve.ts +53 -0
  661. package/src/server/auth-cors.ts +751 -0
  662. package/src/server/background-lifecycle.ts +182 -0
  663. package/src/server/chat-completions.ts +442 -0
  664. package/src/server/chat-native-sse.ts +331 -0
  665. package/src/server/chat-native.ts +426 -0
  666. package/src/server/claude-messages.ts +1030 -0
  667. package/src/server/direct-local-http.ts +347 -0
  668. package/src/server/effort-policy.ts +190 -0
  669. package/src/server/github-copilot-responses-repair.ts +338 -0
  670. package/src/server/gui-static.ts +152 -0
  671. package/src/server/image-retry.ts +42 -0
  672. package/src/server/images.ts +568 -0
  673. package/src/server/index.ts +1813 -0
  674. package/src/server/lifecycle.ts +498 -0
  675. package/src/server/live.ts +717 -0
  676. package/src/server/local-management-read-client.ts +90 -0
  677. package/src/server/local-provider-reload-client.ts +137 -0
  678. package/src/server/management/agent-settings-routes.ts +1433 -0
  679. package/src/server/management/api-access.ts +141 -0
  680. package/src/server/management/api-key-usage.ts +193 -0
  681. package/src/server/management/body.ts +41 -0
  682. package/src/server/management/combo-routes.ts +263 -0
  683. package/src/server/management/config-routes.ts +835 -0
  684. package/src/server/management/context.ts +113 -0
  685. package/src/server/management/integration-routes.ts +498 -0
  686. package/src/server/management/lab-automation-routes.ts +206 -0
  687. package/src/server/management/lab-routes.ts +563 -0
  688. package/src/server/management/logs-usage-routes.ts +586 -0
  689. package/src/server/management/model-routes.ts +560 -0
  690. package/src/server/management/model-rows.ts +163 -0
  691. package/src/server/management/native-integration-routes.ts +769 -0
  692. package/src/server/management/oauth-account-routes.ts +637 -0
  693. package/src/server/management/provider-capability-config.ts +48 -0
  694. package/src/server/management/provider-routes.ts +1033 -0
  695. package/src/server/management/replit-provider-routes.ts +86 -0
  696. package/src/server/management/request-history-routes.ts +191 -0
  697. package/src/server/management/routing-analytics-routes.ts +74 -0
  698. package/src/server/management/routing-profile-routes.ts +385 -0
  699. package/src/server/management/shared.ts +286 -0
  700. package/src/server/management/sidebar-routes.ts +106 -0
  701. package/src/server/management/storage-log-guard-routes.ts +186 -0
  702. package/src/server/management/sync-response.ts +69 -0
  703. package/src/server/management/system-restart.ts +435 -0
  704. package/src/server/management/system-routes.ts +194 -0
  705. package/src/server/management/usage-summary-cache.ts +94 -0
  706. package/src/server/management/vision-sidecar-options.ts +167 -0
  707. package/src/server/management/web-search-sidecar-options.ts +120 -0
  708. package/src/server/management-api.ts +314 -0
  709. package/src/server/management-auth.ts +482 -0
  710. package/src/server/memory-watchdog.ts +156 -0
  711. package/src/server/passive-route-linker.ts +66 -0
  712. package/src/server/port-reclaim.ts +307 -0
  713. package/src/server/ports.ts +156 -0
  714. package/src/server/proxy-liveness.ts +328 -0
  715. package/src/server/readiness.ts +99 -0
  716. package/src/server/relay-eager.ts +353 -0
  717. package/src/server/relay.ts +1209 -0
  718. package/src/server/request-decompress.ts +239 -0
  719. package/src/server/request-log-conversation.ts +168 -0
  720. package/src/server/request-log.ts +1259 -0
  721. package/src/server/responses/agent-task-recovery-cache.ts +143 -0
  722. package/src/server/responses/agent-task-recovery.ts +465 -0
  723. package/src/server/responses/collaboration.ts +551 -0
  724. package/src/server/responses/compact.ts +771 -0
  725. package/src/server/responses/core.ts +5389 -0
  726. package/src/server/responses/empty-completion-guard.ts +276 -0
  727. package/src/server/responses/encrypted-payload.ts +331 -0
  728. package/src/server/responses/fetch-helpers.ts +232 -0
  729. package/src/server/responses/input-admission.ts +185 -0
  730. package/src/server/responses/pacing-overload.ts +13 -0
  731. package/src/server/responses/passthrough-error.ts +78 -0
  732. package/src/server/responses/policy-fallback.ts +178 -0
  733. package/src/server/responses/responses-field-backfill.ts +251 -0
  734. package/src/server/responses/terminal-guard.ts +251 -0
  735. package/src/server/responses/upstream-error.ts +53 -0
  736. package/src/server/responses/ws-upstream.ts +308 -0
  737. package/src/server/responses-custom-tool-repair.ts +282 -0
  738. package/src/server/responses-image-gen-repair.ts +132 -0
  739. package/src/server/responses-item-id-repair.ts +272 -0
  740. package/src/server/responses-json-events.ts +90 -0
  741. package/src/server/responses-model-rewrite.ts +29 -0
  742. package/src/server/responses-reasoning-summary-rewrite.ts +178 -0
  743. package/src/server/responses-snapshot-repair.ts +621 -0
  744. package/src/server/responses-terminal-repair.ts +342 -0
  745. package/src/server/responses-tool-search-repair.ts +267 -0
  746. package/src/server/responses-undeclared-tool-guard.ts +153 -0
  747. package/src/server/responses.ts +25 -0
  748. package/src/server/search.ts +201 -0
  749. package/src/server/sse-frame-buffer.ts +292 -0
  750. package/src/server/sse-payload-rewrite.ts +263 -0
  751. package/src/server/startup-action-control.ts +315 -0
  752. package/src/server/startup-health-cache.ts +131 -0
  753. package/src/server/system-env.ts +484 -0
  754. package/src/server/windows-tcp-drop.ts +184 -0
  755. package/src/server/windows-tray-control.ts +41 -0
  756. package/src/server/ws-bridge.ts +472 -0
  757. package/src/service-manager-probe.ts +892 -0
  758. package/src/service.ts +3575 -0
  759. package/src/sidecar/auth.ts +92 -0
  760. package/src/sidecar/candidates.ts +83 -0
  761. package/src/stall-timeout.ts +20 -0
  762. package/src/storage/cleanup-job.ts +57 -0
  763. package/src/storage/cleanup.ts +3085 -0
  764. package/src/storage/policy-job.ts +457 -0
  765. package/src/storage/policy-scheduler.ts +40 -0
  766. package/src/storage/policy-worker.ts +59 -0
  767. package/src/storage/policy.ts +527 -0
  768. package/src/storage/restore-job.ts +299 -0
  769. package/src/storage/restore-worker.ts +58 -0
  770. package/src/storage/scanner.ts +238 -0
  771. package/src/storage/storage-mutation-coordinator.ts +139 -0
  772. package/src/storage/worker-lifecycle.ts +215 -0
  773. package/src/tray/assets/opencodex-tray-offline.ico +0 -0
  774. package/src/tray/assets/opencodex-tray-online.ico +0 -0
  775. package/src/tray/assets/opencodex-tray-warning.ico +0 -0
  776. package/src/tray/assets/opencodex-tray.png +0 -0
  777. package/src/tray/windows-tray.ps1 +364 -0
  778. package/src/tray/windows.ts +757 -0
  779. package/src/types/accounts.ts +37 -0
  780. package/src/types/config.ts +876 -0
  781. package/src/types/provider.ts +545 -0
  782. package/src/types/request.ts +384 -0
  783. package/src/types/tools.ts +131 -0
  784. package/src/types/wire.ts +80 -0
  785. package/src/types.ts +106 -0
  786. package/src/update/badge.ts +72 -0
  787. package/src/update/index.ts +415 -0
  788. package/src/update/job.ts +1887 -0
  789. package/src/update/notify.ts +263 -0
  790. package/src/update/npm-cache-preflight.d.mts +47 -0
  791. package/src/update/npm-cache-preflight.mjs +201 -0
  792. package/src/update/npm-invocation.d.mts +23 -0
  793. package/src/update/npm-invocation.mjs +94 -0
  794. package/src/update/transactional-install.d.mts +22 -0
  795. package/src/update/transactional-install.mjs +259 -0
  796. package/src/update/tray-update-plan.d.mts +18 -0
  797. package/src/update/tray-update-plan.mjs +38 -0
  798. package/src/usage/cost.ts +625 -0
  799. package/src/usage/debug.ts +97 -0
  800. package/src/usage/expected-prices.ts +416 -0
  801. package/src/usage/log.ts +1223 -0
  802. package/src/usage/summary.ts +753 -0
  803. package/src/usage/totals.ts +14 -0
  804. package/src/usage/user-cost-overlay-reconciler.ts +313 -0
  805. package/src/usage/user-cost-overlays.ts +314 -0
  806. package/src/vision/anthropic-describe.ts +189 -0
  807. package/src/vision/backends.ts +97 -0
  808. package/src/vision/describe.ts +131 -0
  809. package/src/vision/eligibility.ts +250 -0
  810. package/src/vision/index.ts +681 -0
  811. package/src/vision/reasoning.ts +55 -0
  812. package/src/vision/routed-describe.ts +175 -0
  813. package/src/vision/timeout-bounds.ts +9 -0
  814. package/src/web-search/anthropic-executor.ts +195 -0
  815. package/src/web-search/backends.ts +108 -0
  816. package/src/web-search/exa-executor.ts +88 -0
  817. package/src/web-search/executor.ts +113 -0
  818. package/src/web-search/format-result.ts +89 -0
  819. package/src/web-search/gemini-executor.ts +141 -0
  820. package/src/web-search/index.ts +331 -0
  821. package/src/web-search/loop.ts +896 -0
  822. package/src/web-search/parse.ts +315 -0
  823. package/src/web-search/progress-stream.ts +342 -0
  824. package/src/web-search/sources.ts +60 -0
  825. package/src/web-search/synthetic-tool.ts +47 -0
  826. package/src/web-search/xai-executor.ts +219 -0
@@ -0,0 +1,728 @@
1
+ /**
2
+ * OAuth token store at ~/.opencodex/auth.json, keyed by provider name.
3
+ *
4
+ * Multiauth shape (260706): each provider value is a ProviderAccountSet
5
+ * `{ activeAccountId, accounts: [{ id, credential, needsReauth?, addedAt? }] }`.
6
+ * Legacy single-credential values (`{ access, refresh, expires, ... }`) normalize on load,
7
+ * and the first new-shape persist writes a one-time `auth.json.pre-multiauth` backup so a
8
+ * downgraded loader (which silently drops unknown shapes) cannot destroy refresh tokens.
9
+ *
10
+ * Exceptions:
11
+ * - `chatgpt` stays single-slot (always replaced): codex-auth-api uses it as a scratch slot
12
+ * for Codex pool logins, which have their own ledger (codex-accounts.json).
13
+ * - Credentials without identity (no accountId/email) replace the active slot on a normal
14
+ * login: their refresh tokens rotate, so a derived id would duplicate the same human on every
15
+ * re-login. An explicit add-account login instead preserves the prior slot and appends a
16
+ * distinct one. Kimi extracts JWT `user_id`/`sub` as accountId; Cursor extracts JWT `sub` —
17
+ * both append distinct identified accounts under multiauth.
18
+ */
19
+ import { createHash, randomUUID } from "node:crypto";
20
+ import { chmodSync, closeSync, copyFileSync, existsSync, fstatSync, mkdirSync, openSync, readFileSync, statSync, unlinkSync, writeFileSync } from "node:fs";
21
+ import { join } from "node:path";
22
+ import { getConfigDir, atomicWriteFile, backupInvalidConfig, hardenConfigDir, hardenExistingSecret } from "../config";
23
+ import { assertNotRealHomeUnderTest } from "../lib/test-home-guard";
24
+ import { recordOwnedConfigPath } from "../lib/config-ownership";
25
+ import { MAX_PENDING_OAUTH_MUTATIONS } from "../lib/translator-budget";
26
+ import {
27
+ captureConfigGeneration,
28
+ type GenerationContext,
29
+ } from "../lib/state-store-sweeper";
30
+ import { validateCopilotApiBaseUrl } from "./github-copilot";
31
+ import type { OAuthCredentialSource, OAuthCredentials, ProviderAccount, ProviderAccountSet } from "./types";
32
+
33
+ export type AuthStore = Record<string, ProviderAccountSet>;
34
+
35
+ export type AuthStoreBufferSnapshot =
36
+ | { readonly kind: "ready"; readonly store: AuthStore }
37
+ | { readonly kind: "absent" }
38
+ | { readonly kind: "malformed" };
39
+
40
+ const authStoreDecoder = new TextDecoder("utf-8", { fatal: true });
41
+ let lastReconciledGeneration = 0;
42
+ let liveOAuthAccountKeys = new Set<string>();
43
+
44
+ function oauthAccountKey(provider: string, accountId: string): string {
45
+ return `${provider}\0${accountId}`;
46
+ }
47
+
48
+ export function reconcileOAuthReauthState(context: GenerationContext): number {
49
+ if (context.generation <= lastReconciledGeneration) return 0;
50
+ liveOAuthAccountKeys = new Set(context.oauthAccountKeys);
51
+ lastReconciledGeneration = context.generation;
52
+ return 0;
53
+ }
54
+
55
+ /** Providers whose account set is pinned to a single slot (see module doc). */
56
+ const SINGLE_SLOT_PROVIDERS = new Set(["chatgpt"]);
57
+
58
+ export function getAuthStorePath(): string {
59
+ return join(getConfigDir(), "auth.json");
60
+ }
61
+ export function getAuthStoreLockPath(): string { return join(getConfigDir(), "auth.store.lock"); }
62
+ export function getAuthRefreshIntentLockPath(provider: string, accountId: string): string {
63
+ const safeProvider = provider.replace(/[^a-zA-Z0-9_-]/g, "_");
64
+ const accountHash = createHash("sha256").update(accountId).digest("hex").slice(0, 24);
65
+ return join(getConfigDir(), `auth.refresh.${safeProvider}.${accountHash}.lock`);
66
+ }
67
+ export function getAuthRefreshIntentPath(provider: string, accountId: string): string {
68
+ return `${getAuthRefreshIntentLockPath(provider, accountId)}.json`;
69
+ }
70
+ export interface OAuthRefreshIntent { version: 1; provider: string; accountId: string; generation: string; createdAt: number; flightId?: string; staleOwner?: true; uncertain?: true }
71
+ function parseOAuthRefreshIntent(
72
+ provider: string,
73
+ accountId: string,
74
+ raw: string,
75
+ ): OAuthRefreshIntent {
76
+ const value = JSON.parse(raw) as Partial<OAuthRefreshIntent>;
77
+ if (
78
+ value.version !== 1
79
+ || value.provider !== provider
80
+ || value.accountId !== accountId
81
+ || typeof value.generation !== "string"
82
+ || typeof value.createdAt !== "number"
83
+ || (value.flightId !== undefined && typeof value.flightId !== "string")
84
+ || (value.staleOwner !== undefined && value.staleOwner !== true)
85
+ ) {
86
+ return { version: 1, provider, accountId, generation: "", createdAt: 0, uncertain: true };
87
+ }
88
+ return value as OAuthRefreshIntent;
89
+ }
90
+
91
+ export function readOAuthRefreshIntent(provider: string, accountId: string): OAuthRefreshIntent | undefined {
92
+ const path = getAuthRefreshIntentPath(provider, accountId);
93
+ try {
94
+ hardenConfigDir();
95
+ hardenExistingSecret(path);
96
+ return parseOAuthRefreshIntent(provider, accountId, readFileSync(path, "utf8"));
97
+ } catch (error) {
98
+ if (errorCode(error) === "ENOENT") return undefined;
99
+ return { version: 1, provider, accountId, generation: "", createdAt: 0, uncertain: true };
100
+ }
101
+ }
102
+
103
+ /** Observe-only intent read for diagnostics: no chmod/ACL hardening. */
104
+ export function peekOAuthRefreshIntent(provider: string, accountId: string): OAuthRefreshIntent | undefined {
105
+ const path = getAuthRefreshIntentPath(provider, accountId);
106
+ try {
107
+ return parseOAuthRefreshIntent(provider, accountId, readFileSync(path, "utf8"));
108
+ } catch (error) {
109
+ if (errorCode(error) === "ENOENT") return undefined;
110
+ return { version: 1, provider, accountId, generation: "", createdAt: 0, uncertain: true };
111
+ }
112
+ }
113
+ export function writeOAuthRefreshIntent(provider: string, accountId: string, generation: string, createdAt = Date.now(), flightId?: string): void {
114
+ const dir = getConfigDir();
115
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true, mode: 0o700 });
116
+ hardenConfigDir();
117
+ const intent: OAuthRefreshIntent = { version: 1, provider, accountId, generation, createdAt, ...(flightId ? { flightId } : {}) };
118
+ atomicWriteFile(getAuthRefreshIntentPath(provider, accountId), `${JSON.stringify(intent)}\n`);
119
+ }
120
+ export function markOAuthRefreshIntentStaleOwner(provider: string, accountId: string, generation: string, flightId: string): boolean {
121
+ const current = readOAuthRefreshIntent(provider, accountId);
122
+ if (current?.uncertain || current?.generation !== generation || current.flightId !== flightId) return false;
123
+ atomicWriteFile(getAuthRefreshIntentPath(provider, accountId), `${JSON.stringify({ ...current, staleOwner: true })}\n`);
124
+ return true;
125
+ }
126
+ export function clearOAuthRefreshIntent(provider: string, accountId: string, generation: string): boolean {
127
+ const current = readOAuthRefreshIntent(provider, accountId);
128
+ if (!current || current.generation !== generation) return false;
129
+ try { unlinkSync(getAuthRefreshIntentPath(provider, accountId)); return true; }
130
+ catch (error) { if (errorCode(error) === "ENOENT") return false; throw error; }
131
+ }
132
+ export function credentialGeneration(cred: OAuthCredentials): string {
133
+ return createHash("sha256").update(JSON.stringify([cred.refresh, cred.access, cred.expires])).digest("hex");
134
+ }
135
+
136
+ function loadAuthStoreInternal(): { store: AuthStore; hadLegacy: boolean } {
137
+ const path = getAuthStorePath();
138
+ hardenConfigDir();
139
+ hardenExistingSecret(path);
140
+ if (!existsSync(path)) return { store: {}, hadLegacy: false };
141
+ try {
142
+ return normalizeAuthStore(JSON.parse(readFileSync(path, "utf-8")));
143
+ } catch {
144
+ backupInvalidConfig(path);
145
+ return { store: {}, hadLegacy: false };
146
+ }
147
+ }
148
+
149
+ export function loadAuthStore(): AuthStore {
150
+ return loadAuthStoreInternal().store;
151
+ }
152
+
153
+ /**
154
+ * Pure normalization for auth-store bytes already read by another owner.
155
+ * This function performs no filesystem consultation, hardening, backup, or persistence.
156
+ */
157
+ export function normalizeAuthStoreBuffer(buffer: Uint8Array | null): AuthStoreBufferSnapshot {
158
+ if (buffer === null) return { kind: "absent" };
159
+ try {
160
+ const parsed: unknown = JSON.parse(authStoreDecoder.decode(buffer));
161
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
162
+ return { kind: "malformed" };
163
+ }
164
+ const { store } = normalizeAuthStore(parsed);
165
+ if (Object.keys(parsed).length > 0 && Object.keys(store).length === 0) {
166
+ return { kind: "malformed" };
167
+ }
168
+ return { kind: "ready", store };
169
+ } catch {
170
+ return { kind: "malformed" };
171
+ }
172
+ }
173
+
174
+ /**
175
+ * Observe-only auth store read for diagnostics (`ocx doctor` / status).
176
+ * Does not chmod paths or backup invalid JSON — corrupt files are treated as empty.
177
+ */
178
+ export function peekAuthStore(): AuthStore {
179
+ const path = getAuthStorePath();
180
+ if (!existsSync(path)) return {};
181
+ const snapshot = normalizeAuthStoreBuffer(readFileSync(path));
182
+ return snapshot.kind === "ready" ? snapshot.store : {};
183
+ }
184
+
185
+ function persist(store: AuthStore): void {
186
+ const dir = getConfigDir();
187
+ assertNotRealHomeUnderTest(dir);
188
+ if (!existsSync(dir)) {
189
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
190
+ } else {
191
+ try { chmodSync(dir, 0o700); } catch { /* best-effort on existing dir */ }
192
+ }
193
+ hardenConfigDir();
194
+ atomicWriteFile(getAuthStorePath(), JSON.stringify(store, null, 2) + "\n");
195
+ }
196
+
197
+ export class OAuthFileLockError extends Error { readonly code = "OAUTH_FILE_LOCK_UNAVAILABLE"; constructor(message: string, options?: { cause?: unknown }) { super(message, options); this.name = "OAuthFileLockError"; } }
198
+ interface LockSnapshot { bytes: string; dev: number; ino: number; mtimeMs: number; size: number }
199
+ export interface OAuthFileLockOptions { path: string; waitTimeoutMs?: number; staleAfterMs?: number; pollMinMs?: number; pollMaxMs?: number; sleep?: (ms: number) => Promise<void>; now?: () => number; random?: () => number; beforeStaleUnlink?: () => void; beforeReleaseUnlink?: () => void; beforeFailedCreateUnlink?: () => void; writeMetadata?: (fd: number, bytes: string) => void }
200
+ export interface OAuthFileLockGuard { readonly ownerId: string; release(): void }
201
+ function errorCode(error: unknown): string | undefined { return error && typeof error === "object" && "code" in error ? String((error as { code?: unknown }).code) : undefined; }
202
+ function snapshot(path: string): LockSnapshot { const bytes = readFileSync(path, "utf8"); const s = statSync(path); return { bytes, dev:s.dev, ino:s.ino, mtimeMs:s.mtimeMs, size:s.size }; }
203
+ function sameSnapshot(a: LockSnapshot,b: LockSnapshot): boolean { return a.bytes===b.bytes&&a.dev===b.dev&&a.ino===b.ino&&a.mtimeMs===b.mtimeMs&&a.size===b.size; }
204
+ function sameFd(a: LockSnapshot,b: ReturnType<typeof fstatSync>): boolean { return a.dev===b.dev&&a.ino===b.ino&&a.mtimeMs===b.mtimeMs&&a.size===b.size; }
205
+ export function createOAuthFileLock(options: OAuthFileLockOptions): { acquire(): Promise<OAuthFileLockGuard> } {
206
+ const wait=options.waitTimeoutMs??5000, stale=options.staleAfterMs??120000, min=options.pollMinMs??25,max=options.pollMaxMs??100,sleep=options.sleep??(ms=>Bun.sleep(ms)),now=options.now??Date.now,random=options.random??Math.random,write=options.writeMetadata??((fd,b)=>writeFileSync(fd,b,"utf8"));
207
+ if(wait<0||stale<=0||min<0||max<min) throw new OAuthFileLockError("Invalid OAuth file-lock timing options");
208
+ return { async acquire() { hardenConfigDir(); recordOwnedConfigPath(getConfigDir(),options.path); if(!existsSync(getConfigDir())) mkdirSync(getConfigDir(),{recursive:true,mode:0o700}); const ownerId=randomUUID(),started=now(); for(;;){ let fd:number|undefined; try { fd=openSync(options.path,"wx",0o600); const bytes=`${JSON.stringify({version:1,ownerId,pid:process.pid,createdAt:now()})}\n`; write(fd,bytes); const fs=fstatSync(fd); closeSync(fd); fd=undefined; const owned=snapshot(options.path); if(owned.bytes!==bytes||!sameFd(owned,fs)) throw new OAuthFileLockError("OAuth lock changed during creation"); let released=false; return {ownerId,release(){if(released)return;released=true;try{const a=snapshot(options.path);if(!sameSnapshot(owned,a))return;options.beforeReleaseUnlink?.();const b=snapshot(options.path);if(sameSnapshot(owned,b))unlinkSync(options.path);}catch(e){if(errorCode(e)!=="ENOENT")console.warn(`[oauth] lock release failed: ${e instanceof Error?e.message:String(e)}`);}}}; } catch(e) { if(fd!==undefined){let fs;try{fs=fstatSync(fd);}catch{}try{closeSync(fd);}catch{}if(fs)try{const a=snapshot(options.path);if(sameFd(a,fs)){options.beforeFailedCreateUnlink?.();const b=snapshot(options.path);if(sameSnapshot(a,b)&&sameFd(b,fs))unlinkSync(options.path);}}catch{}} if(errorCode(e)!=="EEXIST")throw e instanceof OAuthFileLockError?e:new OAuthFileLockError("Could not create OAuth file lock",{cause:e}); }
209
+ try{const a=snapshot(options.path);let created=a.mtimeMs;try{const p=JSON.parse(a.bytes);if(typeof p.createdAt==="number")created=Math.max(created,p.createdAt);}catch{}if(now()-created>stale){options.beforeStaleUnlink?.();const b=snapshot(options.path);if(sameSnapshot(a,b))unlinkSync(options.path);continue;}}catch(e){if(errorCode(e)==="ENOENT")continue;throw new OAuthFileLockError("Could not inspect OAuth file lock",{cause:e});} const elapsed=now()-started;if(elapsed>=wait)throw new OAuthFileLockError(`Timed out after ${wait}ms waiting for OAuth file lock`);await sleep(Math.min(wait-elapsed,min+Math.floor(random()*(max-min+1)))); } } };
210
+ }
211
+ /** Wait long enough for slow IdP refreshes (e.g. Cursor 15s × 3 attempts) before timing out. */
212
+ export const OAUTH_REFRESH_LOCK_WAIT_MS = 60_000;
213
+
214
+ export function createOAuthRefreshIntentLock(provider:string,accountId:string,overrides:Partial<OAuthFileLockOptions>={}) {
215
+ return createOAuthFileLock({
216
+ path: getAuthRefreshIntentLockPath(provider, accountId),
217
+ staleAfterMs: 120000,
218
+ waitTimeoutMs: OAUTH_REFRESH_LOCK_WAIT_MS,
219
+ ...overrides,
220
+ });
221
+ }
222
+
223
+ /**
224
+ * One-time downgrade safety net: the first time we persist the NEW shape over a file that
225
+ * still contains legacy single-credential entries, keep a pristine copy. An older opencodex
226
+ * would silently drop the new shape (normalizeCredential -> null) and then persist an empty
227
+ * store, destroying refresh tokens; the backup makes that recoverable.
228
+ */
229
+ function backupLegacyOnce(): void {
230
+ const path = getAuthStorePath();
231
+ const backup = `${path}.pre-multiauth`;
232
+ if (!existsSync(path) || existsSync(backup)) return;
233
+ try {
234
+ copyFileSync(path, backup);
235
+ try { chmodSync(backup, 0o600); } catch { /* best-effort */ }
236
+ } catch { /* best-effort */ }
237
+ }
238
+
239
+ function isCredentialSource(value: unknown): value is OAuthCredentialSource {
240
+ return value === "oauth" || value === "local-cli" || value === "credential-file" || value === "environment" || value === "manual";
241
+ }
242
+
243
+ function normalizeCredential(cred: unknown): OAuthCredentials | null {
244
+ if (!cred || typeof cred !== "object") return null;
245
+ const candidate = cred as Partial<OAuthCredentials>;
246
+ if (typeof candidate.access !== "string" || typeof candidate.refresh !== "string" || typeof candidate.expires !== "number") {
247
+ return null;
248
+ }
249
+ const normalized: OAuthCredentials = {
250
+ access: candidate.access,
251
+ refresh: candidate.refresh,
252
+ expires: candidate.expires,
253
+ };
254
+ if (typeof candidate.email === "string" && candidate.email.length > 0) normalized.email = candidate.email;
255
+ if (typeof candidate.accountId === "string" && candidate.accountId.length > 0) normalized.accountId = candidate.accountId;
256
+ if (isCredentialSource(candidate.source)) normalized.source = candidate.source;
257
+ if (typeof candidate.projectId === "string" && candidate.projectId.length > 0) normalized.projectId = candidate.projectId;
258
+ if (typeof candidate.apiBaseUrl === "string" && candidate.apiBaseUrl.length > 0) {
259
+ // Persist only allowlisted Copilot origins; drop anything else so auth.json cannot
260
+ // become an SSRF springboard across reloads.
261
+ const validated = validateCopilotApiBaseUrl(candidate.apiBaseUrl);
262
+ if (validated) normalized.apiBaseUrl = validated;
263
+ }
264
+ if (candidate.kiro && typeof candidate.kiro === "object") {
265
+ const kiro = candidate.kiro;
266
+ const clean = (value: unknown, max: number): string | undefined => {
267
+ if (typeof value !== "string") return undefined;
268
+ const trimmed = value.trim();
269
+ return trimmed && trimmed.length <= max && !/[\x00-\x1f\x7f]/.test(trimmed) ? trimmed : undefined;
270
+ };
271
+ const profileArn = clean(kiro.profileArn, 1024);
272
+ const ssoRegion = clean(kiro.ssoRegion, 64);
273
+ const apiRegion = clean(kiro.apiRegion, 64);
274
+ const clientId = clean(kiro.clientId, 4096);
275
+ const clientSecret = clean(kiro.clientSecret, 4096);
276
+ if (profileArn || ssoRegion || apiRegion || clientId || clientSecret) {
277
+ normalized.kiro = {
278
+ ...(profileArn ? { profileArn } : {}),
279
+ ...(ssoRegion ? { ssoRegion } : {}),
280
+ ...(apiRegion ? { apiRegion } : {}),
281
+ ...(clientId ? { clientId } : {}),
282
+ ...(clientSecret ? { clientSecret } : {}),
283
+ };
284
+ }
285
+ }
286
+ return normalized;
287
+ }
288
+
289
+ /**
290
+ * Stable collision-resistant account id. MUST be deterministic for a given credential:
291
+ * legacy single-credential stores are re-normalized on EVERY load without being persisted,
292
+ * so a time-salted id would differ between two loads (getAccountSet vs
293
+ * getAccountCredential), surfacing as a spurious OAuthLoginRequiredError and making
294
+ * refresh persists silently miss the account (rotated refresh token lost).
295
+ *
296
+ * Keep 128 bits of SHA-256 rather than the historical 32-bit prefix. Existing persisted
297
+ * account ids are read as-is; only newly-derived ids and legacy normalization use this width.
298
+ */
299
+ function newAccountId(cred: OAuthCredentials): string {
300
+ const identity = cred.accountId ?? cred.email ?? cred.refresh;
301
+ return createHash("sha256").update(identity).digest("hex").slice(0, 32);
302
+ }
303
+
304
+ /** Allocate a persisted slot id without reusing any existing account's ownership key. */
305
+ function distinctAccountId(cred: OAuthCredentials, accounts: readonly ProviderAccount[]): string {
306
+ const base = newAccountId(cred);
307
+ const occupied = new Set(accounts.map(account => account.id));
308
+ if (!occupied.has(base)) return base;
309
+ for (let suffix = 1; ; suffix += 1) {
310
+ const candidate = `${base}-${suffix}`;
311
+ if (!occupied.has(candidate)) return candidate;
312
+ }
313
+ }
314
+
315
+ function normalizeAccount(value: unknown): ProviderAccount | null {
316
+ if (!value || typeof value !== "object") return null;
317
+ const candidate = value as Partial<ProviderAccount>;
318
+ if (typeof candidate.id !== "string" || candidate.id.length === 0) return null;
319
+ const credential = normalizeCredential(candidate.credential);
320
+ if (!credential) return null;
321
+ const account: ProviderAccount = { id: candidate.id, credential };
322
+ if (typeof candidate.alias === "string" && candidate.alias.trim()) account.alias = candidate.alias.trim();
323
+ if (candidate.needsReauth === true) account.needsReauth = true;
324
+ if (typeof candidate.addedAt === "number") account.addedAt = candidate.addedAt;
325
+ return account;
326
+ }
327
+
328
+ function normalizeAccountSet(raw: unknown): { set: ProviderAccountSet | null; wasLegacy: boolean } {
329
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return { set: null, wasLegacy: false };
330
+ const candidate = raw as Partial<ProviderAccountSet>;
331
+ if (Array.isArray(candidate.accounts)) {
332
+ const accounts = candidate.accounts.map(normalizeAccount).filter((a): a is ProviderAccount => a !== null);
333
+ if (accounts.length === 0) return { set: null, wasLegacy: false };
334
+ const active = typeof candidate.activeAccountId === "string" && accounts.some(a => a.id === candidate.activeAccountId)
335
+ ? candidate.activeAccountId
336
+ : accounts[0]!.id;
337
+ return { set: { activeAccountId: active, accounts }, wasLegacy: false };
338
+ }
339
+ // Legacy single-credential value.
340
+ const cred = normalizeCredential(raw);
341
+ if (!cred) return { set: null, wasLegacy: false };
342
+ const id = newAccountId(cred);
343
+ return { set: { activeAccountId: id, accounts: [{ id, credential: cred }] }, wasLegacy: true };
344
+ }
345
+
346
+ function normalizeAuthStore(raw: unknown): { store: AuthStore; hadLegacy: boolean } {
347
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return { store: {}, hadLegacy: false };
348
+ const normalized: AuthStore = {};
349
+ let hadLegacy = false;
350
+ for (const [provider, value] of Object.entries(raw)) {
351
+ const { set, wasLegacy } = normalizeAccountSet(value);
352
+ if (set) normalized[provider] = set;
353
+ if (wasLegacy) hadLegacy = true;
354
+ }
355
+ return { store: normalized, hadLegacy };
356
+ }
357
+
358
+ /**
359
+ * In-process write serialization: every mutation runs load-modify-persist under this queue so
360
+ * a guardian refresh persisting a non-active account cannot roll back a concurrent
361
+ * active-account switch (lost update). Cross-process races are accepted (single proxy).
362
+ */
363
+ const OAUTH_MUTATION_WAIT_MS = 30_000;
364
+ const oauthMutationEncoder = new TextEncoder();
365
+ let mutationTail: Promise<void> = Promise.resolve();
366
+ let pendingMutations = 0;
367
+ let mutationCurrentBytes = 0;
368
+ let mutationHighWaterBytes = 0;
369
+ interface QueuedOAuthMutation {
370
+ started: boolean;
371
+ settled: boolean;
372
+ timeout?: ReturnType<typeof setTimeout>;
373
+ run(): Promise<void>;
374
+ }
375
+ const mutationWaiters: QueuedOAuthMutation[] = [];
376
+ let mutationRunning = false;
377
+
378
+ export class OAuthMutationBusyError extends Error {
379
+ readonly code = "oauth_mutation_busy";
380
+ constructor(message = "OAuth mutation queue is busy") {
381
+ super(message);
382
+ this.name = "OAuthMutationBusyError";
383
+ }
384
+ }
385
+
386
+ export function oauthMutationTailSnapshot(): { currentBytes: number; highWaterBytes: number; active: number } {
387
+ return { currentBytes: mutationCurrentBytes, highWaterBytes: mutationHighWaterBytes, active: pendingMutations };
388
+ }
389
+
390
+ function retainedClosureStringBytes(values: readonly unknown[]): number {
391
+ const visit = (value: unknown): number => {
392
+ if (typeof value === "string") return oauthMutationEncoder.encode(value).byteLength;
393
+ if (Array.isArray(value)) return value.reduce((sum, entry) => sum + visit(entry), 0);
394
+ if (value && typeof value === "object") {
395
+ return Object.entries(value as Record<string, unknown>)
396
+ .reduce((sum, [key, entry]) => sum + oauthMutationEncoder.encode(key).byteLength + visit(entry), 0);
397
+ }
398
+ return 0;
399
+ };
400
+ return values.reduce<number>((sum, value) => sum + visit(value), 0);
401
+ }
402
+
403
+ function drainOAuthMutations(): void {
404
+ if (mutationRunning) return;
405
+ const next = mutationWaiters.shift();
406
+ if (!next) return;
407
+ if (next.settled) {
408
+ drainOAuthMutations();
409
+ return;
410
+ }
411
+ next.started = true;
412
+ if (next.timeout) clearTimeout(next.timeout);
413
+ mutationRunning = true;
414
+ mutationTail = next.run().finally(() => {
415
+ mutationRunning = false;
416
+ drainOAuthMutations();
417
+ });
418
+ }
419
+
420
+ function serializeMutation<T>(work: () => Promise<T>, retainedValues: readonly unknown[], waitMs = OAUTH_MUTATION_WAIT_MS): Promise<T> {
421
+ if (pendingMutations >= MAX_PENDING_OAUTH_MUTATIONS) return Promise.reject(new OAuthMutationBusyError());
422
+ pendingMutations += 1;
423
+ const retainedBytes = retainedClosureStringBytes(retainedValues);
424
+ mutationCurrentBytes += retainedBytes;
425
+ mutationHighWaterBytes = Math.max(mutationHighWaterBytes, mutationCurrentBytes);
426
+
427
+ let resolveResult!: (value: T | PromiseLike<T>) => void;
428
+ let rejectResult!: (reason?: unknown) => void;
429
+ const result = new Promise<T>((resolve, reject) => {
430
+ resolveResult = resolve;
431
+ rejectResult = reject;
432
+ });
433
+ const entry: QueuedOAuthMutation = {
434
+ started: false,
435
+ settled: false,
436
+ async run() {
437
+ try {
438
+ resolveResult(await work());
439
+ } catch (error) {
440
+ rejectResult(error);
441
+ } finally {
442
+ release();
443
+ }
444
+ },
445
+ };
446
+ const release = () => {
447
+ if (entry.settled) return;
448
+ entry.settled = true;
449
+ pendingMutations -= 1;
450
+ mutationCurrentBytes = Math.max(0, mutationCurrentBytes - retainedBytes);
451
+ };
452
+ entry.timeout = setTimeout(() => {
453
+ if (entry.started || entry.settled) return;
454
+ const index = mutationWaiters.indexOf(entry);
455
+ if (index >= 0) mutationWaiters.splice(index, 1);
456
+ release();
457
+ rejectResult(new OAuthMutationBusyError("OAuth mutation queue wait timed out"));
458
+ }, waitMs);
459
+ // Only unref the long default wait. Short waitMs (tests) must stay ref'd:
460
+ // on Windows Bun under `bun test --isolate`, an unref'd timer can fail to
461
+ // fire while the head mutation holds an unresolved Promise, hanging the
462
+ // waiter forever (#827 / full admission queue hang on windows-latest).
463
+ if (waitMs >= OAUTH_MUTATION_WAIT_MS) entry.timeout.unref?.();
464
+ mutationWaiters.push(entry);
465
+ drainOAuthMutations();
466
+ return result;
467
+ }
468
+ export function mutateStore<T>(fn:(store:AuthStore)=>T|Promise<T>, retainedValues: readonly unknown[] = [], options?: { waitMs?: number; assertBeforePersist?: () => void }):Promise<T>{return serializeMutation(async()=>{const guard=await createOAuthFileLock({path:getAuthStoreLockPath(),staleAfterMs:30000}).acquire();try{
469
+ const { store, hadLegacy } = loadAuthStoreInternal();
470
+ if (hadLegacy) backupLegacyOnce();
471
+ const result = await fn(store);
472
+ options?.assertBeforePersist?.();
473
+ persist(store);
474
+ return result;
475
+ }finally{guard.release();}}, retainedValues, options?.waitMs);
476
+ }
477
+
478
+ /** The ACTIVE account's credential for a provider (what requests should use). */
479
+ export function getCredential(provider: string): OAuthCredentials | null {
480
+ const set = loadAuthStore()[provider];
481
+ if (!set) return null;
482
+ return set.accounts.find(a => a.id === set.activeAccountId)?.credential ?? null;
483
+ }
484
+
485
+ /**
486
+ * Persist a credential as the ACTIVE account. Identity-matching (accountId ?? email) upserts
487
+ * the same human's slot; a new identity appends a new account. Credentials without identity
488
+ * (rotating refresh tokens would fabricate duplicates) and single-slot providers replace the
489
+ * active slot / whole set instead. An explicit add-account login can preserve the legacy slot;
490
+ * an identity-less credential then gets its deterministic refresh-derived account id.
491
+ */
492
+ export async function saveCredential(
493
+ provider: string,
494
+ cred: OAuthCredentials,
495
+ opts: { preserveIdentityless?: boolean; assertBeforePersist?: () => void } = {},
496
+ ): Promise<void> {
497
+ const safe = normalizeCredential(cred);
498
+ if (!safe) return;
499
+ await mutateStore(store => {
500
+ const set = store[provider];
501
+ const identity = safe.accountId ?? safe.email;
502
+ if (!set || SINGLE_SLOT_PROVIDERS.has(provider)) {
503
+ const id = newAccountId(safe);
504
+ store[provider] = { activeAccountId: id, accounts: [{ id, credential: safe, addedAt: Date.now() }] };
505
+ return;
506
+ }
507
+ if (identity) {
508
+ const existing = set.accounts.find(a => (a.credential.accountId ?? a.credential.email) === identity);
509
+ if (existing) {
510
+ existing.credential = safe;
511
+ delete existing.needsReauth;
512
+ set.activeAccountId = existing.id;
513
+ return;
514
+ }
515
+ // Legacy migration: a pre-identity row (no accountId/email) for this provider is the
516
+ // SAME human re-logging in after the identity extraction shipped — upgrading the
517
+ // active identity-less row in place prevents a stale duplicate that stays selectable
518
+ // and would re-refresh into a second row with the same identity.
519
+ const active = set.accounts.find(a => a.id === set.activeAccountId);
520
+ if (!opts.preserveIdentityless && active && active.credential.accountId === undefined && active.credential.email === undefined) {
521
+ active.credential = safe;
522
+ delete active.needsReauth;
523
+ return;
524
+ }
525
+ const id = distinctAccountId(safe, set.accounts);
526
+ set.accounts.push({ id, credential: safe, addedAt: Date.now() });
527
+ set.activeAccountId = id;
528
+ return;
529
+ }
530
+ if (opts.preserveIdentityless) {
531
+ const id = distinctAccountId(safe, set.accounts);
532
+ set.accounts.push({ id, credential: safe, addedAt: Date.now() });
533
+ set.activeAccountId = id;
534
+ return;
535
+ }
536
+ // No identity during a normal login: replace the active slot in place.
537
+ const active = set.accounts.find(a => a.id === set.activeAccountId);
538
+ if (active) {
539
+ active.credential = safe;
540
+ delete active.needsReauth;
541
+ } else {
542
+ const id = distinctAccountId(safe, set.accounts);
543
+ set.accounts.push({ id, credential: safe, addedAt: Date.now() });
544
+ set.activeAccountId = id;
545
+ }
546
+ }, [provider, safe], { assertBeforePersist: opts.assertBeforePersist });
547
+ }
548
+
549
+ /**
550
+ * Atomically insert or replace an identity-bearing credential and report the disposition.
551
+ * Importers use this instead of a read-then-save pair so duplicate detection and persistence
552
+ * happen inside the existing serialized temp-then-rename store mutation.
553
+ */
554
+ export async function upsertCredentialByIdentity(
555
+ provider: string,
556
+ cred: OAuthCredentials,
557
+ ): Promise<"inserted" | "updated"> {
558
+ const safe = normalizeCredential(cred);
559
+ if (!safe || (!safe.accountId && !safe.email)) {
560
+ throw new Error("Refusing to persist OAuth credential without verified identity");
561
+ }
562
+ return await mutateStore(store => {
563
+ const set = store[provider];
564
+ const matchesEmailOnly = (account: ProviderAccount): boolean => {
565
+ if (account.credential.accountId) return false;
566
+ return Boolean(
567
+ safe.email
568
+ && account.credential.email
569
+ && account.credential.email.toLowerCase() === safe.email.toLowerCase(),
570
+ );
571
+ };
572
+ const existing = safe.accountId
573
+ ? set?.accounts.find(account => account.credential.accountId === safe.accountId)
574
+ ?? set?.accounts.find(matchesEmailOnly)
575
+ : set?.accounts.find(matchesEmailOnly);
576
+ if (existing && set) {
577
+ existing.credential = safe;
578
+ delete existing.needsReauth;
579
+ set.activeAccountId ??= existing.id;
580
+ return "updated";
581
+ }
582
+ const id = newAccountId(safe);
583
+ const account: ProviderAccount = { id, credential: safe, addedAt: Date.now() };
584
+ if (set) {
585
+ set.accounts.push(account);
586
+ set.activeAccountId ??= id;
587
+ } else {
588
+ store[provider] = { activeAccountId: id, accounts: [account] };
589
+ }
590
+ return "inserted";
591
+ }, [provider, safe]);
592
+ }
593
+
594
+ /** Remove the ACTIVE account; remaining accounts promote the first one. */
595
+ export async function removeCredential(provider: string): Promise<void> {
596
+ await mutateStore(store => {
597
+ const set = store[provider];
598
+ if (!set) return;
599
+ set.accounts = set.accounts.filter(a => a.id !== set.activeAccountId);
600
+ if (set.accounts.length === 0) {
601
+ delete store[provider];
602
+ return;
603
+ }
604
+ set.activeAccountId = set.accounts[0]!.id;
605
+ }, [provider]);
606
+ }
607
+
608
+ // ---------------------------------------------------------------------------
609
+ // Multi-account API
610
+ // ---------------------------------------------------------------------------
611
+
612
+ export function getAccountSet(provider: string): ProviderAccountSet | null {
613
+ return loadAuthStore()[provider] ?? null;
614
+ }
615
+
616
+ export function listAccounts(provider: string): ProviderAccount[] {
617
+ return loadAuthStore()[provider]?.accounts ?? [];
618
+ }
619
+
620
+ export function listLiveOAuthAccountKeys(
621
+ providerNames: ReadonlySet<string>,
622
+ ): ReadonlySet<string> {
623
+ const keys = new Set<string>();
624
+ for (const [provider, accountSet] of Object.entries(loadAuthStore())) {
625
+ if (!providerNames.has(provider)) continue;
626
+ for (const account of accountSet.accounts) keys.add(`${provider}\0${account.id}`);
627
+ }
628
+ return keys;
629
+ }
630
+
631
+ export function getAccountCredential(provider: string, accountId: string): OAuthCredentials | null {
632
+ return loadAuthStore()[provider]?.accounts.find(a => a.id === accountId)?.credential ?? null;
633
+ }
634
+
635
+ /** Persist a refreshed credential for a SPECIFIC account without touching activeAccountId. */
636
+ export async function saveAccountCredential(
637
+ provider: string,
638
+ accountId: string,
639
+ cred: OAuthCredentials,
640
+ opts: { assertBeforePersist?: () => void } = {},
641
+ ): Promise<void> {
642
+ const safe = normalizeCredential(cred);
643
+ if (!safe) return;
644
+ await mutateStore(store => {
645
+ const account = store[provider]?.accounts.find(a => a.id === accountId);
646
+ if (!account) return;
647
+ account.credential = safe;
648
+ delete account.needsReauth;
649
+ }, [provider, accountId, safe], { assertBeforePersist: opts.assertBeforePersist });
650
+ }
651
+
652
+ export async function setActiveAccount(provider: string, accountId: string): Promise<boolean> {
653
+ return await mutateStore(store => {
654
+ const set = store[provider];
655
+ if (!set || !set.accounts.some(a => a.id === accountId)) return false;
656
+ set.activeAccountId = accountId;
657
+ return true;
658
+ }, [provider, accountId]);
659
+ }
660
+
661
+ export async function setAccountAlias(provider: string, accountId: string, alias: string | undefined): Promise<boolean> {
662
+ return await mutateStore(store => {
663
+ const account = store[provider]?.accounts.find(a => a.id === accountId);
664
+ if (!account) return false;
665
+ if (alias) account.alias = alias;
666
+ else delete account.alias;
667
+ return true;
668
+ }, [provider, accountId, alias]);
669
+ }
670
+
671
+ /** Remove one account by id; active removal promotes the first remaining account. */
672
+ export async function removeAccount(provider: string, accountId: string): Promise<boolean> {
673
+ const removed = await mutateStore(store => {
674
+ const set = store[provider];
675
+ if (!set) return false;
676
+ const before = set.accounts.length;
677
+ set.accounts = set.accounts.filter(a => a.id !== accountId);
678
+ if (set.accounts.length === before) return false;
679
+ if (set.accounts.length === 0) {
680
+ delete store[provider];
681
+ return true;
682
+ }
683
+ if (set.activeAccountId === accountId) set.activeAccountId = set.accounts[0]!.id;
684
+ return true;
685
+ }, [provider, accountId]);
686
+ return removed;
687
+ }
688
+
689
+ /** Replace or clear a provider account set (used for transactional Kiro add-account rollback). */
690
+ export async function replaceProviderAccountSet(
691
+ provider: string,
692
+ set: ProviderAccountSet | null,
693
+ ): Promise<void> {
694
+ await mutateStore(store => {
695
+ if (!set || set.accounts.length === 0) {
696
+ delete store[provider];
697
+ return;
698
+ }
699
+ store[provider] = {
700
+ activeAccountId: set.activeAccountId,
701
+ accounts: set.accounts.map(account => ({
702
+ id: account.id,
703
+ credential: { ...account.credential, ...(account.credential.kiro ? { kiro: { ...account.credential.kiro } } : {}) },
704
+ ...(account.alias ? { alias: account.alias } : {}),
705
+ ...(account.needsReauth ? { needsReauth: true } : {}),
706
+ ...(account.addedAt !== undefined ? { addedAt: account.addedAt } : {}),
707
+ })),
708
+ };
709
+ }, [provider, set]);
710
+ }
711
+
712
+ export async function markAccountNeedsReauth(
713
+ provider: string,
714
+ accountId: string,
715
+ needsReauth: boolean,
716
+ writerGeneration = captureConfigGeneration(),
717
+ ): Promise<void> {
718
+ if (writerGeneration < lastReconciledGeneration && !liveOAuthAccountKeys.has(oauthAccountKey(provider, accountId))) return;
719
+ await mutateStore(store => {
720
+ const account = store[provider]?.accounts.find(a => a.id === accountId);
721
+ if (!account) return;
722
+ if (needsReauth) account.needsReauth = true;
723
+ else delete account.needsReauth;
724
+ }, [provider, accountId]);
725
+ }
726
+
727
+ export async function mergeAccountCredential(provider:string,accountId:string,credential:OAuthCredentials,opts:{expectedGeneration?:string;afterPrePersistRead?:()=>void|Promise<void>}={}):Promise<{superseded:false}|{superseded:true;stored:OAuthCredentials}>{const safe=normalizeCredential(credential);if(!safe)throw new Error("Refusing to persist invalid OAuth credential");return await mutateStore(async store=>{await opts.afterPrePersistRead?.();const account=store[provider]?.accounts.find(x=>x.id===accountId);if(!account)throw new Error(`OAuth account disappeared before persist: ${provider}`);if(opts.expectedGeneration!==undefined&&credentialGeneration(account.credential)!==opts.expectedGeneration)return{superseded:true,stored:account.credential};account.credential=safe;delete account.needsReauth;return{superseded:false};},[provider,accountId,safe,opts.expectedGeneration]);}
728
+ export async function markAccountNeedsReauthIfGeneration(provider:string,accountId:string,generation:string,writerGeneration=captureConfigGeneration()):Promise<boolean>{const key=oauthAccountKey(provider,accountId);if(writerGeneration<lastReconciledGeneration&&!liveOAuthAccountKeys.has(key))return false;return await mutateStore(store=>{const account=store[provider]?.accounts.find(x=>x.id===accountId);if(!account?.credential||credentialGeneration(account.credential)!==generation)return false;if(writerGeneration<lastReconciledGeneration&&!liveOAuthAccountKeys.has(key))return false;account.needsReauth=true;return true;},[provider,accountId,generation]);}