@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
@@ -0,0 +1,178 @@
1
+ import { join } from "node:path";
2
+
3
+ import { Database } from "bun:sqlite";
4
+
5
+ import {
6
+ assertStableLockFile,
7
+ hardenStableLockFile,
8
+ openStableLockFile,
9
+ type StableLockFile,
10
+ } from "./native-main-lock-file";
11
+ import { nativeMainOwnerFilesystemSupported } from "./native-main-owner";
12
+ import type { NativeProfileContext } from "./native-profile-store";
13
+ import { NativeProfileError } from "./native-profile-types";
14
+
15
+ export const NATIVE_MAIN_CLAIM_DB = ".opencodex-native-main.claim.sqlite";
16
+
17
+ export interface NativeMainClaimOptions {
18
+ waitMs?: number;
19
+ pollMs?: number;
20
+ hardenPath?: (path: string) => Promise<void>;
21
+ platform?: NodeJS.Platform;
22
+ env?: NodeJS.ProcessEnv;
23
+ }
24
+
25
+ export const NATIVE_MAIN_HARDENED_IDENTITY_MAX_ENTRIES = 32;
26
+ export const hardenedIdentities = new Map<string, string>();
27
+
28
+ export function rememberHardenedIdentity(path: string, identity: string): void {
29
+ hardenedIdentities.delete(path);
30
+ hardenedIdentities.set(path, identity);
31
+ while (hardenedIdentities.size > NATIVE_MAIN_HARDENED_IDENTITY_MAX_ENTRIES) {
32
+ const oldest = hardenedIdentities.keys().next().value;
33
+ if (oldest === undefined) break;
34
+ hardenedIdentities.delete(oldest);
35
+ }
36
+ }
37
+
38
+ export function nativeMainClaimPath(context: NativeProfileContext): string {
39
+ return join(context.codexHome, NATIVE_MAIN_CLAIM_DB);
40
+ }
41
+
42
+ function errorCode(error: unknown): string | undefined {
43
+ return error && typeof error === "object" && "code" in error
44
+ ? String((error as { code?: unknown }).code)
45
+ : undefined;
46
+ }
47
+
48
+ function isBusy(error: unknown): boolean {
49
+ const code = errorCode(error);
50
+ const message = error instanceof Error ? error.message : String(error);
51
+ return code === "SQLITE_BUSY" || code === "SQLITE_LOCKED" || /database (?:is|table is) locked/i.test(message);
52
+ }
53
+
54
+ function busyClaimError(message: string): NativeProfileError {
55
+ return new NativeProfileError("NATIVE_MAIN_CLAIM_BUSY", message, 503, true);
56
+ }
57
+
58
+ function unavailableClaimError(): NativeProfileError {
59
+ return new NativeProfileError(
60
+ "NATIVE_MAIN_CLAIM_UNAVAILABLE",
61
+ "The native-main cross-process claim is unavailable.",
62
+ 503,
63
+ true,
64
+ );
65
+ }
66
+
67
+ function mapClaimSetupError(error: unknown, busyMessage: string): NativeProfileError {
68
+ if (error instanceof NativeProfileError) return error;
69
+ return isBusy(error) ? busyClaimError(busyMessage) : unavailableClaimError();
70
+ }
71
+
72
+ async function openClaimDatabase(
73
+ context: NativeProfileContext,
74
+ options: NativeMainClaimOptions,
75
+ ): Promise<{ database: Database; file: StableLockFile }> {
76
+ const platform = options.platform ?? process.platform;
77
+ if (!nativeMainOwnerFilesystemSupported(context.codexHome, platform, options.env ?? process.env)) {
78
+ throw new NativeProfileError(
79
+ "NATIVE_MAIN_CLAIM_UNAVAILABLE",
80
+ "Native-main claims are unsupported on this CODEX_HOME filesystem.",
81
+ 503,
82
+ true,
83
+ );
84
+ }
85
+ const path = nativeMainClaimPath(context);
86
+ let file: StableLockFile | undefined;
87
+ let database: Database | undefined;
88
+ try {
89
+ file = openStableLockFile(path, platform);
90
+ const identity = `${file.dev}:${file.ino}`;
91
+ if (hardenedIdentities.get(path) !== identity) {
92
+ // The resolved platform is threaded into the DEFAULT hardener, not left to
93
+ // `hardenStableLockFile`'s own `process.platform` read. Otherwise a test
94
+ // that forces `platform` here still exercises the host's branch, and the
95
+ // production default — the thing that actually hardens a coordinator
96
+ // database — stays unproved. An audit deleted this call entirely and 89
97
+ // tests stayed green, because nearly every claim test injects `hardenPath`.
98
+ await (options.hardenPath ?? ((target: string) => hardenStableLockFile(target, platform)))(path);
99
+ assertStableLockFile(path, file);
100
+ rememberHardenedIdentity(path, identity);
101
+ }
102
+ database = new Database(path, { create: true });
103
+ // Journal-mode negotiation itself may need a database lock. Disable
104
+ // SQLite's synchronous busy wait first so a contender never blocks Bun's
105
+ // event loop while the current shared holder is waiting for JS to release.
106
+ database.exec("PRAGMA busy_timeout = 0; PRAGMA locking_mode = NORMAL");
107
+ const mode = database.query<{ journal_mode: string }, []>("PRAGMA journal_mode = DELETE").get()?.journal_mode;
108
+ if (mode?.toLowerCase() !== "delete") throw new Error("native-main claim database is not in rollback-journal mode");
109
+ assertStableLockFile(path, file);
110
+ return { database, file };
111
+ } catch (error) {
112
+ try { database?.close(); } catch { /* mapped below */ }
113
+ try { file?.close(); } catch { /* mapped below */ }
114
+ throw mapClaimSetupError(error, "Native-main credentials are changing.");
115
+ }
116
+ }
117
+
118
+ function releaseClaim(database: Database | undefined, file: StableLockFile | undefined): void {
119
+ try { database?.exec("ROLLBACK"); } catch { /* close still releases the OS-backed claim */ }
120
+ try { database?.close(); } catch { /* operation already completed */ }
121
+ try { file?.close(); } catch { /* operation already completed */ }
122
+ }
123
+
124
+ export async function withNativeMainSharedClaim<T>(
125
+ context: NativeProfileContext,
126
+ operation: () => Promise<T>,
127
+ options: NativeMainClaimOptions = {},
128
+ ): Promise<T> {
129
+ let database: Database | undefined;
130
+ let file: StableLockFile | undefined;
131
+ try {
132
+ ({ database, file } = await openClaimDatabase(context, options));
133
+ database.exec("BEGIN");
134
+ // A deferred BEGIN alone owns no lock. Reading page 1 through sqlite_schema
135
+ // establishes the SHARED lock that an exclusive transition must drain.
136
+ database.query<{ rootpage: number }, []>("SELECT rootpage FROM sqlite_schema LIMIT 1").get();
137
+ assertStableLockFile(nativeMainClaimPath(context), file);
138
+ } catch (error) {
139
+ releaseClaim(database, file);
140
+ throw mapClaimSetupError(error, "Native-main credentials are changing.");
141
+ }
142
+ try {
143
+ return await operation();
144
+ } finally {
145
+ releaseClaim(database, file);
146
+ }
147
+ }
148
+
149
+ export async function withNativeMainExclusiveClaim<T>(
150
+ context: NativeProfileContext,
151
+ operation: () => Promise<T>,
152
+ options: NativeMainClaimOptions = {},
153
+ ): Promise<T> {
154
+ const deadline = Date.now() + Math.max(0, options.waitMs ?? 0);
155
+ const pollMs = Math.max(1, options.pollMs ?? 50);
156
+ for (;;) {
157
+ let database: Database | undefined;
158
+ let file: StableLockFile | undefined;
159
+ try {
160
+ ({ database, file } = await openClaimDatabase(context, options));
161
+ database.exec("BEGIN EXCLUSIVE");
162
+ assertStableLockFile(nativeMainClaimPath(context), file);
163
+ } catch (error) {
164
+ releaseClaim(database, file);
165
+ const mapped = mapClaimSetupError(error, "Native-main credentials are in use.");
166
+ if (mapped.code === "NATIVE_MAIN_CLAIM_BUSY" && Date.now() < deadline) {
167
+ await Bun.sleep(Math.min(pollMs, Math.max(1, deadline - Date.now())));
168
+ continue;
169
+ }
170
+ throw mapped;
171
+ }
172
+ try {
173
+ return await operation();
174
+ } finally {
175
+ releaseClaim(database, file);
176
+ }
177
+ }
178
+ }
@@ -0,0 +1,162 @@
1
+ import {
2
+ chmodSync,
3
+ closeSync,
4
+ constants,
5
+ existsSync,
6
+ fstatSync,
7
+ lstatSync,
8
+ openSync,
9
+ type Stats,
10
+ } from "node:fs";
11
+
12
+ import { hardenSecretPathAsync } from "../lib/windows-secret-acl";
13
+
14
+ export interface StableLockFile {
15
+ readonly fd: number;
16
+ readonly dev: number;
17
+ readonly ino: number;
18
+ close(): void;
19
+ }
20
+
21
+ export class StableLockPathUnsafeError extends Error {
22
+ constructor(message: string) {
23
+ super(message);
24
+ this.name = "StableLockPathUnsafeError";
25
+ }
26
+ }
27
+
28
+ interface StableLockEntry {
29
+ readonly fd: number;
30
+ readonly dev: number;
31
+ readonly ino: number;
32
+ refs: number;
33
+ }
34
+
35
+ // POSIX fcntl locks are process-associated: closing any raw descriptor for an
36
+ // inode can release SQLite locks held through a different descriptor in this
37
+ // process. Keep one side descriptor per canonical lock path and close it only
38
+ // after every SQLite user has closed its connection and released its reference.
39
+ const stableLockEntries = new Map<string, StableLockEntry>();
40
+
41
+ function reference(path: string, entry: StableLockEntry): StableLockFile {
42
+ let closed = false;
43
+ return {
44
+ fd: entry.fd,
45
+ dev: entry.dev,
46
+ ino: entry.ino,
47
+ close() {
48
+ if (closed) return;
49
+ closed = true;
50
+ entry.refs = Math.max(0, entry.refs - 1);
51
+ if (entry.refs !== 0) return;
52
+ if (stableLockEntries.get(path) === entry) stableLockEntries.delete(path);
53
+ closeSync(entry.fd);
54
+ },
55
+ };
56
+ }
57
+
58
+ function assertRegular(stats: Stats, path: string): void {
59
+ if (!stats.isFile() || stats.isSymbolicLink()) {
60
+ throw new StableLockPathUnsafeError(`unsafe SQLite lock path: ${path}`);
61
+ }
62
+ }
63
+
64
+ function errorCode(error: unknown): string | undefined {
65
+ return error && typeof error === "object" && "code" in error
66
+ ? String((error as { code?: unknown }).code)
67
+ : undefined;
68
+ }
69
+
70
+ function matches(handle: StableLockFile, stats: Stats): boolean {
71
+ return handle.dev === stats.dev && handle.ino === stats.ino;
72
+ }
73
+
74
+ export function openStableLockFile(path: string, platform: NodeJS.Platform = process.platform): StableLockFile {
75
+ const retained = stableLockEntries.get(path);
76
+ if (retained) {
77
+ retained.refs += 1;
78
+ const handle = reference(path, retained);
79
+ try {
80
+ assertStableLockFile(path, handle);
81
+ return handle;
82
+ } catch (error) {
83
+ handle.close();
84
+ throw error;
85
+ }
86
+ }
87
+ if (existsSync(path)) assertRegular(lstatSync(path), path);
88
+ const noFollow = platform === "win32" ? 0 : constants.O_NOFOLLOW;
89
+ const fd = openSync(path, constants.O_RDWR | constants.O_CREAT | noFollow, 0o600);
90
+ try {
91
+ const descriptor = fstatSync(fd);
92
+ assertRegular(descriptor, path);
93
+ const entry: StableLockEntry = {
94
+ fd,
95
+ dev: descriptor.dev,
96
+ ino: descriptor.ino,
97
+ refs: 1,
98
+ };
99
+ stableLockEntries.set(path, entry);
100
+ const handle = reference(path, entry);
101
+ assertStableLockFile(path, handle);
102
+ return handle;
103
+ } catch (error) {
104
+ const entry = stableLockEntries.get(path);
105
+ if (entry?.fd === fd) stableLockEntries.delete(path);
106
+ closeSync(fd);
107
+ throw error;
108
+ }
109
+ }
110
+
111
+ export function assertStableLockFile(path: string, handle: StableLockFile): void {
112
+ let pathStats: Stats;
113
+ try {
114
+ pathStats = lstatSync(path);
115
+ } catch (error) {
116
+ if (errorCode(error) === "ENOENT") {
117
+ throw new StableLockPathUnsafeError(`SQLite lock path identity changed: ${path}`);
118
+ }
119
+ throw error;
120
+ }
121
+ assertRegular(pathStats, path);
122
+ if (!matches(handle, pathStats)) {
123
+ throw new StableLockPathUnsafeError(`SQLite lock path identity changed: ${path}`);
124
+ }
125
+ }
126
+
127
+ /**
128
+ * Harden the stable lock file: chmod everywhere, per-user NTFS ACLs on Windows.
129
+ *
130
+ * The platform is a parameter rather than a direct `process.platform` read so the
131
+ * Windows branch is reachable from a test. It was not, and an audit deleted the
132
+ * whole ACL delegation without a single test noticing — the primitive underneath
133
+ * had 73 tests while the call edge that activates it had none. That is the same
134
+ * absence-as-guarantee defect one layer up: proving a mechanism works says nothing
135
+ * about whether production still calls it.
136
+ *
137
+ * `required: true` is deliberate: a failure to apply the ACL must reject rather
138
+ * than leave a coordinator database readable by other accounts.
139
+ */
140
+ export async function hardenStableLockFile(
141
+ path: string,
142
+ platform: NodeJS.Platform = process.platform,
143
+ options: { retryTimedOutOnce?: boolean } = {},
144
+ ): Promise<void> {
145
+ if (platform === "win32") {
146
+ // Best-effort here: POSIX modes are not authoritative on NTFS, and the
147
+ // required ACL hardening below is what actually decides.
148
+ try { chmodSync(path, 0o600); } catch { /* ACL below is authoritative. */ }
149
+ await hardenSecretPathAsync(path, {
150
+ required: true,
151
+ retryTimedOutOnce: options.retryTimedOutOnce,
152
+ });
153
+ return;
154
+ }
155
+ // On POSIX the mode IS the mechanism, so a failure may not be swallowed.
156
+ //
157
+ // The previous unconditional catch was a real fail-open: a coordinator
158
+ // database that already existed with permissive bits stayed permissive, and
159
+ // the caller was told the lock file had been hardened. Creation mode 0600 does
160
+ // not repair an existing file, and there is no ACL fallback outside Windows.
161
+ chmodSync(path, 0o600);
162
+ }
@@ -0,0 +1,329 @@
1
+ import { join } from "node:path";
2
+
3
+ import { Database } from "bun:sqlite";
4
+
5
+ import {
6
+ assertStableLockFile,
7
+ hardenStableLockFile,
8
+ openStableLockFile,
9
+ type StableLockFile,
10
+ } from "./native-main-lock-file";
11
+ import type { NativeProfileContext } from "./native-profile-store";
12
+ import { NativeProfileError } from "./native-profile-types";
13
+
14
+ export const NATIVE_MAIN_OWNER_RETRY_MS = 250;
15
+ export const NATIVE_MAIN_OWNER_DB = ".opencodex-native-main.owner.sqlite";
16
+
17
+ export type NativeMainOwnerUnavailableReason = "lock-unavailable" | "unsupported-filesystem";
18
+
19
+ export type NativeMainOwnerSnapshot =
20
+ | { status: "acquiring"; homeId: string }
21
+ | { status: "contended"; homeId: string }
22
+ | { status: "held"; homeId: string }
23
+ | { status: "unavailable"; homeId: string; reason: NativeMainOwnerUnavailableReason }
24
+ | { status: "closing"; homeId: string }
25
+ | { status: "released"; homeId: string };
26
+
27
+ export interface NativeMainOwnerOptions {
28
+ retryMs?: number;
29
+ hardenPath?: (path: string) => Promise<void>;
30
+ platform?: NodeJS.Platform;
31
+ env?: NodeJS.ProcessEnv;
32
+ }
33
+
34
+ export interface NativeMainOwnerReference {
35
+ readonly homeId: string;
36
+ snapshot(): NativeMainOwnerSnapshot;
37
+ subscribe(listener: (snapshot: NativeMainOwnerSnapshot) => void): () => void;
38
+ release(): Promise<void>;
39
+ }
40
+
41
+ interface OwnerEntry {
42
+ readonly context: NativeProfileContext;
43
+ readonly lockPath: string;
44
+ readonly options: Required<Pick<NativeMainOwnerOptions, "retryMs" | "platform">>
45
+ & Pick<NativeMainOwnerOptions, "env" | "hardenPath">;
46
+ refs: number;
47
+ generation: number;
48
+ snapshot: NativeMainOwnerSnapshot;
49
+ listeners: Set<(snapshot: NativeMainOwnerSnapshot) => void>;
50
+ database?: Database;
51
+ file?: StableLockFile;
52
+ prepared: boolean;
53
+ timer?: ReturnType<typeof setTimeout>;
54
+ drive?: Promise<void>;
55
+ activeOperations: Set<Promise<unknown>>;
56
+ aclTimeoutRetryUsed: boolean;
57
+ closing: boolean;
58
+ }
59
+
60
+ const entries = new Map<string, OwnerEntry>();
61
+ const detachedEntries = new Set<OwnerEntry>();
62
+ let exitHookInstalled = false;
63
+
64
+ function errorCode(error: unknown): string | undefined {
65
+ return error && typeof error === "object" && "code" in error
66
+ ? String((error as { code?: unknown }).code)
67
+ : undefined;
68
+ }
69
+
70
+ function isBusy(error: unknown): boolean {
71
+ const code = errorCode(error);
72
+ const message = error instanceof Error ? error.message : String(error);
73
+ return code === "SQLITE_BUSY" || code === "SQLITE_LOCKED" || /database (?:is|table is) locked/i.test(message);
74
+ }
75
+
76
+ export function nativeMainOwnerFilesystemSupported(
77
+ codexHome: string,
78
+ platform: NodeJS.Platform = process.platform,
79
+ env: NodeJS.ProcessEnv = process.env,
80
+ ): boolean {
81
+ if (platform === "win32") {
82
+ const normalized = codexHome.replaceAll("/", "\\");
83
+ if (normalized.startsWith("\\\\?\\UNC\\")) return false;
84
+ if (normalized.startsWith("\\\\") && !normalized.startsWith("\\\\?\\")) return false;
85
+ }
86
+ if (
87
+ platform === "linux"
88
+ && (env.WSL_INTEROP !== undefined || env.WSL_DISTRO_NAME !== undefined)
89
+ && /^\/mnt\/[a-z](?:\/|$)/i.test(codexHome)
90
+ ) return false;
91
+ return true;
92
+ }
93
+
94
+ function publish(entry: OwnerEntry, snapshot: NativeMainOwnerSnapshot): void {
95
+ entry.snapshot = snapshot;
96
+ for (const listener of entry.listeners) {
97
+ try { listener({ ...snapshot }); } catch { /* an observer cannot forfeit process ownership */ }
98
+ }
99
+ }
100
+
101
+ function closeDatabase(entry: OwnerEntry): void {
102
+ const database = entry.database;
103
+ entry.database = undefined;
104
+ if (!database) return;
105
+ try { database.exec("ROLLBACK"); } catch { /* close still releases the OS-backed lock */ }
106
+ try { database.close(); } catch { /* the process exit path is best-effort */ }
107
+ }
108
+
109
+ function closeEntry(entry: OwnerEntry): void {
110
+ closeDatabase(entry);
111
+ try { entry.file?.close(); } catch { /* the process exit path is best-effort */ }
112
+ entry.file = undefined;
113
+ }
114
+
115
+ function installExitHook(): void {
116
+ if (exitHookInstalled) return;
117
+ exitHookInstalled = true;
118
+ process.once("exit", () => {
119
+ for (const entry of entries.values()) {
120
+ if (entry.timer) clearTimeout(entry.timer);
121
+ closeEntry(entry);
122
+ }
123
+ for (const entry of detachedEntries) closeEntry(entry);
124
+ entries.clear();
125
+ detachedEntries.clear();
126
+ });
127
+ }
128
+
129
+ function scheduleRetry(entry: OwnerEntry, generation: number): void {
130
+ if (entry.closing || entry.refs === 0 || entry.timer) return;
131
+ entry.timer = setTimeout(() => {
132
+ entry.timer = undefined;
133
+ if (entry.generation !== generation || entry.closing || entry.refs === 0) return;
134
+ entry.drive = drive(entry, generation).finally(() => {
135
+ if (entry.generation === generation) entry.drive = undefined;
136
+ });
137
+ }, entry.options.retryMs);
138
+ entry.timer.unref?.();
139
+ }
140
+
141
+ async function prepareOwnerDatabase(entry: OwnerEntry): Promise<void> {
142
+ if (entry.prepared) return;
143
+ let file: StableLockFile | undefined;
144
+ let database: Database | undefined;
145
+ try {
146
+ file = openStableLockFile(entry.lockPath, entry.options.platform);
147
+ database = new Database(entry.lockPath, { create: true });
148
+ // The resolved platform is threaded into the DEFAULT hardener for the same
149
+ // reason the claim path does it: otherwise a test that forces `platform`
150
+ // here still exercises the host's branch, and the production default — the
151
+ // thing that actually hardens the owner's lock file — stays unproved. An
152
+ // audit replaced this fallback with a no-op and 91 tests stayed green.
153
+ if (entry.options.hardenPath) {
154
+ await entry.options.hardenPath(entry.lockPath);
155
+ } else {
156
+ await hardenStableLockFile(entry.lockPath, entry.options.platform, {
157
+ retryTimedOutOnce: entry.aclTimeoutRetryUsed,
158
+ });
159
+ }
160
+ assertStableLockFile(entry.lockPath, file);
161
+ entry.file = file;
162
+ file = undefined;
163
+ } catch (error) {
164
+ try { file?.close(); } catch { /* mapped by caller */ }
165
+ throw error;
166
+ } finally {
167
+ try { database?.close(); } catch { /* the caller publishes the hard failure */ }
168
+ }
169
+ entry.prepared = true;
170
+ }
171
+
172
+ async function drive(entry: OwnerEntry, generation: number): Promise<void> {
173
+ if (entry.closing || entry.refs === 0 || entry.generation !== generation || entry.database) return;
174
+ if (!nativeMainOwnerFilesystemSupported(
175
+ entry.context.codexHome,
176
+ entry.options.platform,
177
+ entry.options.env ?? process.env,
178
+ )) {
179
+ publish(entry, { status: "unavailable", homeId: entry.context.homeId, reason: "unsupported-filesystem" });
180
+ return;
181
+ }
182
+ let candidate: Database | undefined;
183
+ try {
184
+ await prepareOwnerDatabase(entry);
185
+ if (entry.closing || entry.refs === 0 || entry.generation !== generation) return;
186
+ candidate = new Database(entry.lockPath, { create: true });
187
+ candidate.exec("PRAGMA locking_mode = NORMAL; PRAGMA busy_timeout = 0; BEGIN IMMEDIATE");
188
+ assertStableLockFile(entry.lockPath, entry.file!);
189
+ if (entry.closing || entry.refs === 0 || entry.generation !== generation) {
190
+ try { candidate.exec("ROLLBACK"); } catch { /* close below */ }
191
+ candidate.close();
192
+ return;
193
+ }
194
+ entry.database = candidate;
195
+ candidate = undefined;
196
+ publish(entry, { status: "held", homeId: entry.context.homeId });
197
+ } catch (error) {
198
+ try { candidate?.close(); } catch { /* acquisition already failed */ }
199
+ if (entry.closing || entry.refs === 0 || entry.generation !== generation) return;
200
+ if (isBusy(error)) {
201
+ publish(entry, { status: "contended", homeId: entry.context.homeId });
202
+ scheduleRetry(entry, generation);
203
+ return;
204
+ }
205
+ if (errorCode(error) === "ETIMEDOUT" && !entry.aclTimeoutRetryUsed) {
206
+ entry.aclTimeoutRetryUsed = true;
207
+ // A timeout is neither ownership contention nor a permanent ACL denial.
208
+ // Stay fail-closed in `acquiring` and spend exactly one fresh-budget retry.
209
+ scheduleRetry(entry, generation);
210
+ return;
211
+ }
212
+ publish(entry, { status: "unavailable", homeId: entry.context.homeId, reason: "lock-unavailable" });
213
+ }
214
+ }
215
+
216
+ function entryFor(context: NativeProfileContext, options: NativeMainOwnerOptions): OwnerEntry {
217
+ const existing = entries.get(context.homeId);
218
+ if (existing) {
219
+ if (existing.context.codexHome !== context.codexHome) {
220
+ throw new NativeProfileError("CODEX_HOME_UNAVAILABLE", "The canonical native-main home changed unexpectedly.", 409);
221
+ }
222
+ return existing;
223
+ }
224
+ const entry: OwnerEntry = {
225
+ context,
226
+ lockPath: join(context.codexHome, NATIVE_MAIN_OWNER_DB),
227
+ options: {
228
+ retryMs: Math.max(1, options.retryMs ?? NATIVE_MAIN_OWNER_RETRY_MS),
229
+ platform: options.platform ?? process.platform,
230
+ env: options.env,
231
+ hardenPath: options.hardenPath,
232
+ },
233
+ refs: 0,
234
+ generation: 1,
235
+ snapshot: { status: "acquiring", homeId: context.homeId },
236
+ listeners: new Set(),
237
+ activeOperations: new Set(),
238
+ aclTimeoutRetryUsed: false,
239
+ prepared: false,
240
+ closing: false,
241
+ };
242
+ entries.set(context.homeId, entry);
243
+ installExitHook();
244
+ return entry;
245
+ }
246
+
247
+ export function retainNativeMainOwner(
248
+ context: NativeProfileContext,
249
+ options: NativeMainOwnerOptions = {},
250
+ ): NativeMainOwnerReference {
251
+ const entry = entryFor(context, options);
252
+ if (entry.closing) {
253
+ throw new NativeProfileError("NATIVE_MAIN_OWNER_BUSY", "Native-main ownership is closing.", 503, true);
254
+ }
255
+ entry.refs += 1;
256
+ if (!entry.drive && !entry.database && !entry.timer && entry.snapshot.status !== "unavailable") {
257
+ const generation = entry.generation;
258
+ entry.drive = drive(entry, generation).finally(() => {
259
+ if (entry.generation === generation) entry.drive = undefined;
260
+ });
261
+ }
262
+ let released = false;
263
+ return {
264
+ homeId: context.homeId,
265
+ snapshot: () => ({ ...entry.snapshot }),
266
+ subscribe(listener) {
267
+ entry.listeners.add(listener);
268
+ listener({ ...entry.snapshot });
269
+ return () => { entry.listeners.delete(listener); };
270
+ },
271
+ async release() {
272
+ if (released) return;
273
+ released = true;
274
+ entry.refs = Math.max(0, entry.refs - 1);
275
+ if (entry.refs !== 0 || entry.closing) return;
276
+ entry.closing = true;
277
+ entry.generation += 1;
278
+ if (entry.timer) clearTimeout(entry.timer);
279
+ entry.timer = undefined;
280
+ publish(entry, { status: "closing", homeId: entry.context.homeId });
281
+ // Detach synchronously before the first await. startServer is synchronous,
282
+ // so a failed listen must be able to create a fresh reference immediately;
283
+ // SQLite will keep that successor contended until this entry finishes close.
284
+ if (entries.get(entry.context.homeId) === entry) entries.delete(entry.context.homeId);
285
+ detachedEntries.add(entry);
286
+ if (entry.drive) await Promise.allSettled([entry.drive]);
287
+ while (entry.activeOperations.size > 0) {
288
+ await Promise.allSettled([...entry.activeOperations]);
289
+ }
290
+ closeEntry(entry);
291
+ publish(entry, { status: "released", homeId: entry.context.homeId });
292
+ detachedEntries.delete(entry);
293
+ },
294
+ };
295
+ }
296
+
297
+ export function nativeMainOwnerSnapshot(context: NativeProfileContext): NativeMainOwnerSnapshot | null {
298
+ const entry = entries.get(context.homeId);
299
+ return entry ? { ...entry.snapshot } : null;
300
+ }
301
+
302
+ export function assertNativeMainOwner(context: NativeProfileContext): void {
303
+ const entry = entries.get(context.homeId);
304
+ if (entry?.snapshot.status === "held" && entry.database && !entry.closing) return;
305
+ const unavailable = entry?.snapshot.status === "unavailable";
306
+ throw new NativeProfileError(
307
+ unavailable ? "NATIVE_MAIN_OWNER_UNAVAILABLE" : "NATIVE_MAIN_OWNER_BUSY",
308
+ unavailable
309
+ ? "This process cannot establish native-main ownership for the effective CODEX_HOME."
310
+ : "Another OpenCodex process owns the physical native-main login for the effective CODEX_HOME.",
311
+ 503,
312
+ true,
313
+ );
314
+ }
315
+
316
+ export async function withNativeMainOwnerOperation<T>(
317
+ context: NativeProfileContext,
318
+ operation: () => Promise<T>,
319
+ ): Promise<T> {
320
+ const entry = entries.get(context.homeId);
321
+ // Direct manager unit tests and non-server library consumers retain the historical
322
+ // transaction-only behavior. Every live server registers an entry before listen.
323
+ if (!entry) return operation();
324
+ assertNativeMainOwner(context);
325
+ let tracked!: Promise<T>;
326
+ tracked = Promise.resolve().then(operation).finally(() => { entry.activeOperations.delete(tracked); });
327
+ entry.activeOperations.add(tracked);
328
+ return tracked;
329
+ }