@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
@@ -5,7 +5,16 @@ import { atomicWriteFile } from "../config";
5
5
  import { hasInjectedCodexRouting } from "./injected-marker";
6
6
  import { CODEX_HOME, CODEX_CONFIG_PATH, CODEX_PROFILE_PATH } from "./paths";
7
7
 
8
- const JOURNAL_PATH = join(CODEX_HOME, "opencodex-journal.json");
8
+ /**
9
+ * Exported so that anything reasoning ABOUT the journal points at the journal.
10
+ *
11
+ * The Codex admission snapshot re-derived this path by hand and got it wrong in
12
+ * both halves — wrong directory and wrong filename — so its "journal identity"
13
+ * watched a file nothing writes. The fixture re-derived it the same wrong way,
14
+ * agreed with the producer, and the pair stayed green. One exported constant
15
+ * removes the opportunity.
16
+ */
17
+ export const JOURNAL_PATH = join(CODEX_HOME, "opencodex-journal.json");
9
18
 
10
19
  interface Journal {
11
20
  version: 1;
@@ -13,6 +22,24 @@ interface Journal {
13
22
  originalProfile: string | null;
14
23
  injectedConfigHash?: string;
15
24
  injectedProfileHash?: string | null;
25
+ /**
26
+ * The exact root `openai_base_url` this injection wrote, when it wrote one.
27
+ *
28
+ * #1798: ownership used to be inferred from a marker COMMENT on the preceding line,
29
+ * which a reserializing Codex app deletes while keeping the value. Recording the value
30
+ * we actually wrote makes ownership provable from evidence rather than from formatting,
31
+ * and it is what lets restore tell OUR loopback URL apart from a gateway the user set.
32
+ */
33
+ injectedOpenaiBaseUrl?: string | null;
34
+ /**
35
+ * The catalog path this injection actually wrote to.
36
+ *
37
+ * #1798: restore re-resolves the catalog from the CURRENT config, so a Codex app rewrite
38
+ * that dropped `model_catalog_json` sends restore to the default catalog while the
39
+ * proxy-written one is left routed. The injected path is the only durable record of which
40
+ * file we actually touched.
41
+ */
42
+ injectedCatalogPath?: string | null;
16
43
  pid: number;
17
44
  timestamp: string;
18
45
  }
@@ -81,15 +108,50 @@ export function writeJournal(options: WriteJournalOptions = {}): void {
81
108
  atomicWriteFile(JOURNAL_PATH, JSON.stringify(journal));
82
109
  }
83
110
 
84
- export function markJournalInjectedState(config: string, profile: string | null): void {
111
+ export interface InjectedJournalOwnership {
112
+ injectedOpenaiBaseUrl: string | null;
113
+ injectedCatalogPath: string | null;
114
+ }
115
+
116
+ export function markJournalInjectedState(
117
+ config: string,
118
+ profile: string | null,
119
+ ownership: InjectedJournalOwnership,
120
+ ): void {
85
121
  const journal = readJournal();
86
122
  if (!journal) return;
87
- if (journal.injectedConfigHash) return;
88
- journal.injectedConfigHash = sha256(config) ?? undefined;
123
+ // The first exact injected config is the only safe whole-snapshot restore boundary for
124
+ // the first native snapshot. A later reinjection may preserve user edits made while routed;
125
+ // hashing those newer bytes and restoring the first snapshot would delete those edits.
126
+ // Keep the first hash so changed/reinjected configs take the owned-field fallback path.
127
+ journal.injectedConfigHash ??= sha256(config) ?? undefined;
128
+ // The profile file is wholly generated by OpenCodex, so its latest exact hash remains safe
129
+ // to refresh and lets restore remove the latest generated profile after a port change.
89
130
  journal.injectedProfileHash = sha256(profile);
131
+ // Only the caller knows which values it actually owns. Deriving these from the final TOML
132
+ // would mistake a preserved user override for injected routing.
133
+ journal.injectedOpenaiBaseUrl = ownership.injectedOpenaiBaseUrl;
134
+ journal.injectedCatalogPath = ownership.injectedCatalogPath;
90
135
  atomicWriteFile(JOURNAL_PATH, JSON.stringify(journal));
91
136
  }
92
137
 
138
+ /**
139
+ * The root `openai_base_url` the last injection wrote, or null when it wrote none.
140
+ *
141
+ * #1798: the fallback strip recognizes an injected URL by the marker COMMENT above it,
142
+ * and a Codex app rewrite keeps values while dropping comments. This is the evidence that
143
+ * survives such a rewrite, so restore can still prove the URL is ours -- and, just as
144
+ * importantly, prove that a DIFFERENT URL is not.
145
+ */
146
+ export function journaledInjectedOpenaiBaseUrl(): string | null {
147
+ return readJournal()?.injectedOpenaiBaseUrl ?? null;
148
+ }
149
+
150
+ /** The catalog path the last injection wrote to, or null when none was recorded. */
151
+ export function journaledInjectedCatalogPath(): string | null {
152
+ return readJournal()?.injectedCatalogPath ?? null;
153
+ }
154
+
93
155
  export function removeJournal(): void {
94
156
  try { unlinkSync(JOURNAL_PATH); } catch { /* ignore */ }
95
157
  }
@@ -0,0 +1,524 @@
1
+ import { statSync } from "node:fs";
2
+ import { join, resolve } from "node:path";
3
+ import { pathToFileURL } from "node:url";
4
+ import { Database, constants } from "bun:sqlite";
5
+
6
+ import {
7
+ getCodexHome,
8
+ resolveCodexSqliteHome,
9
+ type CodexSqliteHomeDeps,
10
+ } from "../paths";
11
+
12
+ const IMMUTABLE_READONLY_FLAGS = constants.SQLITE_OPEN_READONLY | constants.SQLITE_OPEN_URI;
13
+ const KNOWN_LOG_LEVELS = new Set(["TRACE", "DEBUG", "INFO", "WARN", "ERROR"]);
14
+ // The issue reporter measured a ~1 GB database taking 17.3s for GROUP BY level
15
+ // alone; skipping all row aggregates above 64 MiB reduced /api/storage to 628ms.
16
+ const MAX_SYNCHRONOUS_METRICS_DATABASE_BYTES = 64 * 1024 * 1024;
17
+
18
+ interface CurrentLogColumn {
19
+ name: string;
20
+ type: string;
21
+ notnull: number;
22
+ defaultValue: string | null;
23
+ pk: number;
24
+ }
25
+
26
+ // Pinned to Codex logs migration 0002. Keep this schema private: inspection reports
27
+ // compatibility, not column names, so sensitive payload-bearing fields never leak through
28
+ // the management API. Any additive/rebuilt future schema is monitor-only until reviewed.
29
+ const CURRENT_LOG_SCHEMA: readonly CurrentLogColumn[] = [
30
+ { name: "id", type: "INTEGER", notnull: 0, defaultValue: null, pk: 1 },
31
+ { name: "ts", type: "INTEGER", notnull: 1, defaultValue: null, pk: 0 },
32
+ { name: "ts_nanos", type: "INTEGER", notnull: 1, defaultValue: null, pk: 0 },
33
+ { name: "level", type: "TEXT", notnull: 1, defaultValue: null, pk: 0 },
34
+ { name: "target", type: "TEXT", notnull: 1, defaultValue: null, pk: 0 },
35
+ { name: "feedback_log_body", type: "TEXT", notnull: 0, defaultValue: null, pk: 0 },
36
+ { name: "module_path", type: "TEXT", notnull: 0, defaultValue: null, pk: 0 },
37
+ { name: "file", type: "TEXT", notnull: 0, defaultValue: null, pk: 0 },
38
+ { name: "line", type: "INTEGER", notnull: 0, defaultValue: null, pk: 0 },
39
+ { name: "thread_id", type: "TEXT", notnull: 0, defaultValue: null, pk: 0 },
40
+ { name: "process_uuid", type: "TEXT", notnull: 0, defaultValue: null, pk: 0 },
41
+ { name: "estimated_bytes", type: "INTEGER", notnull: 1, defaultValue: "0", pk: 0 },
42
+ ] as const;
43
+
44
+ const CURRENT_LOG_TABLE_SQL = `CREATE TABLE logs (
45
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
46
+ ts INTEGER NOT NULL,
47
+ ts_nanos INTEGER NOT NULL,
48
+ level TEXT NOT NULL,
49
+ target TEXT NOT NULL,
50
+ feedback_log_body TEXT,
51
+ module_path TEXT,
52
+ file TEXT,
53
+ line INTEGER,
54
+ thread_id TEXT,
55
+ process_uuid TEXT,
56
+ estimated_bytes INTEGER NOT NULL DEFAULT 0
57
+ )`;
58
+
59
+ const CURRENT_LOG_INDEX_SQL = {
60
+ idx_logs_ts: "CREATE INDEX idx_logs_ts ON logs(ts DESC, ts_nanos DESC, id DESC)",
61
+ idx_logs_thread_id: "CREATE INDEX idx_logs_thread_id ON logs(thread_id)",
62
+ idx_logs_thread_id_ts: "CREATE INDEX idx_logs_thread_id_ts ON logs(thread_id, ts DESC, ts_nanos DESC, id DESC)",
63
+ idx_logs_process_uuid_threadless_ts: `CREATE INDEX idx_logs_process_uuid_threadless_ts
64
+ ON logs(process_uuid, ts DESC, ts_nanos DESC, id DESC)
65
+ WHERE thread_id IS NULL`,
66
+ } as const;
67
+
68
+ export type CodexLogGuardCapabilityReason =
69
+ | "database_missing"
70
+ | "database_unreadable"
71
+ | "unknown_schema"
72
+ | "inspect_failed";
73
+
74
+ export type CodexLogGuardSchemaState =
75
+ | { state: "compatible" }
76
+ | { state: "missing"; reason: "database_missing" }
77
+ | { state: "unreadable"; reason: "database_unreadable" }
78
+ | { state: "unsupported"; reason: "unknown_schema" }
79
+ | { state: "unavailable"; reason: "inspect_failed" };
80
+
81
+ export type CodexLogGuardCapability =
82
+ | { state: "supported" }
83
+ | { state: "unsupported"; reason: CodexLogGuardCapabilityReason };
84
+
85
+ export interface CodexLogGuardMetrics {
86
+ totalRows: number;
87
+ rowsByLevel: Record<string, number>;
88
+ traceRows: number;
89
+ traceShare: number;
90
+ topTargets: Array<{ target: string; rows: number }>;
91
+ pageSize: number;
92
+ pageCount: number;
93
+ freelistPages: number;
94
+ reclaimableBytes: number;
95
+ estimatedLogBytes: number | null;
96
+ }
97
+
98
+ /**
99
+ * Serializable, privacy-safe Log Guard inspection result.
100
+ *
101
+ * Canonical filesystem paths are deliberately kept local to the inspector. Consumers get
102
+ * only the coarse location relation (`externalSqliteHome`) and aggregate file/SQLite data.
103
+ * `externalSqliteHome` is null only when config resolution itself is unavailable.
104
+ */
105
+ export interface CodexLogGuardInspection {
106
+ generatedAt: number;
107
+ externalSqliteHome: boolean | null;
108
+ snapshot: "checkpointed";
109
+ files: {
110
+ databaseBytes: number;
111
+ walBytes: number;
112
+ shmBytes: number;
113
+ };
114
+ schema: CodexLogGuardSchemaState;
115
+ capabilities: {
116
+ inspection: CodexLogGuardCapability;
117
+ protection: CodexLogGuardCapability;
118
+ reclaim: CodexLogGuardCapability;
119
+ };
120
+ metrics: CodexLogGuardMetrics | null;
121
+ metricsSkipped: null | {
122
+ reason: "database_too_large";
123
+ thresholdBytes: number;
124
+ };
125
+ }
126
+
127
+ interface ColumnRow {
128
+ cid: number;
129
+ name: string;
130
+ type: string;
131
+ notnull: number;
132
+ dflt_value: string | null;
133
+ pk: number;
134
+ }
135
+ interface SchemaObjectRow { name: string; type: string; sql: string | null }
136
+ interface CountRow { n: number }
137
+ interface LevelRow { level: string; rows: number }
138
+ interface TargetCountRow { rows: number }
139
+ interface EstimatedBytesRow { bytes: number | null }
140
+
141
+ type CanonicalTargetState = "missing" | "file" | "unusable";
142
+
143
+ function canonicalTargetState(path: string): CanonicalTargetState {
144
+ try {
145
+ return statSync(path).isFile() ? "file" : "unusable";
146
+ } catch (error) {
147
+ return (error as NodeJS.ErrnoException | undefined)?.code === "ENOENT" ? "missing" : "unusable";
148
+ }
149
+ }
150
+
151
+ function fileSize(path: string): number {
152
+ try {
153
+ const stat = statSync(path);
154
+ return stat.isFile() ? stat.size : 0;
155
+ } catch {
156
+ return 0;
157
+ }
158
+ }
159
+
160
+ /**
161
+ * Memoized inspection, keyed by the database and WAL identity.
162
+ *
163
+ * `readMetrics` runs four unbounded aggregates (`count(*)`, two `GROUP BY`s and
164
+ * a `sum`) over the whole `logs` table. `bun:sqlite` is synchronous, so that
165
+ * work occupies the proxy thread for its full duration — measured at ~49s on a
166
+ * 15.6 GB / 302k-row database, exactly the large fragmented case this feature
167
+ * exists to diagnose. Both `/api/storage` and `/api/storage/codex-logs` call it
168
+ * inline, so merely opening or refreshing the Storage page could stall routing
169
+ * and health responses.
170
+ *
171
+ * A dashboard refresh, a page that renders both panels, and a poll loop all
172
+ * repeat an identical scan. Keying on size+mtime of the database and WAL means a
173
+ * repeat inspection is free until Codex actually writes, which removes the
174
+ * repeated stalls without ever serving stale numbers: any write changes the WAL
175
+ * and invalidates the entry.
176
+ *
177
+ * Memoization bounds repeat cost. The database-size gate below separately bounds
178
+ * cold request-thread work by omitting these aggregates for large databases.
179
+ */
180
+ type InspectionCacheEntry = {
181
+ key: string;
182
+ value: CodexLogGuardInspection;
183
+ };
184
+
185
+ let inspectionCache: InspectionCacheEntry | null = null;
186
+
187
+ function inspectionCacheKey(databasePath: string): string {
188
+ const stamp = (path: string): string => {
189
+ try {
190
+ // `size:mtimeMs` alone is not an identity. An atomic replace (write a new
191
+ // file, rename over the old one) can preserve both, and the cache then
192
+ // served the previous schema/capability verdict indefinitely — trading a
193
+ // repeated-scan cost for a persistent wrong answer. Include the inode and
194
+ // device so a replaced file is a different key even at identical size and
195
+ // mtime, plus nanosecond ctime/mtime so an in-place rewrite inside one
196
+ // millisecond still invalidates.
197
+ const stat = statSync(path, { bigint: true });
198
+ return [
199
+ stat.dev,
200
+ stat.ino,
201
+ stat.size,
202
+ stat.mtimeNs,
203
+ stat.ctimeNs,
204
+ ].join(":");
205
+ } catch {
206
+ return "-";
207
+ }
208
+ };
209
+ return [
210
+ databasePath,
211
+ stamp(databasePath),
212
+ stamp(`${databasePath}-wal`),
213
+ stamp(`${databasePath}-shm`),
214
+ ].join("|");
215
+ }
216
+
217
+ /** Drop the memoized inspection. Exported for tests and for post-mutation refresh. */
218
+ export function resetCodexLogGuardInspectionCache(): void {
219
+ inspectionCache = null;
220
+ }
221
+
222
+ function capabilityFor(schema: CodexLogGuardSchemaState): CodexLogGuardCapability {
223
+ if (schema.state === "compatible") return { state: "supported" };
224
+ return { state: "unsupported", reason: schema.reason };
225
+ }
226
+
227
+ function unavailableInspection(): CodexLogGuardInspection {
228
+ const schema: CodexLogGuardSchemaState = { state: "unavailable", reason: "inspect_failed" };
229
+ const unavailable: CodexLogGuardCapability = { state: "unsupported", reason: "inspect_failed" };
230
+ return {
231
+ generatedAt: Date.now(),
232
+ externalSqliteHome: null,
233
+ snapshot: "checkpointed",
234
+ files: { databaseBytes: 0, walBytes: 0, shmBytes: 0 },
235
+ schema,
236
+ capabilities: {
237
+ inspection: unavailable,
238
+ protection: unavailable,
239
+ reclaim: unavailable,
240
+ },
241
+ metrics: null,
242
+ metricsSkipped: null,
243
+ };
244
+ }
245
+
246
+ function normalizeDeclaredType(type: string): string {
247
+ return String(type ?? "").trim().toUpperCase();
248
+ }
249
+
250
+ function normalizeDefault(value: string | null): string | null {
251
+ return value === null ? null : String(value).trim();
252
+ }
253
+
254
+ function normalizeSchemaSql(sql: string | null | undefined): string {
255
+ return (sql ?? "").trim().replace(/;\s*$/, "").replace(/\s+/g, " ");
256
+ }
257
+
258
+ function sameColumns(columns: ColumnRow[]): boolean {
259
+ if (columns.length !== CURRENT_LOG_SCHEMA.length) return false;
260
+ return columns.every((column, index) => {
261
+ const expected = CURRENT_LOG_SCHEMA[index];
262
+ return column.cid === index
263
+ && column.name === expected.name
264
+ && normalizeDeclaredType(column.type) === expected.type
265
+ && Number(column.notnull) === expected.notnull
266
+ && normalizeDefault(column.dflt_value) === expected.defaultValue
267
+ && Number(column.pk) === expected.pk;
268
+ });
269
+ }
270
+
271
+ /**
272
+ * The authoritative compatibility predicate: exact table SQL, exact column
273
+ * metadata, and every canonical index.
274
+ *
275
+ * Exported because the mutation paths must apply the SAME test inside their
276
+ * write transaction. They used to check column NAMES only, which is strictly
277
+ * weaker than what the inspector reports, so a schema change landing between
278
+ * the outer inspection and the locked write let Protect install a row-dropping
279
+ * trigger and let Reclaim vacuum pages on a database the inspector classifies
280
+ * as monitor-only. The lock serializes OpenCodex against itself; it does not
281
+ * stop Codex or another SQLite writer, so that TOCTOU window is real.
282
+ */
283
+ export function hasCurrentLogsSchema(db: Database): boolean {
284
+ const columns = db.query<ColumnRow, []>("PRAGMA table_info(logs)").all();
285
+ return hasCurrentLogsTable(db, columns);
286
+ }
287
+
288
+ function hasCurrentLogsTable(db: Database, columns: ColumnRow[]): boolean {
289
+ const table = db.query<SchemaObjectRow, []>(
290
+ "SELECT name, type, sql FROM sqlite_schema WHERE name = 'logs' LIMIT 1",
291
+ ).get();
292
+ if (table?.type !== "table"
293
+ || !sameColumns(columns)
294
+ || normalizeSchemaSql(table.sql) !== normalizeSchemaSql(CURRENT_LOG_TABLE_SQL)) {
295
+ return false;
296
+ }
297
+
298
+ const indexes = db.query<SchemaObjectRow, []>(`
299
+ SELECT name, type, sql FROM sqlite_schema
300
+ WHERE name IN (
301
+ 'idx_logs_ts',
302
+ 'idx_logs_thread_id',
303
+ 'idx_logs_thread_id_ts',
304
+ 'idx_logs_process_uuid_threadless_ts'
305
+ )
306
+ `).all();
307
+ const byName = new Map(indexes.map(row => [row.name, row]));
308
+ for (const [name, expectedSql] of Object.entries(CURRENT_LOG_INDEX_SQL)) {
309
+ const row = byName.get(name);
310
+ if (row?.type !== "index" || normalizeSchemaSql(row.sql) !== normalizeSchemaSql(expectedSql)) {
311
+ return false;
312
+ }
313
+ }
314
+
315
+ // Extra indexes and triggers do not redefine the table contract. In particular,
316
+ // Protect intentionally installs OpenCodex-owned triggers and unrelated user triggers
317
+ // are supported, so compatibility is based on the canonical table plus required indexes.
318
+ return true;
319
+ }
320
+
321
+ function pragmaNumber(db: Database, pragma: "page_size" | "page_count" | "freelist_count"): number {
322
+ const row = db.query<Record<string, number>, []>(`PRAGMA ${pragma}`).get();
323
+ return Number(row?.[pragma] ?? 0);
324
+ }
325
+
326
+ function readMetrics(db: Database, columns: string[]): CodexLogGuardMetrics | null {
327
+ if (!columns.includes("level") || !columns.includes("target")) return null;
328
+
329
+ try {
330
+ const totalRows = Number(db.query<CountRow, []>("SELECT count(*) AS n FROM logs").get()?.n ?? 0);
331
+ const rowsByLevel: Record<string, number> = {};
332
+ for (const row of db.query<LevelRow, []>(
333
+ "SELECT level, count(*) AS rows FROM logs GROUP BY level ORDER BY level",
334
+ ).all()) {
335
+ const rawLevel = String(row.level ?? "").toUpperCase();
336
+ const level = KNOWN_LOG_LEVELS.has(rawLevel) ? rawLevel : "OTHER";
337
+ rowsByLevel[level] = (rowsByLevel[level] ?? 0) + Number(row.rows ?? 0);
338
+ }
339
+
340
+ // Preserve the useful top-target distribution without serializing target names from
341
+ // the foreign database. Rank labels are fixed output values and cannot carry local
342
+ // paths, credentials, or other injected diagnostic content.
343
+ const topTargets = db.query<TargetCountRow, []>(
344
+ "SELECT count(*) AS rows FROM logs GROUP BY target ORDER BY rows DESC, target ASC LIMIT 10",
345
+ ).all().map((row, index) => ({ target: `TARGET_${index + 1}`, rows: Number(row.rows ?? 0) }));
346
+
347
+ const pageSize = pragmaNumber(db, "page_size");
348
+ const pageCount = pragmaNumber(db, "page_count");
349
+ const freelistPages = pragmaNumber(db, "freelist_count");
350
+ const traceRows = rowsByLevel.TRACE ?? 0;
351
+ const estimatedLogBytes = columns.includes("estimated_bytes")
352
+ ? Number(db.query<EstimatedBytesRow, []>(
353
+ "SELECT COALESCE(sum(estimated_bytes), 0) AS bytes FROM logs",
354
+ ).get()?.bytes ?? 0)
355
+ : null;
356
+
357
+ return {
358
+ totalRows,
359
+ rowsByLevel,
360
+ traceRows,
361
+ traceShare: totalRows > 0 ? traceRows / totalRows : 0,
362
+ topTargets,
363
+ pageSize,
364
+ pageCount,
365
+ freelistPages,
366
+ reclaimableBytes: pageSize * freelistPages,
367
+ estimatedLogBytes,
368
+ };
369
+ } catch {
370
+ // A future schema may still have a `logs` table but change aggregate-compatible
371
+ // columns or virtual-table behaviour. Metadata inspection remains valid; metrics do
372
+ // not become a reason to throw or to open a writable connection.
373
+ return null;
374
+ }
375
+ }
376
+
377
+ /**
378
+ * Inspect the canonical Codex diagnostic log database without participating in SQLite's
379
+ * write/locking protocol. `immutable=1` intentionally observes the last checkpointed
380
+ * snapshot: file sizes include live sidecars, while SQL aggregates may lag a live WAL.
381
+ * This is a zero-write health view, not an SSD/NAND write-rate measurement.
382
+ */
383
+ export function inspectCodexLogs(deps: CodexSqliteHomeDeps = {}): CodexLogGuardInspection {
384
+ let cacheKey: string | null = null;
385
+ try {
386
+ const home = deps.codexHome ?? getCodexHome();
387
+ cacheKey = inspectionCacheKey(join(resolveCodexSqliteHome({ ...deps, codexHome: home }), "logs_2.sqlite"));
388
+ if (inspectionCache?.key === cacheKey) return inspectionCache.value;
389
+ } catch {
390
+ cacheKey = null;
391
+ }
392
+ const value = inspectCodexLogsUncached(deps);
393
+ // `generatedAt` is part of the memoized value, so a cached response reports the
394
+ // time the numbers were actually measured rather than the time they were served.
395
+ if (cacheKey !== null) inspectionCache = { key: cacheKey, value };
396
+ return value;
397
+ }
398
+
399
+ function inspectCodexLogsUncached(deps: CodexSqliteHomeDeps = {}): CodexLogGuardInspection {
400
+ let codexHome: string;
401
+ let sqliteHome: string;
402
+ try {
403
+ codexHome = deps.codexHome ?? getCodexHome();
404
+ sqliteHome = resolveCodexSqliteHome({ ...deps, codexHome });
405
+ } catch {
406
+ // Resolution errors can include config paths. Convert them to a fixed, non-fatal
407
+ // inspection state before they cross any diagnostic/API boundary.
408
+ return unavailableInspection();
409
+ }
410
+
411
+ // Resolve sqlite_home exactly once so the location flag and inspected file cannot refer
412
+ // to different roots if config changes during one inspection.
413
+ const databasePath = join(sqliteHome, "logs_2.sqlite");
414
+ const targetState = canonicalTargetState(databasePath);
415
+ const files = {
416
+ databaseBytes: fileSize(databasePath),
417
+ walBytes: fileSize(`${databasePath}-wal`),
418
+ shmBytes: fileSize(`${databasePath}-shm`),
419
+ };
420
+
421
+ const common = {
422
+ generatedAt: Date.now(),
423
+ externalSqliteHome: resolve(sqliteHome) !== resolve(codexHome),
424
+ snapshot: "checkpointed" as const,
425
+ files,
426
+ };
427
+
428
+ if (targetState === "missing") {
429
+ const schema: CodexLogGuardSchemaState = { state: "missing", reason: "database_missing" };
430
+ const mutation = capabilityFor(schema);
431
+ return {
432
+ ...common,
433
+ schema,
434
+ metrics: null,
435
+ metricsSkipped: null,
436
+ capabilities: {
437
+ inspection: { state: "supported" },
438
+ protection: mutation,
439
+ reclaim: mutation,
440
+ },
441
+ };
442
+ }
443
+
444
+ if (targetState === "unusable") {
445
+ const schema: CodexLogGuardSchemaState = { state: "unreadable", reason: "database_unreadable" };
446
+ const mutation = capabilityFor(schema);
447
+ return {
448
+ ...common,
449
+ schema,
450
+ metrics: null,
451
+ metricsSkipped: null,
452
+ capabilities: {
453
+ inspection: { state: "supported" },
454
+ protection: mutation,
455
+ reclaim: mutation,
456
+ },
457
+ };
458
+ }
459
+
460
+ // SQLite accepts a zero-byte file as an empty database. For Log Guard this is not a
461
+ // compatible future schema: Codex's canonical logs database must already contain its
462
+ // migrated `logs` table before any future mutation capability can be considered safe.
463
+ if (files.databaseBytes === 0) {
464
+ const schema: CodexLogGuardSchemaState = { state: "unreadable", reason: "database_unreadable" };
465
+ const mutation = capabilityFor(schema);
466
+ return {
467
+ ...common,
468
+ schema,
469
+ metrics: null,
470
+ metricsSkipped: null,
471
+ capabilities: {
472
+ inspection: { state: "supported" },
473
+ protection: mutation,
474
+ reclaim: mutation,
475
+ },
476
+ };
477
+ }
478
+
479
+ try {
480
+ const uri = `${pathToFileURL(databasePath).href}?immutable=1`;
481
+ const db = new Database(uri, IMMUTABLE_READONLY_FLAGS);
482
+ try {
483
+ const columnRows = db.query<ColumnRow, []>("PRAGMA table_info(logs)").all();
484
+ const columns = columnRows.map(row => row.name);
485
+ const schema: CodexLogGuardSchemaState = hasCurrentLogsTable(db, columnRows)
486
+ ? { state: "compatible" }
487
+ : { state: "unsupported", reason: "unknown_schema" };
488
+ const mutation = capabilityFor(schema);
489
+ const metricsSkipped = files.databaseBytes > MAX_SYNCHRONOUS_METRICS_DATABASE_BYTES
490
+ ? {
491
+ reason: "database_too_large" as const,
492
+ thresholdBytes: MAX_SYNCHRONOUS_METRICS_DATABASE_BYTES,
493
+ }
494
+ : null;
495
+ return {
496
+ ...common,
497
+ schema,
498
+ metrics: metricsSkipped === null ? readMetrics(db, columns) : null,
499
+ metricsSkipped,
500
+ capabilities: {
501
+ inspection: { state: "supported" },
502
+ protection: mutation,
503
+ reclaim: mutation,
504
+ },
505
+ };
506
+ } finally {
507
+ db.close();
508
+ }
509
+ } catch {
510
+ const schema: CodexLogGuardSchemaState = { state: "unreadable", reason: "database_unreadable" };
511
+ const mutation = capabilityFor(schema);
512
+ return {
513
+ ...common,
514
+ schema,
515
+ metrics: null,
516
+ metricsSkipped: null,
517
+ capabilities: {
518
+ inspection: { state: "supported" },
519
+ protection: mutation,
520
+ reclaim: mutation,
521
+ },
522
+ };
523
+ }
524
+ }