@iislee/opencodex 2.11.0 → 2.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (675) hide show
  1. package/AGENTS_INSTALL.md +109 -0
  2. package/README.md +114 -19
  3. package/bin/ocx.mjs +164 -36
  4. package/bin/package-main.mjs +1 -1
  5. package/gui/dist/assets/index-BF38heuV.js +104 -0
  6. package/gui/dist/assets/index-DMiI18Kv.css +1 -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 +19 -10
  42. package/src/adapters/anthropic-output-schema.ts +137 -0
  43. package/src/adapters/anthropic.ts +376 -52
  44. package/src/adapters/base.ts +54 -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 +601 -0
  48. package/src/adapters/cursor/checkpoint-store.ts +303 -0
  49. package/src/adapters/cursor/cursor-errors.ts +129 -6
  50. package/src/adapters/cursor/discovery.ts +61 -4
  51. package/src/adapters/cursor/effort-map.ts +27 -3
  52. package/src/adapters/cursor/framing.ts +39 -0
  53. package/src/adapters/cursor/h2-pool.ts +123 -0
  54. package/src/adapters/cursor/http1-bidi.ts +361 -0
  55. package/src/adapters/cursor/images.ts +704 -0
  56. package/src/adapters/cursor/live-models.ts +174 -58
  57. package/src/adapters/cursor/live-transport.ts +609 -170
  58. package/src/adapters/cursor/native-exec-common.ts +23 -2
  59. package/src/adapters/cursor/native-exec-fs.ts +10 -7
  60. package/src/adapters/cursor/native-exec-network.ts +1 -1
  61. package/src/adapters/cursor/native-exec-shell.ts +0 -1
  62. package/src/adapters/cursor/native-exec.ts +101 -14
  63. package/src/adapters/cursor/protobuf-events.ts +829 -11
  64. package/src/adapters/cursor/protobuf-request.ts +383 -65
  65. package/src/adapters/cursor/request-builder.ts +237 -31
  66. package/src/adapters/cursor/tool-definitions.ts +242 -12
  67. package/src/adapters/cursor/tool-result-normalize.ts +92 -0
  68. package/src/adapters/cursor/transport.ts +22 -0
  69. package/src/adapters/cursor/types.ts +28 -1
  70. package/src/adapters/cursor.ts +133 -7
  71. package/src/adapters/google-antigravity-replay.ts +558 -34
  72. package/src/adapters/google-antigravity-wire.ts +43 -10
  73. package/src/adapters/google-http.ts +38 -10
  74. package/src/adapters/google-tool-schema.ts +80 -15
  75. package/src/adapters/google-truncation.ts +11 -0
  76. package/src/adapters/google.ts +618 -74
  77. package/src/adapters/identity.ts +39 -6
  78. package/src/adapters/kiro-errors.ts +11 -0
  79. package/src/adapters/kiro-events.ts +19 -1
  80. package/src/adapters/kiro-thinking.ts +18 -2
  81. package/src/adapters/kiro-tools.ts +10 -1
  82. package/src/adapters/kiro.ts +92 -54
  83. package/src/adapters/mimo-free.ts +17 -0
  84. package/src/adapters/openai-chat-url.ts +11 -0
  85. package/src/adapters/openai-chat.ts +1311 -336
  86. package/src/adapters/openai-responses-url.ts +16 -0
  87. package/src/adapters/openai-responses.ts +830 -56
  88. package/src/adapters/registry.ts +175 -0
  89. package/src/adapters/responses-tool-schema.ts +67 -0
  90. package/src/adapters/tool-call-id.ts +119 -0
  91. package/src/adapters/tool-catalog-nudge.ts +104 -21
  92. package/src/adapters/xai-web-search.ts +185 -0
  93. package/src/bridge.ts +428 -61
  94. package/src/chat/inbound.ts +43 -19
  95. package/src/chat/outbound.ts +82 -26
  96. package/src/claude/agents-inject.ts +32 -9
  97. package/src/claude/context-windows.ts +21 -5
  98. package/src/claude/desktop-3p.ts +243 -9
  99. package/src/claude/gateway-cache.ts +41 -4
  100. package/src/claude/inbound.ts +72 -3
  101. package/src/claude/model-info.ts +38 -15
  102. package/src/claude/outbound.ts +70 -16
  103. package/src/cli/account-api.ts +35 -1
  104. package/src/cli/account-auth.ts +33 -6
  105. package/src/cli/account-catalog-refresh.ts +14 -0
  106. package/src/cli/account-extended.ts +389 -2
  107. package/src/cli/account-main.ts +317 -0
  108. package/src/cli/account.ts +30 -6
  109. package/src/cli/agent.ts +45 -1
  110. package/src/cli/claude-agent-startup-sync.ts +73 -0
  111. package/src/cli/claude-desktop.ts +29 -4
  112. package/src/cli/claude.ts +103 -34
  113. package/src/cli/codex-log-guard-doctor.ts +103 -0
  114. package/src/cli/codex-shim-autorestore.ts +2 -0
  115. package/src/cli/codex-shim-readiness.ts +76 -0
  116. package/src/cli/combo.ts +8 -0
  117. package/src/cli/config-command.ts +74 -10
  118. package/src/cli/dispatch.ts +593 -0
  119. package/src/cli/doctor.ts +315 -43
  120. package/src/cli/ensure-desired-integrations.ts +152 -0
  121. package/src/cli/export-command.ts +46 -20
  122. package/src/cli/help.ts +20 -271
  123. package/src/cli/index.ts +378 -519
  124. package/src/cli/init.ts +4 -17
  125. package/src/cli/integrations.ts +120 -2
  126. package/src/cli/lab.ts +607 -0
  127. package/src/cli/launcher-context.ts +77 -0
  128. package/src/cli/minimax.ts +497 -0
  129. package/src/cli/models-runtime.ts +35 -2
  130. package/src/cli/models.ts +100 -14
  131. package/src/cli/observe.ts +92 -3
  132. package/src/cli/opencode.ts +4 -2
  133. package/src/cli/provider-runtime.ts +18 -1
  134. package/src/cli/provider.ts +24 -3
  135. package/src/cli/ready.ts +301 -0
  136. package/src/cli/registry.ts +437 -0
  137. package/src/cli/root.ts +86 -0
  138. package/src/cli/route-policy.ts +92 -0
  139. package/src/cli/runtime-api.ts +6 -3
  140. package/src/cli/star-prompt.ts +71 -15
  141. package/src/cli/status.ts +10 -3
  142. package/src/cli/system-restart-client.ts +146 -0
  143. package/src/cli/tray-proxy.ts +153 -6
  144. package/src/cli/v2.ts +105 -10
  145. package/src/cli.ts +1 -1
  146. package/src/clients/config-export.ts +1358 -21
  147. package/src/codex/account-label.ts +14 -1
  148. package/src/codex/account-lifecycle.ts +130 -13
  149. package/src/codex/account-namespaces.ts +49 -3
  150. package/src/codex/account-priority.ts +83 -0
  151. package/src/codex/account-store.ts +29 -2
  152. package/src/codex/account-usability.ts +25 -2
  153. package/src/codex/admission.ts +256 -0
  154. package/src/codex/affinity-debug.ts +162 -0
  155. package/src/codex/app-server-processes.ts +493 -106
  156. package/src/codex/app-server-restart-service.ts +232 -0
  157. package/src/codex/auth-api.ts +849 -242
  158. package/src/codex/auth-collision.ts +5 -3
  159. package/src/codex/auth-context.ts +345 -32
  160. package/src/codex/autostart-health.ts +8 -1
  161. package/src/codex/catalog/account-models.ts +67 -0
  162. package/src/codex/catalog/aggregation.ts +68 -10
  163. package/src/codex/catalog/bundled.ts +331 -33
  164. package/src/codex/catalog/effort.ts +121 -30
  165. package/src/codex/catalog/filesystem-evidence.ts +302 -0
  166. package/src/codex/catalog/kinds.ts +2 -0
  167. package/src/codex/catalog/metadata.ts +529 -45
  168. package/src/codex/catalog/native-models.ts +72 -0
  169. package/src/codex/catalog/parsing.ts +224 -30
  170. package/src/codex/catalog/provider-fetch.ts +1460 -134
  171. package/src/codex/catalog/sync.ts +1449 -186
  172. package/src/codex/catalog-admission.ts +199 -0
  173. package/src/codex/catalog-refresh-status.ts +105 -0
  174. package/src/codex/catalog-write-serialization.ts +242 -0
  175. package/src/codex/catalog.ts +6 -3
  176. package/src/codex/codex-write-lock.ts +384 -0
  177. package/src/codex/convergence-types.ts +614 -0
  178. package/src/codex/convergence.ts +651 -0
  179. package/src/codex/coordinator-doctor.ts +332 -0
  180. package/src/codex/custom-model-catalog-migration.ts +176 -0
  181. package/src/codex/desired-state.ts +230 -0
  182. package/src/codex/features.ts +636 -39
  183. package/src/codex/generation.ts +202 -0
  184. package/src/codex/history-job.ts +407 -0
  185. package/src/codex/history-lock.ts +242 -0
  186. package/src/codex/history-migration-guardian.ts +26 -20
  187. package/src/codex/history-provider.ts +231 -28
  188. package/src/codex/history-transition.ts +105 -0
  189. package/src/codex/history-worker.ts +220 -0
  190. package/src/codex/inject-coordination.ts +290 -0
  191. package/src/codex/inject.ts +1073 -152
  192. package/src/codex/injected-marker.ts +37 -3
  193. package/src/codex/integration-record.ts +266 -0
  194. package/src/codex/internal/catalog-writer.ts +203 -0
  195. package/src/codex/internal/history-writer.ts +80 -0
  196. package/src/codex/journal.ts +66 -4
  197. package/src/codex/log-guard/inspect.ts +506 -0
  198. package/src/codex/log-guard/lock.ts +150 -0
  199. package/src/codex/log-guard/maintenance.ts +403 -0
  200. package/src/codex/log-guard/path-safety.ts +88 -0
  201. package/src/codex/log-guard/policy.ts +44 -0
  202. package/src/codex/log-guard/processes.ts +205 -0
  203. package/src/codex/log-guard/protection.ts +489 -0
  204. package/src/codex/log-guard/sqlite-errors.ts +9 -0
  205. package/src/codex/main-account-cache.ts +24 -0
  206. package/src/codex/main-account.ts +29 -1
  207. package/src/codex/management-convergence.ts +167 -0
  208. package/src/codex/model-cache.ts +56 -10
  209. package/src/codex/model-entitlements.ts +353 -0
  210. package/src/codex/native-main-admission.ts +47 -0
  211. package/src/codex/native-main-auth-temp.ts +187 -0
  212. package/src/codex/native-main-claim.ts +178 -0
  213. package/src/codex/native-main-lock-file.ts +162 -0
  214. package/src/codex/native-main-owner.ts +329 -0
  215. package/src/codex/native-profile-api.ts +247 -0
  216. package/src/codex/native-profile-manager.ts +1531 -0
  217. package/src/codex/native-profile-processes.ts +121 -0
  218. package/src/codex/native-profile-recovery.ts +99 -0
  219. package/src/codex/native-profile-stage-store.ts +387 -0
  220. package/src/codex/native-profile-startup.ts +492 -0
  221. package/src/codex/native-profile-store.ts +855 -0
  222. package/src/codex/native-profile-types.ts +120 -0
  223. package/src/codex/native-residue.ts +682 -0
  224. package/src/codex/paths.ts +80 -1
  225. package/src/codex/plan-from-token.ts +140 -0
  226. package/src/codex/plan.ts +40 -0
  227. package/src/codex/plugins-doctor.ts +1 -1
  228. package/src/codex/pool-rotation.ts +74 -4
  229. package/src/codex/project-config-warnings.ts +20 -6
  230. package/src/codex/prompt-journal.ts +352 -0
  231. package/src/codex/prompt-layers.ts +967 -0
  232. package/src/codex/prompt-lock.ts +143 -0
  233. package/src/codex/quota-rejection.ts +298 -0
  234. package/src/codex/quota.ts +175 -13
  235. package/src/codex/refresh.ts +11 -2
  236. package/src/codex/reset-credit-recovery.ts +1044 -0
  237. package/src/codex/routing.ts +505 -94
  238. package/src/codex/runtime.ts +159 -38
  239. package/src/codex/shim.ts +1009 -28
  240. package/src/codex/subagent-model-fallback.ts +350 -35
  241. package/src/codex/sync.ts +191 -2
  242. package/src/codex/transition-state.ts +612 -0
  243. package/src/codex/upstream-host-health.ts +368 -0
  244. package/src/codex/user-identity.ts +557 -0
  245. package/src/codex/warmup.ts +187 -81
  246. package/src/codex/write-coordination.ts +114 -0
  247. package/src/combos/failover.ts +20 -0
  248. package/src/combos/index.ts +4 -0
  249. package/src/combos/request.ts +32 -0
  250. package/src/combos/types.ts +81 -9
  251. package/src/config/provider-name.ts +24 -0
  252. package/src/config.ts +1762 -140
  253. package/src/generated/compatibility-version.json +3164 -0
  254. package/src/generated/{jawcode-model-metadata.ts → model-metadata.ts} +19 -17
  255. package/src/grok/inject.ts +16 -5
  256. package/src/grok/inspect.ts +45 -0
  257. package/src/grok/sync.ts +2 -2
  258. package/src/images/loop.ts +152 -29
  259. package/src/images/plan.ts +23 -13
  260. package/src/integrations/config-io.ts +269 -0
  261. package/src/integrations/journal.ts +315 -0
  262. package/src/integrations/merge.ts +135 -0
  263. package/src/integrations/mutation-flight.ts +71 -0
  264. package/src/integrations/native/ownership-preflight.ts +202 -0
  265. package/src/integrations/omp-yaml-source.ts +358 -0
  266. package/src/integrations/owned-refresh.ts +74 -0
  267. package/src/integrations/ownership.ts +111 -0
  268. package/src/integrations/registry.ts +159 -0
  269. package/src/integrations/serialize.ts +314 -0
  270. package/src/integrations/state.ts +361 -0
  271. package/src/integrations/store.ts +103 -0
  272. package/src/integrations/writer-lock.ts +98 -0
  273. package/src/integrations/writer.ts +691 -0
  274. package/src/lab/artifacts/sanitize.ts +586 -0
  275. package/src/lab/artifacts/secure-fs.ts +475 -0
  276. package/src/lab/artifacts/store.ts +310 -0
  277. package/src/lab/automation/budgets.ts +78 -0
  278. package/src/lab/automation/config-persistence.ts +256 -0
  279. package/src/lab/automation/constants.ts +39 -0
  280. package/src/lab/automation/cooldown.ts +103 -0
  281. package/src/lab/automation/dispatch.ts +211 -0
  282. package/src/lab/automation/index.ts +13 -0
  283. package/src/lab/automation/orchestrator.ts +499 -0
  284. package/src/lab/automation/persistence.ts +512 -0
  285. package/src/lab/automation/planner.ts +371 -0
  286. package/src/lab/automation/policy.ts +136 -0
  287. package/src/lab/automation/queue.ts +191 -0
  288. package/src/lab/automation/recovery.ts +24 -0
  289. package/src/lab/automation/route-context.ts +21 -0
  290. package/src/lab/automation/run-key.ts +44 -0
  291. package/src/lab/automation/runs-query.ts +34 -0
  292. package/src/lab/automation/types.ts +160 -0
  293. package/src/lab/conformance/assertion.ts +325 -0
  294. package/src/lab/conformance/digest.ts +22 -0
  295. package/src/lab/conformance/executor.ts +741 -0
  296. package/src/lab/conformance/fixture-provider.ts +27 -0
  297. package/src/lab/conformance/fixtures/live-v1-cases.json +175 -0
  298. package/src/lab/conformance/fixtures/protocol-v1-cases.json +461 -0
  299. package/src/lab/conformance/harness-budget.ts +47 -0
  300. package/src/lab/conformance/index.ts +5 -0
  301. package/src/lab/conformance/jcs.ts +64 -0
  302. package/src/lab/conformance/json-pointer.ts +39 -0
  303. package/src/lab/conformance/manifest.ts +180 -0
  304. package/src/lab/conformance/mcp-stub.ts +179 -0
  305. package/src/lab/conformance/negative-controls.ts +164 -0
  306. package/src/lab/conformance/observation.ts +355 -0
  307. package/src/lab/conformance/runner.ts +68 -0
  308. package/src/lab/conformance/sse-normalize.ts +59 -0
  309. package/src/lab/conformance/suite-manifest.ts +78 -0
  310. package/src/lab/conformance/types.ts +214 -0
  311. package/src/lab/constants.ts +126 -0
  312. package/src/lab/digest.ts +64 -0
  313. package/src/lab/events/errors.ts +9 -0
  314. package/src/lab/events/limits.ts +117 -0
  315. package/src/lab/events/types.ts +229 -0
  316. package/src/lab/events/validate.ts +781 -0
  317. package/src/lab/fabric/constants.ts +40 -0
  318. package/src/lab/fabric/executor.ts +492 -0
  319. package/src/lab/fabric/index.ts +80 -0
  320. package/src/lab/fabric/manifest.ts +222 -0
  321. package/src/lab/fabric/observe.ts +489 -0
  322. package/src/lab/fabric/patch.ts +79 -0
  323. package/src/lab/fabric/producer-child.ts +139 -0
  324. package/src/lab/fabric/producer-isolate.ts +276 -0
  325. package/src/lab/fabric/producer-protocol.ts +61 -0
  326. package/src/lab/fabric/scratch.ts +439 -0
  327. package/src/lab/fabric/subject.ts +106 -0
  328. package/src/lab/fabric/types.ts +134 -0
  329. package/src/lab/fabric/verifier.ts +98 -0
  330. package/src/lab/index.ts +54 -0
  331. package/src/lab/ledger/artifact-refs.ts +127 -0
  332. package/src/lab/ledger/invalidation.ts +136 -0
  333. package/src/lab/ledger/purge.ts +310 -0
  334. package/src/lab/ledger/store.ts +532 -0
  335. package/src/lab/live/credential-lease.ts +53 -0
  336. package/src/lab/live/destination.ts +155 -0
  337. package/src/lab/live/executor.ts +336 -0
  338. package/src/lab/live/inert-tools.ts +56 -0
  339. package/src/lab/live/manifest.ts +85 -0
  340. package/src/lab/live/mcp-loopback.ts +57 -0
  341. package/src/lab/live/runner.ts +19 -0
  342. package/src/lab/live/sandbox.ts +61 -0
  343. package/src/lab/live/suite-manifest.ts +41 -0
  344. package/src/lab/live/transport.ts +118 -0
  345. package/src/lab/live/types.ts +197 -0
  346. package/src/lab/observe/from-conformance.ts +301 -0
  347. package/src/lab/observe/from-live.ts +117 -0
  348. package/src/lab/paths.ts +153 -0
  349. package/src/lab/projection/rebuild.ts +495 -0
  350. package/src/lab/projection/schema.ts +135 -0
  351. package/src/lab/projection/verdicts.ts +474 -0
  352. package/src/lab/projection/verification.ts +412 -0
  353. package/src/lab/public/bundle.ts +217 -0
  354. package/src/lab/public/community-authority.ts +175 -0
  355. package/src/lab/public/community-files.ts +29 -0
  356. package/src/lab/public/community.ts +479 -0
  357. package/src/lab/public/file-safety.ts +155 -0
  358. package/src/lab/public/ids.ts +26 -0
  359. package/src/lab/public/index.ts +16 -0
  360. package/src/lab/public/mutation-lock.ts +424 -0
  361. package/src/lab/public/operator.ts +353 -0
  362. package/src/lab/public/origin-purge.ts +79 -0
  363. package/src/lab/public/origin.ts +203 -0
  364. package/src/lab/public/privacy.ts +143 -0
  365. package/src/lab/public/private-file.ts +261 -0
  366. package/src/lab/public/project.ts +124 -0
  367. package/src/lab/public/purge-test-fault.ts +21 -0
  368. package/src/lab/public/purge.ts +223 -0
  369. package/src/lab/public/registry.ts +44 -0
  370. package/src/lab/public/revocation.ts +252 -0
  371. package/src/lab/public/signature.ts +243 -0
  372. package/src/lab/public/storage.ts +105 -0
  373. package/src/lab/public/strict-json.ts +206 -0
  374. package/src/lab/public/time.ts +26 -0
  375. package/src/lab/public/types.ts +172 -0
  376. package/src/lab/public/validate.ts +391 -0
  377. package/src/lab/query/catalog.ts +101 -0
  378. package/src/lab/query/connection.ts +107 -0
  379. package/src/lab/query/constants.ts +4 -0
  380. package/src/lab/query/cursor.ts +132 -0
  381. package/src/lab/query/dto-map.ts +277 -0
  382. package/src/lab/query/errors.ts +22 -0
  383. package/src/lab/query/freshness.ts +53 -0
  384. package/src/lab/query/index.ts +45 -0
  385. package/src/lab/query/latest-observation.ts +59 -0
  386. package/src/lab/query/passive-production.ts +159 -0
  387. package/src/lab/query/queries.ts +444 -0
  388. package/src/lab/query/types.ts +266 -0
  389. package/src/lab/subject/behavior-fingerprint.ts +77 -0
  390. package/src/lab/subject/installation-salt.ts +112 -0
  391. package/src/lab/subject/protocol-subject.ts +80 -0
  392. package/src/lab/subject/route-subject.ts +74 -0
  393. package/src/lib/app-owned-memory-stores.ts +22 -0
  394. package/src/lib/bounded-body.ts +153 -9
  395. package/src/lib/bun-runtime.ts +125 -12
  396. package/src/lib/bun-stream-caps.ts +13 -9
  397. package/src/lib/codex-restart-contract.ts +120 -0
  398. package/src/lib/config-ownership.ts +6 -2
  399. package/src/lib/destination-policy.ts +65 -1
  400. package/src/lib/errors.ts +44 -2
  401. package/src/lib/fabric-task-execution-authority.ts +7 -0
  402. package/src/lib/fabric-task-host.ts +29 -0
  403. package/src/lib/lab-activation.ts +223 -0
  404. package/src/lib/lab-live-execution-authority.ts +13 -0
  405. package/src/lib/lab-live-host.ts +30 -0
  406. package/src/lib/lab-live-pinned-sender.ts +56 -0
  407. package/src/lib/lab-live-route-production.ts +130 -0
  408. package/src/lib/lab-passive-linker-registration.ts +26 -0
  409. package/src/lib/local-management-attestation.ts +51 -0
  410. package/src/lib/local-management-capability.ts +100 -0
  411. package/src/lib/local-provider-reload-contract.ts +100 -0
  412. package/src/lib/optional-shutdown-hooks.ts +57 -0
  413. package/src/lib/pinned-http.ts +145 -26
  414. package/src/lib/process-control.ts +4 -1
  415. package/src/lib/provider-outbound.ts +49 -9
  416. package/src/lib/redact.ts +419 -3
  417. package/src/lib/self-launch-argv.ts +15 -0
  418. package/src/lib/server-resource-ownership.ts +71 -0
  419. package/src/lib/shadow-call.ts +35 -4
  420. package/src/lib/sse-decoder.ts +41 -0
  421. package/src/lib/state-store-registrations.ts +10 -2
  422. package/src/lib/system-restart-contract.ts +73 -0
  423. package/src/lib/token-estimate.ts +19 -2
  424. package/src/lib/tool-argument-integers.ts +202 -0
  425. package/src/lib/translator-budget.ts +44 -0
  426. package/src/lib/upstream-http-version.ts +57 -0
  427. package/src/lib/upstream-reachability.ts +95 -0
  428. package/src/lib/upstream-retry.ts +156 -3
  429. package/src/lib/windows-atomic-replace.ts +155 -0
  430. package/src/lib/windows-elevation.ts +70 -2
  431. package/src/lib/windows-secret-acl.ts +409 -69
  432. package/src/lib/windows-service-wrappers.ts +72 -0
  433. package/src/lib/windows-text.ts +106 -0
  434. package/src/lib/windows-user-principal.ts +341 -0
  435. package/src/lib/winsw.ts +33 -5
  436. package/src/oauth/account-import/google-antigravity-adapter.ts +74 -0
  437. package/src/oauth/account-import/index.ts +15 -0
  438. package/src/oauth/account-import/parser.ts +83 -0
  439. package/src/oauth/account-import/registry.ts +18 -0
  440. package/src/oauth/account-import/service.ts +75 -0
  441. package/src/oauth/account-import/types.ts +91 -0
  442. package/src/oauth/anthropic.ts +12 -1
  443. package/src/oauth/callback-server.ts +8 -2
  444. package/src/oauth/chatgpt.ts +12 -1
  445. package/src/oauth/command-code.ts +239 -0
  446. package/src/oauth/cursor.ts +46 -5
  447. package/src/oauth/google-antigravity.ts +35 -3
  448. package/src/oauth/health.ts +20 -12
  449. package/src/oauth/index.ts +398 -66
  450. package/src/oauth/key-providers.ts +16 -0
  451. package/src/oauth/kimi.ts +16 -2
  452. package/src/oauth/kiro.ts +50 -6
  453. package/src/oauth/local-token-detect.ts +11 -2
  454. package/src/oauth/log.ts +3 -1
  455. package/src/oauth/login-cli.ts +88 -28
  456. package/src/oauth/nous.ts +798 -0
  457. package/src/oauth/store.ts +119 -21
  458. package/src/oauth/token-guardian.ts +9 -3
  459. package/src/pi/models.ts +2 -2
  460. package/src/providers/alibaba-region-migration.ts +1 -1
  461. package/src/providers/antigravity-models.ts +521 -31
  462. package/src/providers/base-url-choices.ts +10 -0
  463. package/src/providers/codex-capacity.ts +292 -0
  464. package/src/providers/command-code-efforts.ts +144 -0
  465. package/src/providers/context-cap.ts +22 -5
  466. package/src/providers/cursor-pool.ts +72 -0
  467. package/src/providers/derive.ts +253 -6
  468. package/src/providers/fastwire.ts +501 -0
  469. package/src/providers/free-directory.ts +10 -7
  470. package/src/providers/google-vertex-location.ts +14 -0
  471. package/src/providers/key-failover.ts +71 -3
  472. package/src/providers/label.ts +1 -1
  473. package/src/providers/model-discovery-limits.ts +16 -0
  474. package/src/providers/model-discovery.ts +115 -22
  475. package/src/providers/model-rename-migration.ts +255 -0
  476. package/src/providers/model-rename-startup.ts +28 -0
  477. package/src/providers/openai-sidecar.ts +72 -4
  478. package/src/providers/openai-tier-startup.ts +31 -2
  479. package/src/providers/openai-tiers.ts +119 -4
  480. package/src/providers/openai-virtual-models.ts +1 -0
  481. package/src/providers/opencode-zen-rate-limit.ts +102 -0
  482. package/src/providers/provider-id-rewrite.ts +29 -0
  483. package/src/providers/quota.ts +1319 -38
  484. package/src/providers/registry.ts +1429 -111
  485. package/src/providers/request-pacing.ts +310 -0
  486. package/src/providers/service-tier.ts +277 -0
  487. package/src/providers/slug-codec.ts +42 -6
  488. package/src/providers/static-model-discovery.ts +86 -0
  489. package/src/providers/xai-responses-opt-in.ts +15 -0
  490. package/src/providers/xai-transport.ts +11 -4
  491. package/src/reasoning-effort.ts +49 -1
  492. package/src/responses/compaction.ts +26 -1
  493. package/src/responses/custom-tool-compat.ts +266 -0
  494. package/src/responses/hosted-tool-policy.ts +9 -0
  495. package/src/responses/namespace-tool-compat.ts +355 -0
  496. package/src/responses/parser.ts +220 -38
  497. package/src/responses/provider-continuation.ts +98 -0
  498. package/src/responses/provider-opaque-metadata.ts +73 -0
  499. package/src/responses/reasoning-envelope.ts +9 -1
  500. package/src/responses/reasoning-replay-cache.ts +426 -0
  501. package/src/responses/schema.ts +7 -1
  502. package/src/responses/spill-store.ts +75 -10
  503. package/src/responses/state.ts +565 -27
  504. package/src/responses/thought-signature-replay.ts +347 -0
  505. package/src/responses/tool-search-compat.ts +301 -0
  506. package/src/responses/truncated-stop-reason.ts +60 -0
  507. package/src/router.ts +366 -30
  508. package/src/routing/analytics.ts +378 -0
  509. package/src/routing/capability.ts +244 -0
  510. package/src/routing/compatibility/assemble.ts +73 -0
  511. package/src/routing/compatibility/behavior.ts +278 -0
  512. package/src/routing/compatibility/catalog.ts +99 -0
  513. package/src/routing/compatibility/endpoint.ts +52 -0
  514. package/src/routing/compatibility/lab-evidence-provider.ts +130 -0
  515. package/src/routing/compatibility/policy.ts +181 -0
  516. package/src/routing/compatibility/provider-slot.ts +56 -0
  517. package/src/routing/compatibility/reader.ts +110 -0
  518. package/src/routing/compatibility/subject.ts +191 -0
  519. package/src/routing/compatibility/types.ts +64 -0
  520. package/src/routing/compatibility/version.ts +104 -0
  521. package/src/routing/cost.ts +77 -0
  522. package/src/routing/evaluator.ts +495 -0
  523. package/src/routing/health.ts +412 -0
  524. package/src/routing/history/cursor.ts +43 -0
  525. package/src/routing/history/indexer.ts +605 -0
  526. package/src/routing/history/schema.ts +72 -0
  527. package/src/routing/profile-namespace.ts +15 -0
  528. package/src/routing/profile.ts +547 -0
  529. package/src/routing/quota.ts +145 -0
  530. package/src/routing/request-evidence.ts +45 -0
  531. package/src/routing/trace.ts +776 -0
  532. package/src/server/adapter-resolve.ts +2 -29
  533. package/src/server/auth-cors.ts +267 -46
  534. package/src/server/background-lifecycle.ts +182 -0
  535. package/src/server/chat-completions.ts +130 -56
  536. package/src/server/chat-native-sse.ts +331 -0
  537. package/src/server/chat-native.ts +426 -0
  538. package/src/server/claude-messages.ts +159 -43
  539. package/src/server/direct-local-http.ts +347 -0
  540. package/src/server/effort-policy.ts +18 -0
  541. package/src/server/github-copilot-responses-repair.ts +338 -0
  542. package/src/server/gui-static.ts +39 -10
  543. package/src/server/images.ts +94 -12
  544. package/src/server/index.ts +865 -181
  545. package/src/server/lifecycle.ts +284 -13
  546. package/src/server/live.ts +136 -17
  547. package/src/server/local-management-read-client.ts +90 -0
  548. package/src/server/local-provider-reload-client.ts +137 -0
  549. package/src/server/management/agent-settings-routes.ts +398 -103
  550. package/src/server/management/api-key-usage.ts +31 -5
  551. package/src/server/management/body.ts +6 -0
  552. package/src/server/management/combo-routes.ts +62 -24
  553. package/src/server/management/config-routes.ts +464 -51
  554. package/src/server/management/context.ts +80 -2
  555. package/src/server/management/integration-routes.ts +498 -0
  556. package/src/server/management/lab-automation-routes.ts +206 -0
  557. package/src/server/management/lab-routes.ts +563 -0
  558. package/src/server/management/logs-usage-routes.ts +101 -32
  559. package/src/server/management/model-routes.ts +189 -131
  560. package/src/server/management/model-rows.ts +163 -0
  561. package/src/server/management/native-integration-routes.ts +769 -0
  562. package/src/server/management/oauth-account-routes.ts +80 -4
  563. package/src/server/management/provider-capability-config.ts +48 -0
  564. package/src/server/management/provider-routes.ts +764 -157
  565. package/src/server/management/request-history-routes.ts +191 -0
  566. package/src/server/management/routing-analytics-routes.ts +74 -0
  567. package/src/server/management/routing-profile-routes.ts +380 -0
  568. package/src/server/management/shared.ts +27 -11
  569. package/src/server/management/sidebar-routes.ts +47 -31
  570. package/src/server/management/storage-log-guard-routes.ts +186 -0
  571. package/src/server/management/sync-response.ts +69 -0
  572. package/src/server/management/system-restart.ts +288 -32
  573. package/src/server/management/system-routes.ts +77 -0
  574. package/src/server/management/usage-summary-cache.ts +9 -1
  575. package/src/server/management/vision-sidecar-options.ts +167 -0
  576. package/src/server/management/web-search-sidecar-options.ts +120 -0
  577. package/src/server/management-api.ts +115 -14
  578. package/src/server/management-auth.ts +220 -5
  579. package/src/server/passive-route-linker.ts +66 -0
  580. package/src/server/ports.ts +41 -1
  581. package/src/server/proxy-liveness.ts +132 -5
  582. package/src/server/readiness.ts +99 -0
  583. package/src/server/relay-eager.ts +82 -42
  584. package/src/server/relay.ts +236 -76
  585. package/src/server/request-decompress.ts +113 -6
  586. package/src/server/request-log.ts +235 -22
  587. package/src/server/responses/agent-task-recovery-cache.ts +143 -0
  588. package/src/server/responses/agent-task-recovery.ts +465 -0
  589. package/src/server/responses/collaboration.ts +204 -35
  590. package/src/server/responses/compact.ts +442 -55
  591. package/src/server/responses/core.ts +2872 -331
  592. package/src/server/responses/empty-completion-guard.ts +276 -0
  593. package/src/server/responses/encrypted-payload.ts +62 -39
  594. package/src/server/responses/fetch-helpers.ts +79 -4
  595. package/src/server/responses/input-admission.ts +185 -0
  596. package/src/server/responses/pacing-overload.ts +13 -0
  597. package/src/server/responses/policy-fallback.ts +178 -0
  598. package/src/server/responses/responses-field-backfill.ts +251 -0
  599. package/src/server/responses/terminal-guard.ts +26 -5
  600. package/src/server/responses/upstream-error.ts +5 -0
  601. package/src/server/responses/ws-upstream.ts +308 -0
  602. package/src/server/responses-custom-tool-repair.ts +282 -0
  603. package/src/server/responses-item-id-repair.ts +54 -6
  604. package/src/server/responses-json-events.ts +90 -0
  605. package/src/server/responses-model-rewrite.ts +29 -0
  606. package/src/server/responses-reasoning-summary-rewrite.ts +178 -0
  607. package/src/server/responses-snapshot-repair.ts +621 -0
  608. package/src/server/responses-terminal-repair.ts +342 -0
  609. package/src/server/responses-tool-search-repair.ts +267 -0
  610. package/src/server/responses-undeclared-tool-guard.ts +153 -0
  611. package/src/server/responses.ts +18 -2
  612. package/src/server/search.ts +78 -13
  613. package/src/server/sse-frame-buffer.ts +292 -0
  614. package/src/server/sse-payload-rewrite.ts +110 -22
  615. package/src/server/startup-action-control.ts +8 -1
  616. package/src/server/startup-health-cache.ts +19 -1
  617. package/src/server/system-env.ts +80 -9
  618. package/src/server/ws-bridge.ts +39 -38
  619. package/src/service-manager-probe.ts +892 -0
  620. package/src/service.ts +1111 -90
  621. package/src/sidecar/auth.ts +92 -0
  622. package/src/sidecar/candidates.ts +83 -0
  623. package/src/storage/cleanup.ts +2 -2
  624. package/src/storage/scanner.ts +1 -1
  625. package/src/storage/worker-lifecycle.ts +14 -14
  626. package/src/tray/windows-tray.ps1 +83 -9
  627. package/src/tray/windows.ts +43 -16
  628. package/src/types/accounts.ts +37 -0
  629. package/src/types/config.ts +845 -0
  630. package/src/types/provider.ts +545 -0
  631. package/src/types/request.ts +384 -0
  632. package/src/types/tools.ts +131 -0
  633. package/src/types/wire.ts +80 -0
  634. package/src/types.ts +104 -1236
  635. package/src/update/index.ts +32 -19
  636. package/src/update/job.ts +442 -67
  637. package/src/update/notify.ts +12 -6
  638. package/src/update/npm-cache-preflight.d.mts +47 -0
  639. package/src/update/npm-cache-preflight.mjs +201 -0
  640. package/src/update/transactional-install.d.mts +22 -0
  641. package/src/update/transactional-install.mjs +259 -0
  642. package/src/usage/cost.ts +0 -0
  643. package/src/usage/expected-prices.ts +268 -16
  644. package/src/usage/log.ts +606 -41
  645. package/src/usage/summary.ts +177 -9
  646. package/src/usage/user-cost-overlay-reconciler.ts +313 -0
  647. package/src/usage/user-cost-overlays.ts +314 -0
  648. package/src/vision/anthropic-describe.ts +10 -6
  649. package/src/vision/backends.ts +97 -0
  650. package/src/vision/describe.ts +9 -3
  651. package/src/vision/eligibility.ts +250 -0
  652. package/src/vision/index.ts +238 -24
  653. package/src/vision/reasoning.ts +55 -0
  654. package/src/vision/routed-describe.ts +175 -0
  655. package/src/vision/timeout-bounds.ts +9 -0
  656. package/src/web-search/anthropic-executor.ts +13 -7
  657. package/src/web-search/backends.ts +108 -0
  658. package/src/web-search/exa-executor.ts +88 -0
  659. package/src/web-search/executor.ts +11 -3
  660. package/src/web-search/gemini-executor.ts +141 -0
  661. package/src/web-search/index.ts +150 -15
  662. package/src/web-search/loop.ts +279 -50
  663. package/src/web-search/parse.ts +125 -30
  664. package/src/web-search/sources.ts +60 -0
  665. package/src/web-search/xai-executor.ts +219 -0
  666. package/gui/dist/assets/index-DTpMHS4F.js +0 -67
  667. package/gui/dist/assets/index-ZNVDE3C7.css +0 -1
  668. package/gui/dist/provider-icons/antigravity.svg +0 -1
  669. package/gui/dist/provider-icons/claude.svg +0 -1
  670. package/gui/dist/provider-icons/copilot.svg +0 -1
  671. package/gui/dist/provider-icons/cursor.svg +0 -2
  672. package/gui/dist/provider-icons/gemini.svg +0 -1
  673. package/gui/dist/provider-icons/grok-color.svg +0 -1
  674. package/gui/dist/provider-icons/kiro.svg +0 -14
  675. package/src/cli/internal-dispatch.ts +0 -20
@@ -0,0 +1,205 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import { existsSync, readdirSync, readFileSync } from "node:fs";
3
+
4
+ import {
5
+ listWindowsSnapshots,
6
+ tokenizeCommandLine,
7
+ type ProcessSnapshot,
8
+ } from "../app-server-processes";
9
+
10
+ export interface CodexWriterProcess {
11
+ pid: number;
12
+ commandLine: string;
13
+ }
14
+
15
+ export type CodexWriterProcessCheck =
16
+ | { state: "ok"; processes: CodexWriterProcess[] }
17
+ | { state: "unknown"; reason: "enumeration_failed" };
18
+
19
+ export interface CodexWriterProcessIo {
20
+ platform?: NodeJS.Platform;
21
+ getuid?: () => number | undefined;
22
+ listSnapshots?: () => ProcessSnapshot[];
23
+ }
24
+
25
+ const TARGET_TRIPLE = /^[a-z0-9_]+-[a-z0-9_]+-[a-z0-9_]+(?:-[a-z0-9_]+)?$/i;
26
+
27
+ function basename(token: string): string {
28
+ return token.replace(/\\/g, "/").split("/").pop()?.toLowerCase() ?? "";
29
+ }
30
+
31
+ function isOfficialCodexExecutable(token: string): boolean {
32
+ const base = basename(token);
33
+ if (base === "codex" || base === "codex.exe" || base === "codex.cmd") return true;
34
+ const withoutSuffix = base.replace(/\.(?:exe|cmd)$/i, "");
35
+ if (!withoutSuffix.startsWith("codex-")) return false;
36
+ return TARGET_TRIPLE.test(withoutSuffix.slice("codex-".length));
37
+ }
38
+
39
+ function isCodeModeHostExecutable(token: string): boolean {
40
+ const base = basename(token);
41
+ return base === "codex-code-mode-host" || base === "codex-code-mode-host.exe";
42
+ }
43
+
44
+ function isInterpreterExecutable(token: string): boolean {
45
+ const base = basename(token);
46
+ return base === "node" || base === "node.exe"
47
+ || base === "bun" || base === "bun.exe"
48
+ || base === "deno" || base === "deno.exe";
49
+ }
50
+
51
+ function looksLikePathPrefix(token: string): boolean {
52
+ return token.startsWith("/") || token.startsWith("./") || token.startsWith("../")
53
+ || token.startsWith("~") || /^[a-z]:[\\/]/i.test(token);
54
+ }
55
+
56
+ function flattenedPathPrefix(
57
+ commandLine: string,
58
+ predicate: (candidate: string) => boolean,
59
+ ): { executable: string; remainder: string } | null {
60
+ const parts = commandLine.trim().split(/\s+/).filter(Boolean);
61
+ for (let end = parts.length; end >= 1; end -= 1) {
62
+ const executable = parts.slice(0, end).join(" ");
63
+ if (!predicate(executable)) continue;
64
+ return { executable, remainder: parts.slice(end).join(" ") };
65
+ }
66
+ return null;
67
+ }
68
+
69
+ function interpreterHostWithFlattenedPath(commandLine: string, tokens: readonly string[]): boolean {
70
+ if (tokens.length < 2 || !isInterpreterExecutable(tokens[0]!)) return false;
71
+ const trimmed = commandLine.trim();
72
+ const firstWhitespace = trimmed.search(/\s/);
73
+ if (firstWhitespace < 0) return false;
74
+ const remainder = trimmed.slice(firstWhitespace).trim();
75
+ return flattenedPathPrefix(remainder, isCodeModeHostExecutable) !== null;
76
+ }
77
+
78
+ /**
79
+ * Match official Codex writer executables at argv0 plus the repository's
80
+ * established interpreter-entrypoint form for code-mode-host.
81
+ *
82
+ * OS process listings can flatten argv into a display string. When an unquoted
83
+ * executable path contains spaces, recover only a leading path-shaped executable
84
+ * prefix; arbitrary later argv tokens still never turn a process into Codex.
85
+ */
86
+ export function isCodexWriterCommandLine(commandLine: string, executable?: string): boolean {
87
+ if (executable && (isOfficialCodexExecutable(executable) || isCodeModeHostExecutable(executable))) return true;
88
+ const tokens = tokenizeCommandLine(commandLine.trim());
89
+ if (tokens.length === 0) return false;
90
+ if (isOfficialCodexExecutable(tokens[0]!) || isCodeModeHostExecutable(tokens[0]!)) return true;
91
+ return tokens.length > 1
92
+ && isInterpreterExecutable(tokens[0]!)
93
+ && isCodeModeHostExecutable(tokens[1]!);
94
+ }
95
+
96
+ function statusUid(status: string): number | undefined {
97
+ const match = /^Uid:\s+(\d+)/m.exec(status);
98
+ if (!match) return undefined;
99
+ const value = Number(match[1]);
100
+ return Number.isSafeInteger(value) ? value : undefined;
101
+ }
102
+
103
+ function listLinuxSnapshots(uid: number | undefined): ProcessSnapshot[] {
104
+ if (!existsSync("/proc")) throw new Error("procfs_unavailable");
105
+ const rows: ProcessSnapshot[] = [];
106
+ for (const entry of readdirSync("/proc")) {
107
+ if (!/^\d+$/.test(entry)) continue;
108
+ const pid = Number(entry);
109
+ if (!Number.isSafeInteger(pid) || pid <= 0) continue;
110
+ try {
111
+ const procUid = statusUid(readFileSync(`/proc/${pid}/status`, "utf8"));
112
+ if (uid !== undefined && procUid !== undefined && procUid !== uid) continue;
113
+ const argv = readFileSync(`/proc/${pid}/cmdline`)
114
+ .toString("utf8")
115
+ .split("\0")
116
+ .filter(Boolean);
117
+ const commandLine = argv.join(" ").trim();
118
+ if (commandLine) rows.push({ pid, commandLine, executable: argv[0], uid: procUid });
119
+ } catch {
120
+ // A process disappearing mid-enumeration is normal. A top-level procfs
121
+ // failure is handled before the loop and fails closed.
122
+ }
123
+ }
124
+ return rows;
125
+ }
126
+
127
+ function listDarwinSnapshots(uid: number | undefined): ProcessSnapshot[] {
128
+ const commandOutput = uid !== undefined
129
+ ? execFileSync("/bin/ps", ["-u", String(uid), "-o", "pid=,command="], {
130
+ encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 5_000,
131
+ })
132
+ : execFileSync("/bin/ps", ["-axo", "pid=,uid=,command="], {
133
+ encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 5_000,
134
+ });
135
+ const executableOutput = uid !== undefined
136
+ ? execFileSync("/bin/ps", ["-u", String(uid), "-o", "pid=,comm="], {
137
+ encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 5_000,
138
+ })
139
+ : execFileSync("/bin/ps", ["-axo", "pid=,comm="], {
140
+ encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 5_000,
141
+ });
142
+ const executableByPid = new Map<number, string>();
143
+ for (const raw of executableOutput.split(/\r?\n/)) {
144
+ const match = /^\s*(\d+)\s+(.+)$/.exec(raw);
145
+ if (!match) continue;
146
+ const pid = Number(match[1]);
147
+ const executable = match[2]?.trim() ?? "";
148
+ if (Number.isSafeInteger(pid) && pid > 0 && executable) executableByPid.set(pid, executable);
149
+ }
150
+ const rows: ProcessSnapshot[] = [];
151
+ for (const raw of commandOutput.split(/\r?\n/)) {
152
+ const line = raw.trim();
153
+ if (!line) continue;
154
+ const match = uid !== undefined
155
+ ? /^(\d+)\s+(.*)$/.exec(line)
156
+ : /^(\d+)\s+(\d+)\s+(.*)$/.exec(line);
157
+ if (!match) continue;
158
+ const pid = Number(match[1]);
159
+ const commandLine = (uid !== undefined ? match[2] : match[3])?.trim() ?? "";
160
+ if (!Number.isSafeInteger(pid) || pid <= 0 || !commandLine) continue;
161
+ rows.push({
162
+ pid, commandLine, executable: executableByPid.get(pid),
163
+ uid: uid ?? (Number.isSafeInteger(Number(match[2])) ? Number(match[2]) : undefined),
164
+ });
165
+ }
166
+ return rows;
167
+ }
168
+
169
+ function effectiveUid(getuid?: () => number | undefined): number | undefined {
170
+ try {
171
+ return getuid ? getuid() : process.getuid?.();
172
+ } catch {
173
+ return undefined;
174
+ }
175
+ }
176
+
177
+ function defaultSnapshots(platform: NodeJS.Platform, uid: number | undefined): ProcessSnapshot[] {
178
+ if (platform === "win32") return listWindowsSnapshots();
179
+ if (platform === "darwin") return listDarwinSnapshots(uid);
180
+ if (platform === "linux") return listLinuxSnapshots(uid);
181
+ throw new Error("unsupported_process_enumeration_platform");
182
+ }
183
+
184
+ export function listRunningCodexProcesses(io: CodexWriterProcessIo = {}): CodexWriterProcessCheck {
185
+ const platform = io.platform ?? process.platform;
186
+ let snapshots: ProcessSnapshot[];
187
+ try {
188
+ snapshots = io.listSnapshots?.() ?? defaultSnapshots(platform, effectiveUid(io.getuid));
189
+ } catch {
190
+ return { state: "unknown", reason: "enumeration_failed" };
191
+ }
192
+
193
+ const byPid = new Map<number, CodexWriterProcess>();
194
+ for (const snapshot of snapshots) {
195
+ if (!Number.isSafeInteger(snapshot.pid) || snapshot.pid <= 0) continue;
196
+ if (!isCodexWriterCommandLine(snapshot.commandLine, snapshot.executable)) continue;
197
+ if (!byPid.has(snapshot.pid)) {
198
+ byPid.set(snapshot.pid, { pid: snapshot.pid, commandLine: snapshot.commandLine });
199
+ }
200
+ }
201
+ return {
202
+ state: "ok",
203
+ processes: [...byPid.values()].sort((a, b) => a.pid - b.pid),
204
+ };
205
+ }
@@ -0,0 +1,489 @@
1
+ import { lstatSync, realpathSync } from "node:fs";
2
+ import { pathToFileURL } from "node:url";
3
+ import { Database, constants as sqliteConstants } from "bun:sqlite";
4
+
5
+ import { getCodexHome, resolveCodexLogsDbPath } from "../paths";
6
+ import { hasCurrentLogsSchema, inspectCodexLogs, type CodexLogGuardInspection } from "./inspect";
7
+ import { withCodexLogGuardLock, type CodexLogGuardLockOutcome } from "./lock";
8
+ import { sameLogGuardPathIdentity } from "./path-safety";
9
+ import { isSqliteBusy } from "./sqlite-errors";
10
+ import {
11
+ readCodexLogGuardMode,
12
+ writeCodexLogGuardMode,
13
+ type CodexLogGuardMode,
14
+ } from "./policy";
15
+ import {
16
+ listRunningCodexProcesses,
17
+ type CodexWriterProcessCheck,
18
+ } from "./processes";
19
+
20
+ export { type CodexLogGuardMode } from "./policy";
21
+
22
+ const IMMUTABLE_READONLY_FLAGS = sqliteConstants.SQLITE_OPEN_READONLY | sqliteConstants.SQLITE_OPEN_URI;
23
+ const COMPAT_TRIGGER = "opencodex_log_guard_compat_v1";
24
+ const QUIET_TRIGGER = "opencodex_log_guard_quiet_v1";
25
+ const OWNED_TRIGGER_NAMES = [COMPAT_TRIGGER, QUIET_TRIGGER] as const;
26
+
27
+ const CURRENT_LOG_COLUMNS = [
28
+ "id",
29
+ "ts",
30
+ "ts_nanos",
31
+ "level",
32
+ "target",
33
+ "feedback_log_body",
34
+ "module_path",
35
+ "file",
36
+ "line",
37
+ "thread_id",
38
+ "process_uuid",
39
+ "estimated_bytes",
40
+ ] as const;
41
+
42
+ /**
43
+ * Versioned compatibility policy pinned to the current upstream Codex persistent
44
+ * log filters researched for Log Guard v1. It intentionally preserves unrelated
45
+ * TRACE rows rather than assuming all TRACE diagnostics are disposable.
46
+ */
47
+ /**
48
+ * Upstream configures these filters with `Targets::with_target`, which matches a
49
+ * target and every module path BENEATH it: `hyper_util` also covers
50
+ * `hyper_util::client::legacy::pool`, and `codex_api::sse` also covers
51
+ * `codex_api::sse::responses`. Exact equality reproduced only the parent, so the
52
+ * high-volume child targets — the ones that actually fill the database — kept
53
+ * writing while compat mode reported itself active.
54
+ *
55
+ * The comparison uses `substr`, not `LIKE`. SQLite's `LIKE` is ASCII
56
+ * case-insensitive by default, so a `LIKE` form would also suppress
57
+ * `HYPER_UTIL::child` — Rust target paths are case-sensitive, and silently
58
+ * dropping a differently-cased target is a wrong answer, not a safe default.
59
+ * `substr(NEW.target, 1, N) = 'X::'` is a plain case-sensitive comparison with
60
+ * no wildcard metacharacters to escape, which also removes the `_`/`%` hazard
61
+ * that `LIKE` would have required an ESCAPE clause to contain.
62
+ *
63
+ * The `'::'` boundary is deliberate and NARROWER than upstream's raw prefix
64
+ * rule: `Targets::with_target("hyper_util")` would also match a sibling crate
65
+ * named `hyper_utilities`. Suppressing an unrelated crate's logs is worse for
66
+ * a guard that silently discards rows, so this matches the module-descendant
67
+ * relation instead. The existing regression pins `hyper_utilities` as
68
+ * preserved.
69
+ *
70
+ * `opentelemetry_sdk` stays exact because upstream registers it with exact
71
+ * equality rather than a prefix filter.
72
+ */
73
+ function targetOrDescendant(target: string): string {
74
+ const prefix = `${target}::`;
75
+ return `(NEW.target = '${target}' OR substr(NEW.target, 1, ${prefix.length}) = '${prefix}')`;
76
+ }
77
+
78
+ function anyTargetOrDescendant(targets: readonly string[]): string {
79
+ return `(${targets.map(targetOrDescendant).join(" OR ")})`;
80
+ }
81
+
82
+ const COMPAT_TRIGGER_SQL = `CREATE TRIGGER ${COMPAT_TRIGGER}
83
+ BEFORE INSERT ON logs
84
+ WHEN
85
+ NEW.target = 'log'
86
+ OR NEW.target = 'codex_otel.log_only'
87
+ OR NEW.target = 'codex_otel.trace_safe'
88
+ OR NEW.target = 'codex_api::responses_websocket_timing'
89
+ OR NEW.target = 'codex_core::post_sampling_token_estimate'
90
+ OR (${targetOrDescendant("hyper_util")} AND upper(NEW.level) IN ('TRACE', 'DEBUG', 'INFO'))
91
+ OR (${anyTargetOrDescendant(["codex_rmcp_client", "rmcp"])} AND upper(NEW.level) IN ('TRACE', 'DEBUG'))
92
+ OR (${anyTargetOrDescendant([
93
+ "codex_http_client::transport",
94
+ "codex_api::sse",
95
+ "codex_tui::streaming::controller",
96
+ "codex_tui::streaming::table_holdback",
97
+ ])} AND upper(NEW.level) = 'TRACE')
98
+ OR (NEW.target = 'opentelemetry_sdk' AND upper(NEW.level) IN ('TRACE', 'DEBUG'))
99
+ BEGIN
100
+ SELECT RAISE(IGNORE);
101
+ END`;
102
+
103
+ const QUIET_TRIGGER_SQL = `CREATE TRIGGER ${QUIET_TRIGGER}
104
+ BEFORE INSERT ON logs
105
+ WHEN upper(NEW.level) = 'TRACE'
106
+ BEGIN
107
+ SELECT RAISE(IGNORE);
108
+ END`;
109
+
110
+ const SQL_BY_MODE: Record<Exclude<CodexLogGuardMode, "off">, string> = {
111
+ compat: COMPAT_TRIGGER_SQL,
112
+ quiet: QUIET_TRIGGER_SQL,
113
+ };
114
+
115
+ export type CodexLogGuardObservedMode = CodexLogGuardMode | "collision";
116
+ export type CodexLogGuardProtectionState = "off" | "active" | "drifted" | "unsupported" | "unknown";
117
+
118
+ export interface CodexLogGuardProtectionSummary {
119
+ desiredMode: CodexLogGuardMode;
120
+ observedMode: CodexLogGuardObservedMode;
121
+ state: CodexLogGuardProtectionState;
122
+ }
123
+
124
+ export type CodexLogGuardStatus = CodexLogGuardInspection & {
125
+ protection: CodexLogGuardProtectionSummary;
126
+ };
127
+
128
+ export type CodexLogGuardMutationError =
129
+ | "unsupported_schema"
130
+ | "codex_running"
131
+ | "process_enumeration_failed"
132
+ | "trigger_collision"
133
+ | "unsafe_path"
134
+ | "busy"
135
+ | "database_error"
136
+ | "config_write_failed";
137
+
138
+ export type CodexLogGuardMutationResult =
139
+ | { ok: true; status: CodexLogGuardStatus }
140
+ | { ok: false; error: CodexLogGuardMutationError };
141
+
142
+ export interface CodexLogGuardProtectionDeps {
143
+ codexHome?: string;
144
+ processCheck?: () => CodexWriterProcessCheck;
145
+ readDesiredMode?: () => CodexLogGuardMode;
146
+ writeDesiredMode?: (mode: CodexLogGuardMode) => void;
147
+ withLock?: <T>(
148
+ canonicalCodexHome: string,
149
+ canonicalLogsDbPath: string,
150
+ work: () => T,
151
+ ) => CodexLogGuardLockOutcome<T>;
152
+ }
153
+
154
+ interface TriggerRow {
155
+ name: string;
156
+ sql: string | null;
157
+ }
158
+ interface ColumnRow { name: string }
159
+ interface OwnedTriggerSnapshot { name: string; sql: string }
160
+
161
+ type LockedMutationResult =
162
+ | { ok: true }
163
+ | { ok: false; error: CodexLogGuardMutationError };
164
+
165
+ function normalizeSql(sql: string | null | undefined): string {
166
+ return (sql ?? "").trim().replace(/;\s*$/, "").replace(/\s+/g, " ");
167
+ }
168
+
169
+ function expectedSql(mode: Exclude<CodexLogGuardMode, "off">): string {
170
+ return normalizeSql(SQL_BY_MODE[mode]);
171
+ }
172
+
173
+ function ownedModeForRow(row: TriggerRow): Exclude<CodexLogGuardMode, "off"> | null {
174
+ if (row.name === COMPAT_TRIGGER && normalizeSql(row.sql) === expectedSql("compat")) return "compat";
175
+ if (row.name === QUIET_TRIGGER && normalizeSql(row.sql) === expectedSql("quiet")) return "quiet";
176
+ return null;
177
+ }
178
+
179
+ function queryReservedTriggers(db: Database): TriggerRow[] {
180
+ const placeholders = OWNED_TRIGGER_NAMES.map(() => "?").join(", ");
181
+ return db.query<TriggerRow, string[]>(
182
+ `SELECT name, sql FROM sqlite_master WHERE type = 'trigger' AND name IN (${placeholders}) ORDER BY name`,
183
+ ).all(...OWNED_TRIGGER_NAMES);
184
+ }
185
+
186
+ function observeTriggers(db: Database): CodexLogGuardObservedMode {
187
+ const rows = queryReservedTriggers(db);
188
+ if (rows.length === 0) return "off";
189
+ const modes = rows.map(ownedModeForRow);
190
+ if (modes.some(mode => mode === null)) return "collision";
191
+ const unique = new Set(modes);
192
+ return unique.size === 1 && rows.length === 1 ? modes[0]! : "collision";
193
+ }
194
+
195
+ function exactCurrentSchema(db: Database): boolean {
196
+ // Delegates to the inspector's predicate so the locked recheck is exactly as
197
+ // strict as the compatibility report. Column names alone let a schema change
198
+ // between inspection and the locked write slip a mutation onto a database the
199
+ // inspector calls monitor-only.
200
+ return hasCurrentLogsSchema(db);
201
+ }
202
+
203
+ /**
204
+ * Read trigger metadata with the same immutable/checkpointed semantics as PR 1
205
+ * diagnostics. A status GET must never participate in Codex's SQLite WAL/SHM
206
+ * protocol or materialise sidecars merely to report protection state.
207
+ */
208
+ function openReadOnly(databasePath: string): Database {
209
+ const uri = `${pathToFileURL(databasePath).href}?immutable=1`;
210
+ return new Database(uri, IMMUTABLE_READONLY_FLAGS);
211
+ }
212
+
213
+ function openReadWrite(databasePath: string): Database {
214
+ // READWRITE without CREATE: a missing/moved canonical DB is a refusal, not a
215
+ // reason for OpenCodex to materialise a new foreign database.
216
+ return new Database(databasePath, sqliteConstants.SQLITE_OPEN_READWRITE);
217
+ }
218
+
219
+ function databasePathIsSafe(databasePath: string): boolean {
220
+ try {
221
+ const stat = lstatSync(databasePath);
222
+ if (!stat.isFile() || stat.isSymbolicLink()) return false;
223
+ return sameLogGuardPathIdentity(realpathSync.native(databasePath), databasePath);
224
+ } catch {
225
+ return false;
226
+ }
227
+ }
228
+
229
+ function protectionSummary(
230
+ inspection: CodexLogGuardInspection,
231
+ desiredMode: CodexLogGuardMode,
232
+ observedMode: CodexLogGuardObservedMode,
233
+ ): CodexLogGuardProtectionSummary {
234
+ if (inspection.capabilities.protection.state !== "supported") {
235
+ return { desiredMode, observedMode, state: "unsupported" };
236
+ }
237
+ if (observedMode === "collision") return { desiredMode, observedMode, state: "unknown" };
238
+ if (desiredMode === "off" && observedMode === "off") {
239
+ return { desiredMode, observedMode, state: "off" };
240
+ }
241
+ if (desiredMode !== "off" && desiredMode === observedMode) {
242
+ return { desiredMode, observedMode, state: "active" };
243
+ }
244
+ return { desiredMode, observedMode, state: "drifted" };
245
+ }
246
+
247
+ function inspectionDeps(deps: CodexLogGuardProtectionDeps): { codexHome?: string } {
248
+ return deps.codexHome ? { codexHome: deps.codexHome } : {};
249
+ }
250
+
251
+ export function getCodexLogGuardProtectionStatus(
252
+ deps: CodexLogGuardProtectionDeps = {},
253
+ ): CodexLogGuardStatus {
254
+ const codexHome = deps.codexHome ?? getCodexHome();
255
+ const inspection = inspectCodexLogs({ codexHome });
256
+ const databasePath = resolveCodexLogsDbPath({ codexHome });
257
+ const desiredMode = (deps.readDesiredMode ?? readCodexLogGuardMode)();
258
+ let observedMode: CodexLogGuardObservedMode = inspection.schema.state === "compatible" ? "collision" : "off";
259
+
260
+ if (inspection.schema.state === "compatible" && databasePathIsSafe(databasePath)) {
261
+ try {
262
+ const db = openReadOnly(databasePath);
263
+ try { observedMode = observeTriggers(db); }
264
+ finally { db.close(); }
265
+ } catch {
266
+ observedMode = "collision";
267
+ }
268
+ }
269
+
270
+ return {
271
+ ...inspection,
272
+ protection: protectionSummary(inspection, desiredMode, observedMode),
273
+ };
274
+ }
275
+
276
+ function successfulMutationStatus(
277
+ codexHome: string,
278
+ mode: CodexLogGuardMode,
279
+ ): CodexLogGuardStatus {
280
+ const inspection = inspectCodexLogs({ codexHome });
281
+ const state: CodexLogGuardProtectionState = inspection.capabilities.protection.state === "supported"
282
+ ? (mode === "off" ? "off" : "active")
283
+ : "unsupported";
284
+ return {
285
+ ...inspection,
286
+ protection: { desiredMode: mode, observedMode: mode, state },
287
+ };
288
+ }
289
+
290
+ function processRefusal(check: CodexWriterProcessCheck): CodexLogGuardMutationError | null {
291
+ if (check.state === "unknown") return "process_enumeration_failed";
292
+ if (check.processes.length > 0) return "codex_running";
293
+ return null;
294
+ }
295
+
296
+ function mutateOwnedTrigger(
297
+ databasePath: string,
298
+ mode: CodexLogGuardMode,
299
+ ): { ok: true; previousTriggers: readonly OwnedTriggerSnapshot[] } | { ok: false; error: CodexLogGuardMutationError } {
300
+ let db: Database | undefined;
301
+ let transactionOpen = false;
302
+ try {
303
+ if (!databasePathIsSafe(databasePath)) return { ok: false, error: "unsafe_path" };
304
+ db = openReadWrite(databasePath);
305
+ db.exec("PRAGMA busy_timeout = 0; BEGIN IMMEDIATE");
306
+ transactionOpen = true;
307
+ if (!exactCurrentSchema(db)) {
308
+ // Same reasoning as the caller's gate: installing into an unrecognized
309
+ // schema is refused, but removing a trigger we installed ourselves stays
310
+ // available so a schema upgrade cannot strand it.
311
+ if (mode !== "off") {
312
+ db.exec("ROLLBACK");
313
+ transactionOpen = false;
314
+ return { ok: false, error: "unsupported_schema" };
315
+ }
316
+ }
317
+
318
+ const rows = queryReservedTriggers(db);
319
+ const modes = rows.map(ownedModeForRow);
320
+ if (modes.some(item => item === null)) {
321
+ db.exec("ROLLBACK");
322
+ transactionOpen = false;
323
+ return { ok: false, error: "trigger_collision" };
324
+ }
325
+ const previousTriggers: OwnedTriggerSnapshot[] = rows.map(row => ({
326
+ name: row.name,
327
+ sql: row.sql!,
328
+ }));
329
+
330
+ for (const row of rows) {
331
+ // Name is from our fixed allow-list; never interpolate arbitrary sqlite_master data.
332
+ db.exec(`DROP TRIGGER ${row.name}`);
333
+ }
334
+ if (mode !== "off") db.exec(SQL_BY_MODE[mode]);
335
+
336
+ const observed = observeTriggers(db);
337
+ if (observed !== mode) throw new Error("log_guard_trigger_verification_failed");
338
+ db.exec("COMMIT");
339
+ transactionOpen = false;
340
+ return { ok: true, previousTriggers };
341
+ } catch (error) {
342
+ if (transactionOpen) {
343
+ try { db?.exec("ROLLBACK"); } catch { /* close releases the transaction */ }
344
+ }
345
+ if (isSqliteBusy(error)) return { ok: false, error: "busy" };
346
+ return { ok: false, error: "database_error" };
347
+ } finally {
348
+ try { db?.close(); } catch { /* mutation already settled */ }
349
+ }
350
+ }
351
+
352
+ function restoreOwnedTriggers(databasePath: string, previousTriggers: readonly OwnedTriggerSnapshot[]): void {
353
+ // Best-effort compensation only. Failure is deliberately not hidden by
354
+ // claiming success; the caller returns config_write_failed and status will
355
+ // expose any remaining drift on the next read.
356
+ let db: Database | undefined;
357
+ let transactionOpen = false;
358
+ try {
359
+ if (!databasePathIsSafe(databasePath)) return;
360
+ db = openReadWrite(databasePath);
361
+ db.exec("PRAGMA busy_timeout = 0; BEGIN IMMEDIATE");
362
+ transactionOpen = true;
363
+ if (!exactCurrentSchema(db)) {
364
+ db.exec("ROLLBACK");
365
+ transactionOpen = false;
366
+ return;
367
+ }
368
+
369
+ const current = queryReservedTriggers(db);
370
+ if (current.some(row => ownedModeForRow(row) === null)) {
371
+ db.exec("ROLLBACK");
372
+ transactionOpen = false;
373
+ return;
374
+ }
375
+ for (const row of current) db.exec(`DROP TRIGGER ${row.name}`);
376
+
377
+ for (const trigger of previousTriggers) {
378
+ if (ownedModeForRow(trigger) === null) throw new Error("invalid_owned_trigger_snapshot");
379
+ db.exec(trigger.sql);
380
+ }
381
+
382
+ const restored = queryReservedTriggers(db);
383
+ const expected = new Map(previousTriggers.map(row => [row.name, normalizeSql(row.sql)]));
384
+ if (restored.length !== previousTriggers.length
385
+ || restored.some(row => expected.get(row.name) !== normalizeSql(row.sql))) {
386
+ throw new Error("log_guard_trigger_restore_verification_failed");
387
+ }
388
+ db.exec("COMMIT");
389
+ transactionOpen = false;
390
+ } catch {
391
+ if (transactionOpen) {
392
+ try { db?.exec("ROLLBACK"); } catch { /* close releases the transaction */ }
393
+ }
394
+ } finally {
395
+ try { db?.close(); } catch { /* compensation already settled */ }
396
+ }
397
+ }
398
+
399
+ function performMutation(
400
+ requestedMode: CodexLogGuardMode | (() => CodexLogGuardMode),
401
+ deps: CodexLogGuardProtectionDeps,
402
+ ): CodexLogGuardMutationResult {
403
+ const codexHome = deps.codexHome ?? getCodexHome();
404
+ const inspection = inspectCodexLogs({ codexHome });
405
+ const databasePath = resolveCodexLogsDbPath({ codexHome });
406
+ // Removal must not be gated on the schema still being recognized. A Codex
407
+ // upgrade that changes the logs schema would otherwise strand an installed
408
+ // trigger: Protect is refused (correctly), but so is Disable, leaving the
409
+ // user with an active OpenCodex trigger and no in-product way to remove it.
410
+ // Installing into an unknown schema stays refused; taking our own trigger
411
+ // back out is always allowed.
412
+ const removingProtection = typeof requestedMode !== "function" && requestedMode === "off";
413
+ if (!removingProtection && inspection.capabilities.protection.state !== "supported") {
414
+ return { ok: false, error: "unsupported_schema" };
415
+ }
416
+ if (!databasePathIsSafe(databasePath)) return { ok: false, error: "unsafe_path" };
417
+
418
+ const checkProcesses = deps.processCheck ?? listRunningCodexProcesses;
419
+ const firstRefusal = processRefusal(checkProcesses());
420
+ if (firstRefusal) return { ok: false, error: firstRefusal };
421
+
422
+ const withLock = deps.withLock ?? withCodexLogGuardLock;
423
+ const writeDesired = deps.writeDesiredMode ?? writeCodexLogGuardMode;
424
+ let locked: CodexLogGuardLockOutcome<LockedMutationResult>;
425
+ // Repair passes a resolver instead of a value: its target mode must be read
426
+ // INSIDE L. Reading it before acquiring the lock let a Disable complete in
427
+ // the gap, after which the stale Repair reinstalled protection and reported
428
+ // success — the caller saw an honest "off" and got "compat".
429
+ let effectiveMode: CodexLogGuardMode = typeof requestedMode === "function" ? "off" : requestedMode;
430
+ try {
431
+ locked = withLock(codexHome, databasePath, () => {
432
+ // Recheck after acquiring L so a Codex process that starts during lock
433
+ // acquisition cannot race the foreign-schema mutation.
434
+ const secondRefusal = processRefusal(checkProcesses());
435
+ if (secondRefusal) return { ok: false, error: secondRefusal };
436
+
437
+ effectiveMode = typeof requestedMode === "function" ? requestedMode() : requestedMode;
438
+ const mutation = mutateOwnedTrigger(databasePath, effectiveMode);
439
+ if (!mutation.ok) return mutation;
440
+
441
+ // Desired state belongs to the same logical transition as the trigger.
442
+ // Keep L held through this write so another OpenCodex process cannot
443
+ // interleave a different mode between the DB commit and config commit.
444
+ try {
445
+ writeDesired(effectiveMode);
446
+ } catch {
447
+ restoreOwnedTriggers(databasePath, mutation.previousTriggers);
448
+ return { ok: false, error: "config_write_failed" as const };
449
+ }
450
+ return { ok: true };
451
+ });
452
+ } catch {
453
+ return { ok: false, error: "database_error" };
454
+ }
455
+ if (locked.kind === "unavailable") {
456
+ return {
457
+ ok: false,
458
+ error: locked.reason === "busy"
459
+ ? "busy"
460
+ : locked.reason === "unsafe-path" ? "unsafe_path" : "database_error",
461
+ };
462
+ }
463
+ if (!locked.value.ok) return locked.value;
464
+
465
+ // Report the mode that was actually applied under the lock, not the one the
466
+ // caller guessed before acquiring it.
467
+ return { ok: true, status: successfulMutationStatus(codexHome, effectiveMode) };
468
+ }
469
+
470
+ export function protectCodexLogs(
471
+ mode: Exclude<CodexLogGuardMode, "off">,
472
+ deps: CodexLogGuardProtectionDeps = {},
473
+ ): CodexLogGuardMutationResult {
474
+ return performMutation(mode, deps);
475
+ }
476
+
477
+ export function unprotectCodexLogs(
478
+ deps: CodexLogGuardProtectionDeps = {},
479
+ ): CodexLogGuardMutationResult {
480
+ return performMutation("off", deps);
481
+ }
482
+
483
+ export function repairCodexLogGuardProtection(
484
+ deps: CodexLogGuardProtectionDeps = {},
485
+ ): CodexLogGuardMutationResult {
486
+ // Resolve the desired mode under the lock (see performMutation): a Disable
487
+ // that lands between the read and the lock must win, not be silently undone.
488
+ return performMutation(() => (deps.readDesiredMode ?? readCodexLogGuardMode)(), deps);
489
+ }