@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,532 @@
1
+ import {
2
+ closeSync,
3
+ constants as fsConstants,
4
+ existsSync,
5
+ fsyncSync,
6
+ mkdirSync,
7
+ openSync,
8
+ readFileSync,
9
+ readSync,
10
+ statSync,
11
+ unlinkSync,
12
+ writeSync,
13
+ } from "node:fs";
14
+ import { randomBytes } from "node:crypto";
15
+ import { dirname } from "node:path";
16
+ import { jcsStringify } from "../digest";
17
+ import { MAX_SERIALIZED_EVENT_BYTES } from "../constants";
18
+ import type { LabEvent, LedgerCorruption, ReplayResult } from "../events/types";
19
+ import { LabValidationError, validateLabEvent } from "../events/validate";
20
+ import { ensureLabDirs, labLedgerPath } from "../paths";
21
+
22
+ export interface LedgerStore {
23
+ path: string;
24
+ append(event: LabEvent): void;
25
+ replay(): ReplayResult;
26
+ }
27
+
28
+ export interface LedgerMutationContext {
29
+ replay(): ReplayResult;
30
+ append(event: LabEvent): void;
31
+ appendIfAbsent(event: LabEvent): boolean;
32
+ }
33
+
34
+ const LEDGER_LOCK_STALE_MS = 60_000;
35
+ const LEDGER_LOCK_WAIT_MS = 5_000;
36
+
37
+ interface LedgerLockMeta {
38
+ pid: number;
39
+ createdAt: number;
40
+ token: string;
41
+ }
42
+
43
+ /** Block synchronously for the given duration (ledger lock retry only). */
44
+ function sleepSyncMs(ms: number): void {
45
+ Bun.sleepSync(ms);
46
+ }
47
+
48
+ /** Read pid, createdAt, and token metadata from a ledger lock file, if well-formed. */
49
+ function readLedgerLockMeta(lockPath: string): LedgerLockMeta | null {
50
+ try {
51
+ const parsed = JSON.parse(readFileSync(lockPath, "utf8")) as LedgerLockMeta;
52
+ if (
53
+ typeof parsed.pid === "number"
54
+ && typeof parsed.createdAt === "number"
55
+ && typeof parsed.token === "string"
56
+ && parsed.token.length > 0
57
+ ) {
58
+ return parsed;
59
+ }
60
+ } catch {
61
+ /* ignore */
62
+ }
63
+ return null;
64
+ }
65
+
66
+ /** Return true when the lock holder process is still running. */
67
+ function isLockHolderAlive(pid: number): boolean {
68
+ try {
69
+ process.kill(pid, 0);
70
+ return true;
71
+ } catch (error) {
72
+ const code = (error as NodeJS.ErrnoException).code;
73
+ if (code === "ESRCH") return false;
74
+ if (code === "EPERM") return true;
75
+ return false;
76
+ }
77
+ }
78
+
79
+ /** Return true when a ledger lock file has dead metadata and can be recovered. */
80
+ function isLedgerLockStale(lockPath: string): boolean {
81
+ const meta = readLedgerLockMeta(lockPath);
82
+ if (!meta) {
83
+ try {
84
+ return Date.now() - statSync(lockPath).mtimeMs > LEDGER_LOCK_STALE_MS;
85
+ } catch {
86
+ return false;
87
+ }
88
+ }
89
+ return !isLockHolderAlive(meta.pid);
90
+ }
91
+
92
+ /** Write lock ownership metadata to a newly created exclusive lock file. */
93
+ function writeLedgerLockMeta(fd: number, token: string): void {
94
+ const metadataBytes = Buffer.from(JSON.stringify({
95
+ pid: process.pid,
96
+ createdAt: Date.now(),
97
+ token,
98
+ }), "utf8");
99
+ let written = 0;
100
+ while (written < metadataBytes.byteLength) {
101
+ const n = writeSync(fd, metadataBytes, written, metadataBytes.byteLength - written);
102
+ if (n <= 0) {
103
+ throw new LabValidationError("short_write", "ledger lock metadata write incomplete");
104
+ }
105
+ written += n;
106
+ }
107
+ }
108
+
109
+ /** Discard a lock whose exclusive creator failed before publishing ownership metadata. */
110
+ function discardUninitialisedLedgerLock(lockPath: string, lockFd: number): void {
111
+ try {
112
+ closeSync(lockFd);
113
+ } catch {
114
+ /* ignore */
115
+ }
116
+ try {
117
+ unlinkSync(lockPath);
118
+ } catch {
119
+ /* best-effort */
120
+ }
121
+ }
122
+
123
+ /** Release a lock file only when the token still matches the path owner. */
124
+ function releaseLedgerLock(lockPath: string, lockFd: number, token: string): void {
125
+ try {
126
+ closeSync(lockFd);
127
+ } catch {
128
+ /* ignore */
129
+ }
130
+ try {
131
+ const meta = readLedgerLockMeta(lockPath);
132
+ if (meta?.token === token) unlinkSync(lockPath);
133
+ } catch {
134
+ /* best-effort */
135
+ }
136
+ }
137
+
138
+ /**
139
+ * Recover one stale lock while holding a separate recovery mutex.
140
+ *
141
+ * The recovery mutex prevents two waiters from both observing the same stale
142
+ * owner and then unlinking each other's replacement lock. If a process dies
143
+ * while holding the recovery mutex, acquisition fails closed instead of
144
+ * guessing ownership of that mutex.
145
+ */
146
+ function recoverStaleLedgerLock(lockPath: string): boolean {
147
+ const recoveryPath = `${lockPath}.recovery`;
148
+ const token = randomBytes(16).toString("hex");
149
+ let recoveryFd: number;
150
+ try {
151
+ recoveryFd = openSync(
152
+ recoveryPath,
153
+ fsConstants.O_CREAT | fsConstants.O_EXCL | fsConstants.O_WRONLY,
154
+ 0o600,
155
+ );
156
+ } catch (error) {
157
+ if ((error as NodeJS.ErrnoException).code === "EEXIST") return false;
158
+ throw error;
159
+ }
160
+
161
+ try {
162
+ writeLedgerLockMeta(recoveryFd, token);
163
+ } catch (error) {
164
+ discardUninitialisedLedgerLock(recoveryPath, recoveryFd);
165
+ throw error;
166
+ }
167
+
168
+ try {
169
+ // Re-check after taking the recovery mutex. Another waiter may already
170
+ // have recovered the old lock and installed a live replacement.
171
+ if (!existsSync(lockPath) || !isLedgerLockStale(lockPath)) return false;
172
+ unlinkSync(lockPath);
173
+ return true;
174
+ } finally {
175
+ releaseLedgerLock(recoveryPath, recoveryFd, token);
176
+ }
177
+ }
178
+
179
+ /** Create a ledger lock file exclusively, recovering stale locks when needed. */
180
+ function tryAcquireLedgerLock(lockPath: string, deadline: number): { fd: number; token: string } {
181
+ while (Date.now() < deadline) {
182
+ const token = randomBytes(16).toString("hex");
183
+ let fd: number;
184
+ try {
185
+ fd = openSync(lockPath, fsConstants.O_CREAT | fsConstants.O_EXCL | fsConstants.O_WRONLY, 0o600);
186
+ } catch (error) {
187
+ if (existsSync(lockPath) && isLedgerLockStale(lockPath)) {
188
+ try {
189
+ if (recoverStaleLedgerLock(lockPath)) continue;
190
+ } catch (recoveryError) {
191
+ if (Date.now() >= deadline) throw recoveryError;
192
+ }
193
+ }
194
+ if (Date.now() >= deadline) throw error;
195
+ sleepSyncMs(10);
196
+ continue;
197
+ }
198
+ try {
199
+ writeLedgerLockMeta(fd, token);
200
+ } catch (error) {
201
+ discardUninitialisedLedgerLock(lockPath, fd);
202
+ throw error;
203
+ }
204
+ return { fd, token };
205
+ }
206
+ throw new Error("ledger lock acquisition timed out");
207
+ }
208
+
209
+ /** Run a ledger mutation while holding the compatibility ledger lock file. */
210
+ function withLedgerLock<T>(ledgerPath: string, fn: () => T): T {
211
+ const lockPath = `${ledgerPath}.lock`;
212
+ mkdirSync(dirname(ledgerPath), { recursive: true, mode: 0o700 });
213
+ const deadline = Date.now() + LEDGER_LOCK_WAIT_MS;
214
+ const { fd, token } = tryAcquireLedgerLock(lockPath, deadline);
215
+ try {
216
+ return fn();
217
+ } finally {
218
+ releaseLedgerLock(lockPath, fd, token);
219
+ }
220
+ }
221
+
222
+ /** Durable append of one already-validated event as a single JSONL line + fsync. */
223
+ function appendValidatedLabEvent(ledgerPath: string, event: LabEvent): void {
224
+ mkdirSync(dirname(ledgerPath), { recursive: true, mode: 0o700 });
225
+ const line = `${jcsStringify(event)}\n`;
226
+ const bytes = new TextEncoder().encode(line);
227
+ const fd = openSync(ledgerPath, "a", 0o600);
228
+ try {
229
+ let written = 0;
230
+ while (written < bytes.byteLength) {
231
+ const n = writeSync(fd, bytes, written, bytes.byteLength - written);
232
+ if (n <= 0) {
233
+ throw new LabValidationError("short_write", "ledger append made no progress");
234
+ }
235
+ written += n;
236
+ }
237
+ fsyncSync(fd);
238
+ } finally {
239
+ closeSync(fd);
240
+ }
241
+ }
242
+
243
+ function isThenable(value: unknown): value is PromiseLike<unknown> {
244
+ return (
245
+ (typeof value === "object" && value !== null) || typeof value === "function"
246
+ ) && typeof (value as { then?: unknown }).then === "function";
247
+ }
248
+
249
+ /**
250
+ * Serialize a ledger read-modify-write transaction with all ordinary appends.
251
+ * The callback is intentionally synchronous. Mutation methods become invalid
252
+ * as soon as the callback returns, so an accidental async continuation cannot
253
+ * write after the lock has been released.
254
+ */
255
+ export function withLedgerMutation<T>(
256
+ ledgerPath: string,
257
+ fn: (mutation: LedgerMutationContext) => T,
258
+ ): T {
259
+ return withLedgerLock(ledgerPath, () => {
260
+ let active = true;
261
+ const requireActive = () => {
262
+ if (!active) {
263
+ throw new LabValidationError(
264
+ "inactive_ledger_mutation",
265
+ "ledger mutation context used after its lock was released",
266
+ );
267
+ }
268
+ };
269
+ const replay = () => {
270
+ requireActive();
271
+ return replayLabLedger(ledgerPath);
272
+ };
273
+ const append = (event: LabEvent) => {
274
+ requireActive();
275
+ appendValidatedLabEvent(ledgerPath, validateLabEvent(event));
276
+ };
277
+ const appendIfAbsent = (event: LabEvent): boolean => {
278
+ requireActive();
279
+ const validated = validateLabEvent(event);
280
+ if (replay().events.some((row) => row.eventId === validated.eventId)) return false;
281
+ appendValidatedLabEvent(ledgerPath, validated);
282
+ return true;
283
+ };
284
+
285
+ try {
286
+ const result = fn({ replay, append, appendIfAbsent });
287
+ if (isThenable(result)) {
288
+ throw new LabValidationError(
289
+ "async_ledger_mutation",
290
+ "ledger mutation callback must be synchronous",
291
+ );
292
+ }
293
+ return result;
294
+ } finally {
295
+ active = false;
296
+ }
297
+ });
298
+ }
299
+
300
+ /** Durable append of one validated event as a single JSONL line + fsync. */
301
+ export function appendLabEvent(ledgerPath: string, event: LabEvent): void {
302
+ withLedgerMutation(ledgerPath, (mutation) => {
303
+ mutation.append(event);
304
+ });
305
+ }
306
+
307
+ /**
308
+ * Append only when eventId is absent. Uses the same mutation lock as every
309
+ * other ledger writer so the presence check and append are one transaction.
310
+ */
311
+ export function appendLabEventIfAbsent(ledgerPath: string, event: LabEvent): boolean {
312
+ return withLedgerMutation(ledgerPath, (mutation) => mutation.appendIfAbsent(event));
313
+ }
314
+
315
+ function processLine(
316
+ line: string,
317
+ lineNumber: number,
318
+ lineHasTrailingNewline: boolean,
319
+ events: LabEvent[],
320
+ seenIds: Set<string>,
321
+ corruptions: LedgerCorruption[],
322
+ ): void {
323
+ if (!lineHasTrailingNewline) {
324
+ corruptions.push({
325
+ kind: "partial_line",
326
+ lineNumber,
327
+ detail: "partial final JSONL line (missing trailing newline)",
328
+ });
329
+ return;
330
+ }
331
+ if (line.trim() === "") {
332
+ corruptions.push({ kind: "malformed_line", lineNumber, detail: "empty line" });
333
+ return;
334
+ }
335
+
336
+ let parsed: unknown;
337
+ try {
338
+ parsed = JSON.parse(line);
339
+ } catch {
340
+ corruptions.push({ kind: "malformed_line", lineNumber, detail: "JSON parse failed" });
341
+ return;
342
+ }
343
+
344
+ let event: LabEvent;
345
+ try {
346
+ event = validateLabEvent(parsed);
347
+ } catch (err) {
348
+ corruptions.push({
349
+ kind: "invalid_event",
350
+ lineNumber,
351
+ detail: err instanceof Error ? err.message : String(err),
352
+ });
353
+ return;
354
+ }
355
+
356
+ if (seenIds.has(event.eventId)) {
357
+ corruptions.push({
358
+ kind: "duplicate_event",
359
+ lineNumber,
360
+ eventId: event.eventId,
361
+ detail: "duplicate eventId",
362
+ });
363
+ return;
364
+ }
365
+ seenIds.add(event.eventId);
366
+ events.push(event);
367
+ }
368
+
369
+ function processBufferedLines(
370
+ buf: Buffer,
371
+ state: {
372
+ lineNumber: number;
373
+ totalLineCount: number;
374
+ skippingOversizedLine: boolean;
375
+ events: LabEvent[];
376
+ seenIds: Set<string>;
377
+ corruptions: LedgerCorruption[];
378
+ },
379
+ ): { carry: Buffer; skippingOversizedLine: boolean } {
380
+ let start = 0;
381
+ let skipping = state.skippingOversizedLine;
382
+
383
+ while (start < buf.length) {
384
+ const newlineIdx = buf.indexOf(0x0a, start);
385
+ if (newlineIdx < 0) {
386
+ const tail = buf.subarray(start);
387
+ if (skipping) {
388
+ return { carry: Buffer.alloc(0), skippingOversizedLine: true };
389
+ }
390
+ if (tail.length > MAX_SERIALIZED_EVENT_BYTES) {
391
+ state.lineNumber += 1;
392
+ state.totalLineCount += 1;
393
+ state.corruptions.push({
394
+ kind: "malformed_line",
395
+ lineNumber: state.lineNumber,
396
+ detail: `line exceeds ${MAX_SERIALIZED_EVENT_BYTES} bytes without newline`,
397
+ });
398
+ // Drop the oversized prefix immediately. Keeping it would defeat the
399
+ // replay memory bound and count the same line again at EOF.
400
+ return { carry: Buffer.alloc(0), skippingOversizedLine: true };
401
+ }
402
+ return { carry: tail, skippingOversizedLine: false };
403
+ }
404
+
405
+ const lineBytes = buf.subarray(start, newlineIdx);
406
+ start = newlineIdx + 1;
407
+
408
+ if (skipping) {
409
+ skipping = false;
410
+ continue;
411
+ }
412
+
413
+ state.lineNumber += 1;
414
+ state.totalLineCount += 1;
415
+ // Decode only complete JSONL lines. Keeping incomplete line bytes as Buffer
416
+ // carry naturally preserves UTF-8 code points split across read chunks.
417
+ processLine(lineBytes.toString("utf8"), state.lineNumber, true, state.events, state.seenIds, state.corruptions);
418
+ }
419
+
420
+ return { carry: Buffer.alloc(0), skippingOversizedLine: skipping };
421
+ }
422
+
423
+ /**
424
+ * Replay the JSONL ledger using chunked reads (no whole-file string buffer).
425
+ * Malformed or partial lines contribute no evidence and are reported as corruption.
426
+ */
427
+ export function replayLabLedger(ledgerPath: string): ReplayResult {
428
+ const corruptions: LedgerCorruption[] = [];
429
+ const events: LabEvent[] = [];
430
+ if (!existsSync(ledgerPath)) {
431
+ return { events, corruptions, validLineCount: 0, totalLineCount: 0 };
432
+ }
433
+ const size = statSync(ledgerPath).size;
434
+ if (size === 0) {
435
+ return {
436
+ events,
437
+ corruptions: [{ kind: "empty_ledger", detail: "ledger file is empty" }],
438
+ validLineCount: 0,
439
+ totalLineCount: 0,
440
+ };
441
+ }
442
+
443
+ const fd = openSync(ledgerPath, "r");
444
+ const chunkSize = 64 * 1024;
445
+ const chunk = Buffer.alloc(chunkSize);
446
+ let carry: Buffer = Buffer.alloc(0);
447
+ let lineNumber = 0;
448
+ let totalLineCount = 0;
449
+ const seenIds = new Set<string>();
450
+ let offset = 0;
451
+ let skippingOversizedLine = false;
452
+
453
+ const state = {
454
+ lineNumber,
455
+ totalLineCount,
456
+ skippingOversizedLine,
457
+ events,
458
+ seenIds,
459
+ corruptions,
460
+ };
461
+
462
+ try {
463
+ while (offset < size) {
464
+ const toRead = Math.min(chunkSize, size - offset);
465
+ const n = readSync(fd, chunk, 0, toRead, offset);
466
+ if (n <= 0) break;
467
+ offset += n;
468
+
469
+ // `chunk` is reused by readSync, so any bytes retained beyond this
470
+ // iteration must be detached from it before the next read.
471
+ const combined = carry.length > 0
472
+ ? Buffer.concat([carry, chunk.subarray(0, n)])
473
+ : Buffer.from(chunk.subarray(0, n));
474
+ const result = processBufferedLines(combined, state);
475
+ carry = result.carry.length > 0 ? Buffer.from(result.carry) : Buffer.alloc(0);
476
+ skippingOversizedLine = result.skippingOversizedLine;
477
+ state.skippingOversizedLine = skippingOversizedLine;
478
+ lineNumber = state.lineNumber;
479
+ totalLineCount = state.totalLineCount;
480
+ }
481
+
482
+ if (carry.length > 0) {
483
+ if (skippingOversizedLine) {
484
+ lineNumber += 1;
485
+ totalLineCount += 1;
486
+ corruptions.push({
487
+ kind: "malformed_line",
488
+ lineNumber,
489
+ detail: `oversized line exceeds ${MAX_SERIALIZED_EVENT_BYTES} bytes`,
490
+ });
491
+ } else if (carry.length > MAX_SERIALIZED_EVENT_BYTES) {
492
+ lineNumber += 1;
493
+ totalLineCount += 1;
494
+ corruptions.push({
495
+ kind: "malformed_line",
496
+ lineNumber,
497
+ detail: `partial line exceeds ${MAX_SERIALIZED_EVENT_BYTES} bytes`,
498
+ });
499
+ } else {
500
+ lineNumber += 1;
501
+ totalLineCount += 1;
502
+ processLine(carry.toString("utf8"), lineNumber, false, events, seenIds, corruptions);
503
+ }
504
+ }
505
+ } finally {
506
+ closeSync(fd);
507
+ }
508
+
509
+ return {
510
+ events,
511
+ corruptions,
512
+ validLineCount: events.length,
513
+ totalLineCount,
514
+ };
515
+ }
516
+
517
+ export function openLedgerStore(configDir?: string): LedgerStore {
518
+ const paths = ensureLabDirs(configDir);
519
+ return {
520
+ path: paths.ledgerPath,
521
+ append(event: LabEvent) {
522
+ appendLabEvent(paths.ledgerPath, event);
523
+ },
524
+ replay() {
525
+ return replayLabLedger(paths.ledgerPath);
526
+ },
527
+ };
528
+ }
529
+
530
+ export function defaultLedgerPath(configDir?: string): string {
531
+ return labLedgerPath(configDir);
532
+ }
@@ -0,0 +1,53 @@
1
+ import { LAB_CREDENTIAL_LEASE, type LabCredentialLeaseV1, type LabDestinationV1 } from "./types";
2
+
3
+ export class LabCredentialError extends Error {
4
+ override readonly name = "LabCredentialError";
5
+ constructor(message: string, readonly code: "harness_failure" | "budget_exhausted") { super(message); }
6
+ }
7
+
8
+ interface LeaseState { destinationFingerprint: string; transportId: string; remaining: number }
9
+ const LEASE_STATE = new WeakMap<object, LeaseState>();
10
+ type InternalLease = LabCredentialLeaseV1 & { toJSON(): never };
11
+
12
+ export interface CreateLeaseOptions {
13
+ destination: LabDestinationV1;
14
+ transportId?: string;
15
+ budget?: number;
16
+ }
17
+
18
+ /** Trusted credential owner may associate secret state with this opaque capability outside Lab. */
19
+ export function createCredentialLease(opts: CreateLeaseOptions): LabCredentialLeaseV1 {
20
+ const budget = opts.budget ?? 1;
21
+ if (!Number.isInteger(budget) || budget <= 0) throw new LabCredentialError("invalid credential lease budget", "harness_failure");
22
+ let lease!: InternalLease;
23
+ lease = {
24
+ [LAB_CREDENTIAL_LEASE]: true,
25
+ get remainingRequests(): number { return LEASE_STATE.get(lease)?.remaining ?? 0; },
26
+ consume(): void {
27
+ const state = LEASE_STATE.get(lease);
28
+ if (!state) throw new LabCredentialError("unknown credential lease", "harness_failure");
29
+ if (state.remaining <= 0) throw new LabCredentialError("credential lease exhausted", "budget_exhausted");
30
+ state.remaining -= 1;
31
+ },
32
+ toJSON(): never { throw new LabCredentialError("credential lease is non-serializable", "harness_failure"); },
33
+ };
34
+ LEASE_STATE.set(lease, {
35
+ destinationFingerprint: opts.destination.fingerprint,
36
+ transportId: opts.transportId ?? "default",
37
+ remaining: budget,
38
+ });
39
+ return Object.freeze(lease);
40
+ }
41
+
42
+ export function isCredentialLease(value: unknown): value is LabCredentialLeaseV1 {
43
+ return typeof value === "object" && value !== null && LEASE_STATE.has(value as object)
44
+ && (value as LabCredentialLeaseV1)[LAB_CREDENTIAL_LEASE] === true;
45
+ }
46
+
47
+ export function assertLeaseScope(lease: LabCredentialLeaseV1, destination: LabDestinationV1, transportId = "default"): void {
48
+ const state = LEASE_STATE.get(lease as object);
49
+ if (!state) throw new LabCredentialError("unknown credential lease", "harness_failure");
50
+ if (state.destinationFingerprint !== destination.fingerprint || state.transportId !== transportId) {
51
+ throw new LabCredentialError("credential lease scope mismatch", "harness_failure");
52
+ }
53
+ }