@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,150 @@
1
+ import { createHash } from "node:crypto";
2
+ import { chmodSync, lstatSync, mkdirSync, realpathSync } from "node:fs";
3
+ import { dirname, isAbsolute, join } from "node:path";
4
+
5
+ import { Database } from "bun:sqlite";
6
+
7
+ import {
8
+ normalizeTrustedDarwinSystemAlias,
9
+ sameLogGuardPathIdentity,
10
+ } from "./path-safety";
11
+ import { isSqliteBusy } from "./sqlite-errors";
12
+
13
+ import {
14
+ CodexUserIdentityRefusal,
15
+ resolveCodexCoordinatorDatabasePath,
16
+ resolveEffectiveUserIdentity,
17
+ } from "../user-identity";
18
+
19
+ export type CodexLogGuardLockOutcome<T> =
20
+ | { kind: "completed"; value: T }
21
+ | { kind: "unavailable"; reason: "busy" | "database" | "unsafe-path" };
22
+
23
+ export interface CodexLogGuardLockDeps {
24
+ /** Test seam. Production resolves a dedicated DB in the trusted user runtime root. */
25
+ resolveDatabasePath?: (canonicalCodexHome: string, canonicalLogsDbPath: string) => string;
26
+ }
27
+
28
+ function lockFileIsSafe(path: string, requireRealpath: boolean): boolean {
29
+ const stat = lstatSync(path);
30
+ if (stat.isSymbolicLink() || !stat.isFile()) return false;
31
+ if (requireRealpath && !sameLogGuardPathIdentity(realpathSync.native(path), path)) return false;
32
+ if (process.platform === "win32") return true;
33
+ const uid = process.getuid?.();
34
+ return uid !== undefined && stat.uid === uid && (stat.mode & 0o777) === 0o600;
35
+ }
36
+
37
+ export function codexLogGuardLockDigest(
38
+ canonicalCodexHome: string,
39
+ canonicalLogsDbPath: string,
40
+ ): string {
41
+ const digestCodexHome = normalizeTrustedDarwinSystemAlias(canonicalCodexHome);
42
+ const digestLogsDbPath = normalizeTrustedDarwinSystemAlias(canonicalLogsDbPath);
43
+ return createHash("sha256")
44
+ .update(`${digestCodexHome.length}:${digestCodexHome}`)
45
+ .update(`${digestLogsDbPath.length}:${digestLogsDbPath}`)
46
+ .digest("hex");
47
+ }
48
+
49
+ function resolveLogGuardLockDatabase(
50
+ canonicalCodexHome: string,
51
+ canonicalLogsDbPath: string,
52
+ ): string {
53
+ if (!isAbsolute(canonicalCodexHome) || !isAbsolute(canonicalLogsDbPath)) {
54
+ throw new CodexUserIdentityRefusal("Codex Log Guard lock keys must be absolute paths.");
55
+ }
56
+
57
+ const identity = resolveEffectiveUserIdentity();
58
+ // Use the native-coordinator resolver only to enter the already-audited,
59
+ // environment-independent per-user runtime root. L itself is a different
60
+ // database in a sibling directory and never acquires N or H.
61
+ const coordinatorPath = resolveCodexCoordinatorDatabasePath(identity, canonicalCodexHome);
62
+ const runtimeRoot = dirname(dirname(coordinatorPath));
63
+ const locksDir = join(runtimeRoot, "log-guard-locks");
64
+ mkdirSync(locksDir, { recursive: true, mode: 0o700 });
65
+ const dirStat = lstatSync(locksDir);
66
+ if (!dirStat.isDirectory() || dirStat.isSymbolicLink()
67
+ || !sameLogGuardPathIdentity(realpathSync.native(locksDir), locksDir)) {
68
+ throw new CodexUserIdentityRefusal("Codex Log Guard lock directory is unsafe.");
69
+ }
70
+ if (process.platform !== "win32") {
71
+ const uid = process.getuid?.();
72
+ // `mkdirSync` applies `mode` only when it CREATES the directory, so a
73
+ // directory left by an earlier build (or a permissive umask on a older
74
+ // version) keeps its old permissions forever and every Log Guard mutation
75
+ // fails `unsafe_path` with no in-product way out. Tighten it ourselves when
76
+ // we own it, and only refuse if that cannot be achieved.
77
+ if (uid === undefined || dirStat.uid !== uid) {
78
+ throw new CodexUserIdentityRefusal("Codex Log Guard lock directory is not private to the effective user.");
79
+ }
80
+ if ((dirStat.mode & 0o777) !== 0o700) {
81
+ try {
82
+ chmodSync(locksDir, 0o700);
83
+ } catch {
84
+ throw new CodexUserIdentityRefusal("Codex Log Guard lock directory is not private to the effective user.");
85
+ }
86
+ // Re-read rather than trusting the chmod: a filesystem that ignores mode
87
+ // bits must still fail closed.
88
+ if ((lstatSync(locksDir).mode & 0o777) !== 0o700) {
89
+ throw new CodexUserIdentityRefusal("Codex Log Guard lock directory is not private to the effective user.");
90
+ }
91
+ }
92
+ }
93
+
94
+ return join(locksDir, `${codexLogGuardLockDigest(canonicalCodexHome, canonicalLogsDbPath)}.sqlite`);
95
+ }
96
+
97
+ export function withCodexLogGuardLock<T>(
98
+ canonicalCodexHome: string,
99
+ canonicalLogsDbPath: string,
100
+ work: () => T,
101
+ deps: CodexLogGuardLockDeps = {},
102
+ ): CodexLogGuardLockOutcome<T> {
103
+ let databasePath: string;
104
+ try {
105
+ databasePath = deps.resolveDatabasePath?.(canonicalCodexHome, canonicalLogsDbPath)
106
+ ?? resolveLogGuardLockDatabase(canonicalCodexHome, canonicalLogsDbPath);
107
+ } catch (error) {
108
+ if (error instanceof CodexUserIdentityRefusal) {
109
+ return { kind: "unavailable", reason: "unsafe-path" };
110
+ }
111
+ return { kind: "unavailable", reason: "database" };
112
+ }
113
+
114
+ let database: Database | undefined;
115
+ let transactionOpen = false;
116
+ try {
117
+ let absent = false;
118
+ try {
119
+ if (!lockFileIsSafe(databasePath, false)) {
120
+ return { kind: "unavailable", reason: "unsafe-path" };
121
+ }
122
+ } catch (error) {
123
+ if ((error as NodeJS.ErrnoException | undefined)?.code !== "ENOENT") throw error;
124
+ absent = true;
125
+ }
126
+
127
+ database = new Database(databasePath, { create: true });
128
+ if (absent) {
129
+ try { chmodSync(databasePath, 0o600); } catch { /* Windows permissions are enforced by the trusted runtime root. */ }
130
+ }
131
+ if (!lockFileIsSafe(databasePath, true)) {
132
+ return { kind: "unavailable", reason: "unsafe-path" };
133
+ }
134
+
135
+ database.exec("PRAGMA busy_timeout = 0; BEGIN IMMEDIATE");
136
+ transactionOpen = true;
137
+ const value = work();
138
+ database.exec("COMMIT");
139
+ transactionOpen = false;
140
+ return { kind: "completed", value };
141
+ } catch (error) {
142
+ if (transactionOpen) {
143
+ try { database?.exec("ROLLBACK"); } catch { /* close releases it */ }
144
+ }
145
+ if (isSqliteBusy(error)) return { kind: "unavailable", reason: "busy" };
146
+ throw error;
147
+ } finally {
148
+ try { database?.close(); } catch { /* acquisition already settled */ }
149
+ }
150
+ }
@@ -0,0 +1,403 @@
1
+ import { lstatSync, realpathSync, statSync } from "node:fs";
2
+ import { Database, constants as sqliteConstants } from "bun:sqlite";
3
+
4
+ import { getCodexHome, resolveCodexLogsDbPath } from "../paths";
5
+ import { samePathIdentity } from "../user-identity";
6
+ import { hasCurrentLogsSchema, inspectCodexLogs } from "./inspect";
7
+ import { withCodexLogGuardLock, type CodexLogGuardLockOutcome } from "./lock";
8
+ import { sameLogGuardPathIdentity } from "./path-safety";
9
+ import { isSqliteBusy } from "./sqlite-errors";
10
+ import { listRunningCodexProcesses, type CodexWriterProcessCheck } from "./processes";
11
+
12
+ const CURRENT_LOG_COLUMNS = [
13
+ "id",
14
+ "ts",
15
+ "ts_nanos",
16
+ "level",
17
+ "target",
18
+ "feedback_log_body",
19
+ "module_path",
20
+ "file",
21
+ "line",
22
+ "thread_id",
23
+ "process_uuid",
24
+ "estimated_bytes",
25
+ ] as const;
26
+
27
+ /**
28
+ * Budgets are expressed in BYTES and converted with the database's real page
29
+ * size, because that is what the guide promises: ~8 MiB per batch and ~256 MiB
30
+ * per run. Fixed page counts silently meant something different on every page
31
+ * size — at the 4 KiB pages the fixtures use, 512/8192 pages is 2 MiB/32 MiB,
32
+ * a quarter of the documented budget.
33
+ */
34
+ const DEFAULT_BATCH_BYTES = 8 * 1024 * 1024;
35
+ const DEFAULT_MAX_BYTES_PER_RUN = 256 * 1024 * 1024;
36
+ const MAX_ITERATIONS = 64;
37
+
38
+ /** Convert a byte budget to whole pages, never returning zero pages. */
39
+ function pagesForBytes(bytes: number, pageSize: number): number {
40
+ if (!Number.isFinite(pageSize) || pageSize <= 0) return 1;
41
+ return Math.max(1, Math.floor(bytes / pageSize));
42
+ }
43
+
44
+ type CompactStopReason = "complete" | "page_budget" | "no_progress" | "busy";
45
+
46
+ export interface CodexLogGuardCompactionMeasure {
47
+ databaseBytes: number;
48
+ /** On-disk WAL sidecar size at measurement time; FULL checkpoint does not imply shrinkage. */
49
+ walBytes: number;
50
+ pageCount: number;
51
+ freelistPages: number;
52
+ reclaimableBytes: number;
53
+ }
54
+
55
+ export interface CodexLogGuardCompactionReport {
56
+ pageSize: number;
57
+ before: CodexLogGuardCompactionMeasure;
58
+ after: CodexLogGuardCompactionMeasure;
59
+ pagesReclaimed: number;
60
+ /**
61
+ * Logical space returned to the free list, in bytes (`pagesReclaimed * pageSize`).
62
+ * Distinct from `physicalDatabaseBytesReclaimed`: an incremental vacuum can
63
+ * reclaim pages without the file shrinking, so a run that reports logical
64
+ * progress and zero physical shrinkage is normal rather than a failure. The
65
+ * guide documented this field before it existed.
66
+ */
67
+ logicalBytesReclaimed: number;
68
+ physicalDatabaseBytesReclaimed: number;
69
+ iterations: number;
70
+ complete: boolean;
71
+ stopReason: CompactStopReason;
72
+ integrity: { before: "ok"; after: "ok" };
73
+ }
74
+
75
+ export type CodexLogGuardCompactionError =
76
+ | "unsupported_schema"
77
+ | "codex_running"
78
+ | "process_enumeration_failed"
79
+ | "unsafe_path"
80
+ | "busy"
81
+ | "database_error"
82
+ | "auto_vacuum_not_incremental"
83
+ | "integrity_check_failed";
84
+
85
+ export type CodexLogGuardCompactionResult =
86
+ | { ok: true; report: CodexLogGuardCompactionReport }
87
+ | {
88
+ ok: false;
89
+ error: Exclude<CodexLogGuardCompactionError, "integrity_check_failed">;
90
+ }
91
+ | { ok: false; error: "integrity_check_failed"; phase: "before" | "after" };
92
+
93
+ export interface CodexLogGuardMaintenanceDeps {
94
+ codexHome?: string;
95
+ processCheck?: () => CodexWriterProcessCheck;
96
+ withLock?: <T>(
97
+ canonicalCodexHome: string,
98
+ canonicalLogsDbPath: string,
99
+ work: () => T,
100
+ ) => CodexLogGuardLockOutcome<T>;
101
+ quickCheck?: (db: Database) => string[];
102
+ openDatabase?: (databasePath: string, flags: number) => Database;
103
+ batchPages?: number;
104
+ maxPagesPerRun?: number;
105
+ }
106
+
107
+ interface ColumnRow { name: string }
108
+ interface CheckpointRow {
109
+ busy?: number;
110
+ log?: number;
111
+ checkpointed?: number;
112
+ }
113
+
114
+ interface DatabaseFileIdentity {
115
+ dev: number;
116
+ ino: number;
117
+ realPath: string;
118
+ }
119
+
120
+ function databasePathIdentity(databasePath: string): DatabaseFileIdentity | null {
121
+ try {
122
+ const stat = lstatSync(databasePath);
123
+ if (!stat.isFile() || stat.isSymbolicLink()) return null;
124
+ const realPath = realpathSync.native(databasePath);
125
+ if (!sameLogGuardPathIdentity(realPath, databasePath)) return null;
126
+ return { dev: stat.dev, ino: stat.ino, realPath };
127
+ } catch {
128
+ return null;
129
+ }
130
+ }
131
+
132
+ function databasePathIsSafe(databasePath: string): boolean {
133
+ return databasePathIdentity(databasePath) !== null;
134
+ }
135
+
136
+ function databasePathStillMatches(
137
+ databasePath: string,
138
+ before: DatabaseFileIdentity,
139
+ ): boolean {
140
+ const after = databasePathIdentity(databasePath);
141
+ return after !== null
142
+ && after.dev === before.dev
143
+ && after.ino === before.ino
144
+ && samePathIdentity(after.realPath, before.realPath);
145
+ }
146
+
147
+ function exactCurrentSchema(db: Database): boolean {
148
+ // Same reasoning as protection.ts: the pre-mutation recheck must match the
149
+ // inspector's compatibility contract exactly, or Reclaim can vacuum a
150
+ // database the inspector classifies as monitor-only.
151
+ return hasCurrentLogsSchema(db);
152
+ }
153
+
154
+ function pragmaNumber(db: Database, sql: string): number {
155
+ const row = db.query<Record<string, unknown>, []>(sql).get();
156
+ if (!row) throw new Error(`missing pragma result for ${sql}`);
157
+ const value = Number(Object.values(row)[0]);
158
+ if (!Number.isFinite(value)) throw new Error(`invalid pragma result for ${sql}`);
159
+ return value;
160
+ }
161
+
162
+ function defaultQuickCheck(db: Database): string[] {
163
+ return db.query<Record<string, unknown>, []>("PRAGMA quick_check").all().map(row => {
164
+ const value = Object.values(row)[0];
165
+ return value === undefined ? "" : String(value);
166
+ });
167
+ }
168
+
169
+ function quickCheckIsOk(rows: string[]): boolean {
170
+ return rows.length === 1 && rows[0]?.trim().toLowerCase() === "ok";
171
+ }
172
+
173
+ function processRefusal(
174
+ check: CodexWriterProcessCheck,
175
+ ): "process_enumeration_failed" | "codex_running" | null {
176
+ if (check.state === "unknown") return "process_enumeration_failed";
177
+ if (check.processes.length > 0) return "codex_running";
178
+ return null;
179
+ }
180
+
181
+ function checkpointFull(db: Database): "ok" | "busy" {
182
+ const row = db.query<CheckpointRow, []>("PRAGMA wal_checkpoint(FULL)").get();
183
+ if (!row) throw new Error("missing wal_checkpoint result");
184
+ const values = Object.values(row).map(Number);
185
+ const busy = Number(row.busy ?? values[0] ?? 0);
186
+ const log = Number(row.log ?? values[1] ?? -1);
187
+ const checkpointed = Number(row.checkpointed ?? values[2] ?? -1);
188
+ if (busy !== 0) return "busy";
189
+ // SQLite returns -1/-1 when the database is not in WAL mode or there are no
190
+ // WAL frames to report. Otherwise FULL must have copied every frame.
191
+ if (log >= 0 && checkpointed >= 0 && checkpointed < log) return "busy";
192
+ return "ok";
193
+ }
194
+
195
+ function measure(databasePath: string, db: Database, pageSize: number): CodexLogGuardCompactionMeasure {
196
+ const databaseBytes = (() => {
197
+ try {
198
+ const stat = statSync(databasePath);
199
+ return stat.isFile() ? stat.size : 0;
200
+ } catch {
201
+ return 0;
202
+ }
203
+ })();
204
+ const walBytes = (() => {
205
+ try {
206
+ const stat = statSync(`${databasePath}-wal`);
207
+ return stat.isFile() ? stat.size : 0;
208
+ } catch {
209
+ return 0;
210
+ }
211
+ })();
212
+ const pageCount = pragmaNumber(db, "PRAGMA page_count");
213
+ const freelistPages = pragmaNumber(db, "PRAGMA freelist_count");
214
+ return {
215
+ databaseBytes,
216
+ walBytes,
217
+ pageCount,
218
+ freelistPages,
219
+ reclaimableBytes: pageSize * freelistPages,
220
+ };
221
+ }
222
+
223
+ function runCompaction(
224
+ databasePath: string,
225
+ deps: CodexLogGuardMaintenanceDeps,
226
+ ): CodexLogGuardCompactionResult {
227
+ let db: Database | undefined;
228
+ let probeOpen = false;
229
+ let reportBusyPartial: (() => CodexLogGuardCompactionResult) | undefined;
230
+ try {
231
+ const beforeOpenIdentity = databasePathIdentity(databasePath);
232
+ if (!beforeOpenIdentity) return { ok: false, error: "unsafe_path" };
233
+ const openDatabase = deps.openDatabase
234
+ ?? ((path: string, flags: number) => new Database(path, flags));
235
+ db = openDatabase(databasePath, sqliteConstants.SQLITE_OPEN_READWRITE);
236
+ // The path is user-writable foreign state. Re-check its regular-file,
237
+ // canonical-path and st_dev/st_ino identity immediately after SQLite opens
238
+ // it, before issuing any pragma or write-capable statement.
239
+ if (!databasePathStillMatches(databasePath, beforeOpenIdentity)) {
240
+ return { ok: false, error: "unsafe_path" };
241
+ }
242
+ db.exec("PRAGMA busy_timeout = 0");
243
+
244
+ if (!exactCurrentSchema(db)) return { ok: false, error: "unsupported_schema" };
245
+ if (pragmaNumber(db, "PRAGMA auto_vacuum") !== 2) {
246
+ return { ok: false, error: "auto_vacuum_not_incremental" };
247
+ }
248
+
249
+ const quickCheck = deps.quickCheck ?? defaultQuickCheck;
250
+ if (!quickCheckIsOk(quickCheck(db))) {
251
+ return { ok: false, error: "integrity_check_failed", phase: "before" };
252
+ }
253
+
254
+ // Confirm no SQLite writer can acquire the file before the first checkpoint.
255
+ // BEGIN IMMEDIATE is intentionally released before PRAGMA wal_checkpoint,
256
+ // which cannot run while this same connection holds a write transaction.
257
+ db.exec("BEGIN IMMEDIATE");
258
+ probeOpen = true;
259
+ db.exec("ROLLBACK");
260
+ probeOpen = false;
261
+
262
+ if (checkpointFull(db) === "busy") return { ok: false, error: "busy" };
263
+
264
+ const pageSize = pragmaNumber(db, "PRAGMA page_size");
265
+ const before = measure(databasePath, db, pageSize);
266
+ // Derived from the byte budgets AFTER reading the real page size, so the
267
+ // documented ~8 MiB batch / ~256 MiB run hold at any page size. Explicit
268
+ // page-count overrides still win, which is what the tests use.
269
+ const batchPages = Math.max(1, Math.floor(deps.batchPages ?? pagesForBytes(DEFAULT_BATCH_BYTES, pageSize)));
270
+ const maxPages = Math.max(batchPages, Math.floor(deps.maxPagesPerRun ?? pagesForBytes(DEFAULT_MAX_BYTES_PER_RUN, pageSize)));
271
+ let previousFreelist = before.freelistPages;
272
+ let pagesReclaimed = 0;
273
+ let iterations = 0;
274
+ let stopReason: CompactStopReason = previousFreelist === 0 ? "complete" : "page_budget";
275
+
276
+ const finish = (reason: CompactStopReason): CodexLogGuardCompactionResult => {
277
+ const after = measure(databasePath, db!, pageSize);
278
+ if (!quickCheckIsOk(quickCheck(db!))) {
279
+ return { ok: false, error: "integrity_check_failed", phase: "after" };
280
+ }
281
+ const complete = after.freelistPages === 0;
282
+ // If SQLITE_BUSY is thrown after an incremental_vacuum commit but before the
283
+ // loop can sample freelist_count, the before/after measurements still capture
284
+ // that committed logical reclamation. Never under-report already-landed work.
285
+ const observedPagesReclaimed = Math.max(
286
+ pagesReclaimed,
287
+ Math.max(0, before.freelistPages - after.freelistPages),
288
+ );
289
+ return {
290
+ ok: true,
291
+ report: {
292
+ pageSize,
293
+ before,
294
+ after,
295
+ pagesReclaimed: observedPagesReclaimed,
296
+ logicalBytesReclaimed: observedPagesReclaimed * pageSize,
297
+ physicalDatabaseBytesReclaimed: Math.max(0, before.databaseBytes - after.databaseBytes),
298
+ iterations,
299
+ complete,
300
+ stopReason: reason === "busy" ? "busy" : complete ? "complete" : reason,
301
+ integrity: { before: "ok", after: "ok" },
302
+ },
303
+ };
304
+ };
305
+ reportBusyPartial = () => iterations > 0 ? finish("busy") : { ok: false, error: "busy" };
306
+
307
+ while (previousFreelist > 0 && pagesReclaimed < maxPages && iterations < MAX_ITERATIONS) {
308
+ const pageBudget = Math.min(batchPages, maxPages - pagesReclaimed, previousFreelist);
309
+ if (pageBudget <= 0) {
310
+ stopReason = "page_budget";
311
+ break;
312
+ }
313
+ const priorFreelist = previousFreelist;
314
+ db.exec(`PRAGMA incremental_vacuum(${pageBudget})`);
315
+ iterations += 1;
316
+ const checkpoint = checkpointFull(db);
317
+ const currentFreelist = pragmaNumber(db, "PRAGMA freelist_count");
318
+ const reclaimed = Math.max(0, priorFreelist - currentFreelist);
319
+ pagesReclaimed += reclaimed;
320
+ previousFreelist = currentFreelist;
321
+
322
+ // incremental_vacuum has already committed by this point. A busy FULL
323
+ // checkpoint is therefore a partial-success stop, not an atomic refusal.
324
+ if (checkpoint === "busy") return finish("busy");
325
+ if (currentFreelist === 0) {
326
+ stopReason = "complete";
327
+ break;
328
+ }
329
+ if (currentFreelist >= priorFreelist) {
330
+ stopReason = "no_progress";
331
+ break;
332
+ }
333
+ stopReason = "page_budget";
334
+ }
335
+
336
+ if (previousFreelist > 0 && iterations >= MAX_ITERATIONS && stopReason !== "no_progress") {
337
+ // MAX_ITERATIONS is a bounded-work limit, not evidence that vacuum stalled.
338
+ stopReason = "page_budget";
339
+ }
340
+
341
+ // The preceding incremental-vacuum iterations checkpoint after every batch.
342
+ // One final FULL checkpoint backfills any remaining WAL frames before the
343
+ // final main-database measurement. FULL does not reset or shrink the WAL
344
+ // sidecar, so `after.walBytes` is an observational size, not reclaimed WAL.
345
+ if (checkpointFull(db) === "busy") {
346
+ return iterations > 0 ? finish("busy") : { ok: false, error: "busy" };
347
+ }
348
+ return finish(stopReason);
349
+ } catch (error) {
350
+ if (probeOpen) {
351
+ try { db?.exec("ROLLBACK"); } catch { /* close releases it */ }
352
+ }
353
+ if (isSqliteBusy(error)) {
354
+ // Mirror the explicit busy exits. Once at least one vacuum batch completed,
355
+ // a later thrown busy is a partial-success stop rather than a pure refusal.
356
+ if (reportBusyPartial) {
357
+ try { return reportBusyPartial(); } catch { /* fall through to refusal */ }
358
+ }
359
+ return { ok: false, error: "busy" };
360
+ }
361
+ return { ok: false, error: "database_error" };
362
+ } finally {
363
+ try { db?.close(); } catch { /* maintenance already settled */ }
364
+ }
365
+ }
366
+
367
+ export function compactCodexLogs(
368
+ deps: CodexLogGuardMaintenanceDeps = {},
369
+ ): CodexLogGuardCompactionResult {
370
+ const codexHome = deps.codexHome ?? getCodexHome();
371
+ const inspection = inspectCodexLogs({ codexHome });
372
+ const databasePath = resolveCodexLogsDbPath({ codexHome });
373
+ if (inspection.capabilities.reclaim.state !== "supported") {
374
+ return { ok: false, error: "unsupported_schema" };
375
+ }
376
+ if (!databasePathIsSafe(databasePath)) return { ok: false, error: "unsafe_path" };
377
+
378
+ const checkProcesses = deps.processCheck ?? listRunningCodexProcesses;
379
+ const firstRefusal = processRefusal(checkProcesses());
380
+ if (firstRefusal) return { ok: false, error: firstRefusal };
381
+
382
+ const withLock = deps.withLock ?? withCodexLogGuardLock;
383
+ let locked: CodexLogGuardLockOutcome<CodexLogGuardCompactionResult>;
384
+ try {
385
+ locked = withLock(codexHome, databasePath, () => {
386
+ const secondRefusal = processRefusal(checkProcesses());
387
+ if (secondRefusal) return { ok: false as const, error: secondRefusal };
388
+ return runCompaction(databasePath, deps);
389
+ });
390
+ } catch {
391
+ return { ok: false, error: "database_error" };
392
+ }
393
+
394
+ if (locked.kind === "unavailable") {
395
+ return {
396
+ ok: false,
397
+ error: locked.reason === "busy"
398
+ ? "busy"
399
+ : locked.reason === "unsafe-path" ? "unsafe_path" : "database_error",
400
+ };
401
+ }
402
+ return locked.value;
403
+ }
@@ -0,0 +1,88 @@
1
+ import { lstatSync, realpathSync } from "node:fs";
2
+ import { dirname, resolve, sep } from "node:path";
3
+
4
+ import { samePathIdentity } from "../user-identity";
5
+
6
+ const TRUSTED_DARWIN_SYSTEM_ALIASES = [
7
+ { alias: "/var", canonical: "/private/var" },
8
+ { alias: "/tmp", canonical: "/private/tmp" },
9
+ ] as const;
10
+
11
+ export function normalizeTrustedDarwinSystemAlias(path: string): string {
12
+ const requested = resolve(path);
13
+ if (process.platform !== "darwin") return requested;
14
+
15
+ for (const entry of TRUSTED_DARWIN_SYSTEM_ALIASES) {
16
+ if (requested !== entry.alias && !requested.startsWith(`${entry.alias}${sep}`)) continue;
17
+
18
+ let actualAliasTarget: string;
19
+ try {
20
+ actualAliasTarget = realpathSync.native(entry.alias);
21
+ } catch {
22
+ // If the platform alias is absent or unreadable, keep the strict spelling check.
23
+ return requested;
24
+ }
25
+ if (!samePathIdentity(actualAliasTarget, entry.canonical, "darwin")) return requested;
26
+ return `${entry.canonical}${requested.slice(entry.alias.length)}`;
27
+ }
28
+
29
+ return requested;
30
+ }
31
+
32
+ /**
33
+ * Compare a canonical realpath with a requested Log Guard path without treating
34
+ * macOS's OS-owned /var and /tmp aliases as user-controlled redirections.
35
+ * Arbitrary ancestor symlinks remain refused.
36
+ */
37
+ export function sameLogGuardPathIdentity(realPath: string, requestedPath: string): boolean {
38
+ const requested = normalizeTrustedDarwinSystemAlias(requestedPath);
39
+ if (samePathIdentity(realPath, requested)) return true;
40
+ return sameWindowsCanonicalPath(realPath, requested);
41
+ }
42
+
43
+ /**
44
+ * On Windows, is the difference between these two spellings the OS canonicalizing the
45
+ * request rather than a redirection?
46
+ *
47
+ * `realpathSync.native` expands 8.3 short components — the `RUNNER~1` form that appears
48
+ * throughout `%TEMP%` — so the canonical path and the requested path can disagree as
49
+ * strings while naming the same file. Reading that as an ancestor-symlink redirection made
50
+ * every Log Guard mutation refuse with `unsafe_path` on Windows, which is what the CI shards
51
+ * were reporting.
52
+ *
53
+ * The first version of this re-canonicalized the requested path and compared the two
54
+ * canonical forms. That was wrong, and the Windows shard proved it: the caller already
55
+ * passes `realpathSync.native(requested)` as `realPath`, so re-resolving the request
56
+ * produced the same value on BOTH sides and a symlinked database compared equal. The
57
+ * widening let through exactly what the guard exists to refuse.
58
+ *
59
+ * The comparison is therefore link-aware. A short-name expansion rewrites the spelling of
60
+ * components that are all still directories on the same chain, so it is enough to require
61
+ * that no component of the request is a link: with none present, any remaining difference
62
+ * is the OS's own canonical spelling. A symlink or junction anywhere in the chain fails
63
+ * closed as before.
64
+ */
65
+ function sameWindowsCanonicalPath(realPath: string, requestedPath: string): boolean {
66
+ if (process.platform !== "win32") return false;
67
+ try {
68
+ if (pathChainContainsLink(requestedPath)) return false;
69
+ return samePathIdentity(realPath, realpathSync.native(requestedPath));
70
+ } catch {
71
+ return false;
72
+ }
73
+ }
74
+
75
+ /** Is any component of this path a symlink or junction? Fails closed on an unreadable one. */
76
+ function pathChainContainsLink(path: string): boolean {
77
+ let current = resolve(path);
78
+ for (;;) {
79
+ try {
80
+ if (lstatSync(current).isSymbolicLink()) return true;
81
+ } catch {
82
+ return true;
83
+ }
84
+ const parent = dirname(current);
85
+ if (parent === current) return false;
86
+ current = parent;
87
+ }
88
+ }
@@ -0,0 +1,44 @@
1
+ import { loadConfig, saveConfigPreservingClaudeCode } from "../../config";
2
+ import type { OcxConfig } from "../../types";
3
+
4
+ export type CodexLogGuardMode = "off" | "compat" | "quiet";
5
+
6
+ type ConfigWithLogGuard = OcxConfig & {
7
+ codexLogGuard?: {
8
+ mode?: unknown;
9
+ [key: string]: unknown;
10
+ };
11
+ };
12
+
13
+ export interface CodexLogGuardPolicyDeps {
14
+ load?: () => OcxConfig;
15
+ save?: (config: OcxConfig) => void;
16
+ }
17
+
18
+ export function readCodexLogGuardMode(deps: CodexLogGuardPolicyDeps = {}): CodexLogGuardMode {
19
+ const config = (deps.load ?? loadConfig)() as ConfigWithLogGuard;
20
+ const mode = config.codexLogGuard?.mode;
21
+ return mode === "compat" || mode === "quiet" ? mode : "off";
22
+ }
23
+
24
+ /**
25
+ * Persist user intent separately from Codex's logs database.
26
+ *
27
+ * A Codex migration may rebuild the `logs` table and thereby remove our
28
+ * trigger. Keeping intent in OpenCodex config makes that observable as drift
29
+ * rather than silently treating protection as disabled. `off` is explicit so
30
+ * a stale unknown value cannot reactivate protection later.
31
+ */
32
+ export function writeCodexLogGuardMode(
33
+ mode: CodexLogGuardMode,
34
+ deps: CodexLogGuardPolicyDeps = {},
35
+ ): void {
36
+ const load = deps.load ?? loadConfig;
37
+ const save = deps.save ?? saveConfigPreservingClaudeCode;
38
+ const config = load() as ConfigWithLogGuard;
39
+ const current = config.codexLogGuard && typeof config.codexLogGuard === "object"
40
+ ? config.codexLogGuard
41
+ : {};
42
+ config.codexLogGuard = { ...current, mode };
43
+ save(config);
44
+ }