@iislee/opencodex 2.11.0 → 2.35.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (733) hide show
  1. package/AGENTS_INSTALL.md +109 -0
  2. package/README.md +117 -22
  3. package/bin/ocx.mjs +245 -88
  4. package/bin/package-main.mjs +1 -1
  5. package/gui/dist/assets/index-Be1sDpWw.css +1 -0
  6. package/gui/dist/assets/index-D4OKgUV4.js +114 -0
  7. package/gui/dist/index.html +2 -2
  8. package/gui/dist/provider-icons/alibaba-color.svg +1 -1
  9. package/gui/dist/provider-icons/antigravity-color.svg +1 -1
  10. package/gui/dist/provider-icons/claude-color.svg +1 -1
  11. package/gui/dist/provider-icons/cline-color.svg +16 -0
  12. package/gui/dist/provider-icons/cloudflare-ai-gateway-color.svg +1 -1
  13. package/gui/dist/provider-icons/commandcode-color.svg +1 -0
  14. package/gui/dist/provider-icons/copilot-color.svg +1 -1
  15. package/gui/dist/provider-icons/cursor-color.svg +1 -1
  16. package/gui/dist/provider-icons/deepseek-color.svg +1 -1
  17. package/gui/dist/provider-icons/firepass-color.svg +1 -1
  18. package/gui/dist/provider-icons/fireworks-color.svg +1 -1
  19. package/gui/dist/provider-icons/gemini-color.svg +1 -1
  20. package/gui/dist/provider-icons/github-copilot-color.svg +1 -1
  21. package/gui/dist/provider-icons/gitlab-duo-color.svg +1 -1
  22. package/gui/dist/provider-icons/grok.svg +1 -1
  23. package/gui/dist/provider-icons/groq-color.svg +1 -1
  24. package/gui/dist/provider-icons/huggingface-color.svg +1 -1
  25. package/gui/dist/provider-icons/kimi-color.svg +1 -1
  26. package/gui/dist/provider-icons/kiro-color.svg +2 -2
  27. package/gui/dist/provider-icons/lm-studio-color.svg +1 -1
  28. package/gui/dist/provider-icons/mistral-color.svg +1 -1
  29. package/gui/dist/provider-icons/moonshot-color.svg +1 -1
  30. package/gui/dist/provider-icons/nvidia-color.svg +1 -1
  31. package/gui/dist/provider-icons/ollama-color.svg +1 -1
  32. package/gui/dist/provider-icons/openai.svg +1 -1
  33. package/gui/dist/provider-icons/opencode.svg +2 -1
  34. package/gui/dist/provider-icons/openrouter-color.svg +1 -1
  35. package/gui/dist/provider-icons/pi.svg +2 -2
  36. package/gui/dist/provider-icons/qianfan-color.svg +1 -1
  37. package/gui/dist/provider-icons/qwen-portal-color.svg +1 -1
  38. package/gui/dist/provider-icons/vercel-ai-gateway-color.svg +1 -1
  39. package/gui/dist/provider-icons/vllm-color.svg +1 -1
  40. package/gui/dist/provider-icons/xiaomi-color.svg +1 -1
  41. package/package.json +21 -10
  42. package/src/adapters/anthropic-output-schema.ts +137 -0
  43. package/src/adapters/anthropic.ts +475 -62
  44. package/src/adapters/base.ts +82 -7
  45. package/src/adapters/client-fingerprint.ts +18 -12
  46. package/src/adapters/cline-pass-deepseek-v4-tool-replay.ts +69 -0
  47. package/src/adapters/command-code.ts +637 -0
  48. package/src/adapters/cursor/call-id.ts +44 -0
  49. package/src/adapters/cursor/catalog.ts +541 -0
  50. package/src/adapters/cursor/checkpoint-store.ts +308 -0
  51. package/src/adapters/cursor/cursor-errors.ts +144 -6
  52. package/src/adapters/cursor/discovery.ts +122 -14
  53. package/src/adapters/cursor/effort-map.ts +106 -4
  54. package/src/adapters/cursor/envelope-echo.ts +290 -0
  55. package/src/adapters/cursor/framing.ts +39 -0
  56. package/src/adapters/cursor/h2-pool.ts +123 -0
  57. package/src/adapters/cursor/http1-bidi.ts +361 -0
  58. package/src/adapters/cursor/images.ts +704 -0
  59. package/src/adapters/cursor/live-models.ts +180 -59
  60. package/src/adapters/cursor/live-transport.ts +623 -170
  61. package/src/adapters/cursor/message-mapper.ts +4 -1
  62. package/src/adapters/cursor/native-exec-common.ts +23 -2
  63. package/src/adapters/cursor/native-exec-desktop.ts +23 -0
  64. package/src/adapters/cursor/native-exec-fs.ts +10 -7
  65. package/src/adapters/cursor/native-exec-network.ts +1 -1
  66. package/src/adapters/cursor/native-exec-shell.ts +5 -3
  67. package/src/adapters/cursor/native-exec.ts +121 -14
  68. package/src/adapters/cursor/protobuf-events.ts +829 -11
  69. package/src/adapters/cursor/protobuf-request.ts +447 -75
  70. package/src/adapters/cursor/request-builder.ts +271 -35
  71. package/src/adapters/cursor/tool-definitions.ts +252 -12
  72. package/src/adapters/cursor/tool-result-normalize.ts +115 -0
  73. package/src/adapters/cursor/transport.ts +22 -0
  74. package/src/adapters/cursor/types.ts +48 -1
  75. package/src/adapters/cursor.ts +336 -39
  76. package/src/adapters/exec-tool-result-normalize.ts +99 -0
  77. package/src/adapters/google-antigravity-replay.ts +713 -41
  78. package/src/adapters/google-antigravity-wire.ts +48 -10
  79. package/src/adapters/google-errors.ts +44 -12
  80. package/src/adapters/google-http.ts +38 -10
  81. package/src/adapters/google-tool-schema.ts +80 -15
  82. package/src/adapters/google-truncation.ts +11 -0
  83. package/src/adapters/google.ts +742 -86
  84. package/src/adapters/identity.ts +39 -6
  85. package/src/adapters/image.ts +1 -1
  86. package/src/adapters/kiro-constants.ts +27 -0
  87. package/src/adapters/kiro-errors.ts +11 -0
  88. package/src/adapters/kiro-events.ts +19 -1
  89. package/src/adapters/kiro-thinking.ts +18 -2
  90. package/src/adapters/kiro-tools.ts +65 -17
  91. package/src/adapters/kiro.ts +270 -73
  92. package/src/adapters/mimo-free.ts +17 -0
  93. package/src/adapters/openai-chat-url.ts +11 -0
  94. package/src/adapters/openai-chat.ts +1377 -344
  95. package/src/adapters/openai-responses-url.ts +16 -0
  96. package/src/adapters/openai-responses.ts +1164 -64
  97. package/src/adapters/registry.ts +175 -0
  98. package/src/adapters/responses-tool-schema.ts +67 -0
  99. package/src/adapters/run-turn-queue.ts +36 -1
  100. package/src/adapters/tool-call-id.ts +119 -0
  101. package/src/adapters/tool-catalog-nudge.ts +105 -21
  102. package/src/adapters/xai-tool-schema.ts +436 -0
  103. package/src/adapters/xai-web-search.ts +186 -0
  104. package/src/bridge.ts +512 -92
  105. package/src/chat/inbound.ts +57 -20
  106. package/src/chat/outbound.ts +97 -33
  107. package/src/claude/agents-inject.ts +40 -10
  108. package/src/claude/context-windows.ts +37 -14
  109. package/src/claude/desktop-3p.ts +243 -9
  110. package/src/claude/gateway-cache.ts +41 -4
  111. package/src/claude/inbound.ts +72 -3
  112. package/src/claude/model-info.ts +38 -15
  113. package/src/claude/outbound.ts +88 -21
  114. package/src/cli/access.ts +46 -3
  115. package/src/cli/account-api.ts +150 -19
  116. package/src/cli/account-auth.ts +33 -6
  117. package/src/cli/account-catalog-refresh.ts +14 -0
  118. package/src/cli/account-extended.ts +648 -28
  119. package/src/cli/account-main.ts +317 -0
  120. package/src/cli/account.ts +97 -19
  121. package/src/cli/agent.ts +53 -2
  122. package/src/cli/alias.ts +66 -0
  123. package/src/cli/capabilities-command.ts +94 -0
  124. package/src/cli/capabilities.ts +496 -0
  125. package/src/cli/claude-agent-startup-sync.ts +73 -0
  126. package/src/cli/claude-desktop.ts +60 -15
  127. package/src/cli/claude.ts +129 -35
  128. package/src/cli/codex-log-guard-doctor.ts +103 -0
  129. package/src/cli/codex-shim-autorestore.ts +2 -0
  130. package/src/cli/codex-shim-readiness.ts +76 -0
  131. package/src/cli/combo.ts +8 -0
  132. package/src/cli/config-command.ts +74 -10
  133. package/src/cli/dispatch.ts +821 -0
  134. package/src/cli/doctor.ts +440 -56
  135. package/src/cli/ensure-desired-integrations.ts +152 -0
  136. package/src/cli/export-command.ts +46 -20
  137. package/src/cli/help.ts +34 -274
  138. package/src/cli/index.ts +417 -528
  139. package/src/cli/init.ts +5 -17
  140. package/src/cli/inspect.ts +230 -0
  141. package/src/cli/integrations.ts +120 -2
  142. package/src/cli/lab.ts +607 -0
  143. package/src/cli/launcher-context.ts +77 -0
  144. package/src/cli/minimax.ts +497 -0
  145. package/src/cli/models-runtime.ts +130 -2
  146. package/src/cli/models.ts +118 -16
  147. package/src/cli/observe.ts +123 -11
  148. package/src/cli/opencode.ts +4 -2
  149. package/src/cli/provider-runtime.ts +34 -3
  150. package/src/cli/provider.ts +25 -3
  151. package/src/cli/ready.ts +301 -0
  152. package/src/cli/registry.ts +476 -0
  153. package/src/cli/root.ts +86 -0
  154. package/src/cli/route-policy.ts +92 -0
  155. package/src/cli/runtime-api.ts +57 -10
  156. package/src/cli/star-prompt.ts +71 -15
  157. package/src/cli/status.ts +51 -4
  158. package/src/cli/storage.ts +234 -0
  159. package/src/cli/system-command.ts +16 -0
  160. package/src/cli/system-restart-client.ts +146 -0
  161. package/src/cli/tray-proxy.ts +153 -6
  162. package/src/cli/usage-report.ts +184 -0
  163. package/src/cli/v2.ts +137 -18
  164. package/src/cli/version-skew.ts +46 -0
  165. package/src/cli.ts +1 -1
  166. package/src/clients/config-export.ts +1442 -23
  167. package/src/clients/effective-status.ts +30 -2
  168. package/src/clients/probes/cline.ts +81 -0
  169. package/src/codex/account-label.ts +35 -1
  170. package/src/codex/account-lifecycle.ts +130 -13
  171. package/src/codex/account-namespaces.ts +49 -3
  172. package/src/codex/account-pause.ts +2 -1
  173. package/src/codex/account-priority.ts +84 -0
  174. package/src/codex/account-store.ts +29 -2
  175. package/src/codex/account-usability.ts +25 -2
  176. package/src/codex/admission.ts +256 -0
  177. package/src/codex/affinity-debug.ts +162 -0
  178. package/src/codex/app-server-processes.ts +570 -107
  179. package/src/codex/app-server-restart-service.ts +232 -0
  180. package/src/codex/auth-api.ts +893 -246
  181. package/src/codex/auth-collision.ts +5 -3
  182. package/src/codex/auth-context.ts +348 -32
  183. package/src/codex/autostart-health.ts +24 -1
  184. package/src/codex/catalog/account-models.ts +67 -0
  185. package/src/codex/catalog/aggregation.ts +86 -10
  186. package/src/codex/catalog/bundled.ts +331 -33
  187. package/src/codex/catalog/effort.ts +138 -32
  188. package/src/codex/catalog/filesystem-evidence.ts +302 -0
  189. package/src/codex/catalog/kinds.ts +2 -0
  190. package/src/codex/catalog/metadata.ts +555 -45
  191. package/src/codex/catalog/native-models.ts +75 -0
  192. package/src/codex/catalog/parsing.ts +269 -30
  193. package/src/codex/catalog/provider-fetch.ts +1626 -144
  194. package/src/codex/catalog/sync.ts +1576 -187
  195. package/src/codex/catalog-admission.ts +199 -0
  196. package/src/codex/catalog-refresh-status.ts +105 -0
  197. package/src/codex/catalog-write-serialization.ts +242 -0
  198. package/src/codex/catalog.ts +6 -3
  199. package/src/codex/codex-write-lock.ts +386 -0
  200. package/src/codex/convergence-types.ts +614 -0
  201. package/src/codex/convergence.ts +676 -0
  202. package/src/codex/coordinator-doctor.ts +332 -0
  203. package/src/codex/custom-model-catalog-migration.ts +176 -0
  204. package/src/codex/desired-state.ts +230 -0
  205. package/src/codex/desktop-app-restart.ts +355 -0
  206. package/src/codex/features.ts +636 -39
  207. package/src/codex/generation.ts +202 -0
  208. package/src/codex/history-job.ts +436 -0
  209. package/src/codex/history-lock.ts +242 -0
  210. package/src/codex/history-manifest.ts +112 -0
  211. package/src/codex/history-migration-guardian.ts +30 -24
  212. package/src/codex/history-provider.ts +1016 -235
  213. package/src/codex/history-transition.ts +105 -0
  214. package/src/codex/history-worker.ts +223 -0
  215. package/src/codex/inject-coordination.ts +373 -0
  216. package/src/codex/inject.ts +1114 -152
  217. package/src/codex/injected-marker.ts +37 -3
  218. package/src/codex/integration-record.ts +266 -0
  219. package/src/codex/internal/catalog-writer.ts +203 -0
  220. package/src/codex/internal/history-writer.ts +81 -0
  221. package/src/codex/journal.ts +66 -4
  222. package/src/codex/log-guard/inspect.ts +524 -0
  223. package/src/codex/log-guard/lock.ts +150 -0
  224. package/src/codex/log-guard/maintenance.ts +403 -0
  225. package/src/codex/log-guard/path-safety.ts +88 -0
  226. package/src/codex/log-guard/policy.ts +44 -0
  227. package/src/codex/log-guard/processes.ts +205 -0
  228. package/src/codex/log-guard/protection.ts +489 -0
  229. package/src/codex/log-guard/sqlite-errors.ts +9 -0
  230. package/src/codex/main-account-cache.ts +24 -0
  231. package/src/codex/main-account.ts +29 -1
  232. package/src/codex/management-convergence.ts +167 -0
  233. package/src/codex/model-cache.ts +56 -10
  234. package/src/codex/model-entitlements.ts +360 -0
  235. package/src/codex/native-main-admission.ts +47 -0
  236. package/src/codex/native-main-auth-temp.ts +187 -0
  237. package/src/codex/native-main-claim.ts +178 -0
  238. package/src/codex/native-main-lock-file.ts +162 -0
  239. package/src/codex/native-main-owner.ts +329 -0
  240. package/src/codex/native-profile-api.ts +247 -0
  241. package/src/codex/native-profile-manager.ts +1531 -0
  242. package/src/codex/native-profile-processes.ts +121 -0
  243. package/src/codex/native-profile-recovery.ts +99 -0
  244. package/src/codex/native-profile-stage-store.ts +387 -0
  245. package/src/codex/native-profile-startup.ts +622 -0
  246. package/src/codex/native-profile-store.ts +855 -0
  247. package/src/codex/native-profile-types.ts +120 -0
  248. package/src/codex/native-residue.ts +675 -0
  249. package/src/codex/paths.ts +80 -1
  250. package/src/codex/plan-from-token.ts +140 -0
  251. package/src/codex/plan.ts +40 -0
  252. package/src/codex/plugins-doctor.ts +1 -1
  253. package/src/codex/pool-rotation.ts +74 -4
  254. package/src/codex/project-config-warnings.ts +20 -6
  255. package/src/codex/prompt-journal.ts +352 -0
  256. package/src/codex/prompt-layers.ts +1313 -0
  257. package/src/codex/prompt-lock.ts +143 -0
  258. package/src/codex/prompt-text-probe.ts +238 -0
  259. package/src/codex/quota-rejection.ts +298 -0
  260. package/src/codex/quota.ts +264 -21
  261. package/src/codex/refresh.ts +11 -2
  262. package/src/codex/reset-credit-recovery.ts +1044 -0
  263. package/src/codex/routing.ts +514 -94
  264. package/src/codex/runtime.ts +159 -38
  265. package/src/codex/shim.ts +1065 -31
  266. package/src/codex/subagent-model-fallback.ts +437 -43
  267. package/src/codex/sync.ts +191 -2
  268. package/src/codex/transition-state.ts +720 -0
  269. package/src/codex/upstream-host-health.ts +368 -0
  270. package/src/codex/user-identity.ts +557 -0
  271. package/src/codex/warmup.ts +187 -81
  272. package/src/codex/write-coordination.ts +114 -0
  273. package/src/combos/failover.ts +47 -0
  274. package/src/combos/index.ts +4 -0
  275. package/src/combos/request.ts +32 -0
  276. package/src/combos/types.ts +91 -9
  277. package/src/compatibility/index.ts +26 -0
  278. package/src/compatibility/manifest.ts +253 -0
  279. package/src/compatibility/openai-responses.ts +121 -0
  280. package/src/config/atomic-write.ts +219 -0
  281. package/src/config/paths.ts +40 -0
  282. package/src/config/process-state.ts +309 -0
  283. package/src/config/provider-name.ts +24 -0
  284. package/src/config/provider-validation.ts +177 -0
  285. package/src/config/rebase-provenance.ts +68 -0
  286. package/src/config.ts +1713 -709
  287. package/src/generated/compatibility-version.json +3324 -0
  288. package/src/generated/{jawcode-model-metadata.ts → model-metadata.ts} +19 -17
  289. package/src/grok/inject.ts +16 -5
  290. package/src/grok/inspect.ts +45 -0
  291. package/src/grok/sync.ts +2 -2
  292. package/src/images/loop.ts +157 -33
  293. package/src/images/plan.ts +24 -13
  294. package/src/integrations/config-io.ts +269 -0
  295. package/src/integrations/journal.ts +315 -0
  296. package/src/integrations/merge.ts +135 -0
  297. package/src/integrations/mutation-flight.ts +71 -0
  298. package/src/integrations/native/ownership-preflight.ts +202 -0
  299. package/src/integrations/omp-yaml-source.ts +358 -0
  300. package/src/integrations/owned-refresh.ts +74 -0
  301. package/src/integrations/ownership-policy.ts +160 -0
  302. package/src/integrations/ownership.ts +155 -0
  303. package/src/integrations/registry.ts +166 -0
  304. package/src/integrations/serialize.ts +314 -0
  305. package/src/integrations/state.ts +433 -0
  306. package/src/integrations/store.ts +103 -0
  307. package/src/integrations/writer-lock.ts +98 -0
  308. package/src/integrations/writer.ts +715 -0
  309. package/src/lab/artifacts/sanitize.ts +586 -0
  310. package/src/lab/artifacts/secure-fs.ts +475 -0
  311. package/src/lab/artifacts/store.ts +310 -0
  312. package/src/lab/automation/budgets.ts +78 -0
  313. package/src/lab/automation/config-persistence.ts +256 -0
  314. package/src/lab/automation/constants.ts +39 -0
  315. package/src/lab/automation/cooldown.ts +103 -0
  316. package/src/lab/automation/dispatch.ts +211 -0
  317. package/src/lab/automation/index.ts +13 -0
  318. package/src/lab/automation/orchestrator.ts +499 -0
  319. package/src/lab/automation/persistence.ts +512 -0
  320. package/src/lab/automation/planner.ts +371 -0
  321. package/src/lab/automation/policy.ts +136 -0
  322. package/src/lab/automation/queue.ts +191 -0
  323. package/src/lab/automation/recovery.ts +24 -0
  324. package/src/lab/automation/route-context.ts +21 -0
  325. package/src/lab/automation/run-key.ts +44 -0
  326. package/src/lab/automation/runs-query.ts +34 -0
  327. package/src/lab/automation/types.ts +160 -0
  328. package/src/lab/conformance/assertion.ts +325 -0
  329. package/src/lab/conformance/digest.ts +22 -0
  330. package/src/lab/conformance/executor.ts +741 -0
  331. package/src/lab/conformance/fixture-provider.ts +27 -0
  332. package/src/lab/conformance/fixtures/live-v1-cases.json +175 -0
  333. package/src/lab/conformance/fixtures/protocol-v1-cases.json +461 -0
  334. package/src/lab/conformance/harness-budget.ts +47 -0
  335. package/src/lab/conformance/index.ts +5 -0
  336. package/src/lab/conformance/jcs.ts +64 -0
  337. package/src/lab/conformance/json-pointer.ts +39 -0
  338. package/src/lab/conformance/manifest.ts +180 -0
  339. package/src/lab/conformance/mcp-stub.ts +179 -0
  340. package/src/lab/conformance/negative-controls.ts +164 -0
  341. package/src/lab/conformance/observation.ts +355 -0
  342. package/src/lab/conformance/runner.ts +68 -0
  343. package/src/lab/conformance/sse-normalize.ts +59 -0
  344. package/src/lab/conformance/suite-manifest.ts +78 -0
  345. package/src/lab/conformance/types.ts +214 -0
  346. package/src/lab/constants.ts +126 -0
  347. package/src/lab/digest.ts +64 -0
  348. package/src/lab/events/errors.ts +9 -0
  349. package/src/lab/events/limits.ts +117 -0
  350. package/src/lab/events/types.ts +229 -0
  351. package/src/lab/events/validate.ts +781 -0
  352. package/src/lab/fabric/constants.ts +40 -0
  353. package/src/lab/fabric/executor.ts +492 -0
  354. package/src/lab/fabric/index.ts +80 -0
  355. package/src/lab/fabric/manifest.ts +222 -0
  356. package/src/lab/fabric/observe.ts +489 -0
  357. package/src/lab/fabric/patch.ts +79 -0
  358. package/src/lab/fabric/producer-child.ts +139 -0
  359. package/src/lab/fabric/producer-isolate.ts +276 -0
  360. package/src/lab/fabric/producer-protocol.ts +61 -0
  361. package/src/lab/fabric/scratch.ts +439 -0
  362. package/src/lab/fabric/subject.ts +106 -0
  363. package/src/lab/fabric/types.ts +134 -0
  364. package/src/lab/fabric/verifier.ts +98 -0
  365. package/src/lab/index.ts +54 -0
  366. package/src/lab/ledger/artifact-refs.ts +127 -0
  367. package/src/lab/ledger/invalidation.ts +136 -0
  368. package/src/lab/ledger/purge.ts +310 -0
  369. package/src/lab/ledger/store.ts +532 -0
  370. package/src/lab/live/credential-lease.ts +53 -0
  371. package/src/lab/live/destination.ts +155 -0
  372. package/src/lab/live/executor.ts +336 -0
  373. package/src/lab/live/inert-tools.ts +56 -0
  374. package/src/lab/live/manifest.ts +85 -0
  375. package/src/lab/live/mcp-loopback.ts +57 -0
  376. package/src/lab/live/runner.ts +19 -0
  377. package/src/lab/live/sandbox.ts +61 -0
  378. package/src/lab/live/suite-manifest.ts +41 -0
  379. package/src/lab/live/transport.ts +118 -0
  380. package/src/lab/live/types.ts +197 -0
  381. package/src/lab/observe/from-conformance.ts +301 -0
  382. package/src/lab/observe/from-live.ts +117 -0
  383. package/src/lab/paths.ts +153 -0
  384. package/src/lab/projection/rebuild.ts +495 -0
  385. package/src/lab/projection/schema.ts +135 -0
  386. package/src/lab/projection/verdicts.ts +474 -0
  387. package/src/lab/projection/verification.ts +412 -0
  388. package/src/lab/public/bundle.ts +217 -0
  389. package/src/lab/public/community-authority.ts +175 -0
  390. package/src/lab/public/community-files.ts +29 -0
  391. package/src/lab/public/community.ts +479 -0
  392. package/src/lab/public/file-safety.ts +155 -0
  393. package/src/lab/public/ids.ts +26 -0
  394. package/src/lab/public/index.ts +16 -0
  395. package/src/lab/public/mutation-lock.ts +424 -0
  396. package/src/lab/public/operator.ts +353 -0
  397. package/src/lab/public/origin-purge.ts +79 -0
  398. package/src/lab/public/origin.ts +203 -0
  399. package/src/lab/public/privacy.ts +143 -0
  400. package/src/lab/public/private-file.ts +261 -0
  401. package/src/lab/public/project.ts +124 -0
  402. package/src/lab/public/purge-test-fault.ts +21 -0
  403. package/src/lab/public/purge.ts +223 -0
  404. package/src/lab/public/registry.ts +44 -0
  405. package/src/lab/public/revocation.ts +252 -0
  406. package/src/lab/public/signature.ts +243 -0
  407. package/src/lab/public/storage.ts +105 -0
  408. package/src/lab/public/strict-json.ts +206 -0
  409. package/src/lab/public/time.ts +26 -0
  410. package/src/lab/public/types.ts +172 -0
  411. package/src/lab/public/validate.ts +391 -0
  412. package/src/lab/query/catalog.ts +101 -0
  413. package/src/lab/query/connection.ts +107 -0
  414. package/src/lab/query/constants.ts +4 -0
  415. package/src/lab/query/cursor.ts +132 -0
  416. package/src/lab/query/dto-map.ts +277 -0
  417. package/src/lab/query/errors.ts +22 -0
  418. package/src/lab/query/freshness.ts +53 -0
  419. package/src/lab/query/index.ts +45 -0
  420. package/src/lab/query/latest-observation.ts +59 -0
  421. package/src/lab/query/passive-production.ts +159 -0
  422. package/src/lab/query/queries.ts +444 -0
  423. package/src/lab/query/types.ts +266 -0
  424. package/src/lab/subject/behavior-fingerprint.ts +77 -0
  425. package/src/lab/subject/installation-salt.ts +112 -0
  426. package/src/lab/subject/protocol-subject.ts +80 -0
  427. package/src/lab/subject/route-subject.ts +74 -0
  428. package/src/lib/admin-secrets.ts +24 -0
  429. package/src/lib/app-owned-memory-stores.ts +22 -0
  430. package/src/lib/bounded-body.ts +167 -11
  431. package/src/lib/bun-runtime.ts +125 -12
  432. package/src/lib/bun-stream-caps.ts +13 -9
  433. package/src/lib/codex-restart-contract.ts +120 -0
  434. package/src/lib/config-ownership.ts +26 -2
  435. package/src/lib/destination-policy.ts +65 -1
  436. package/src/lib/errors.ts +80 -5
  437. package/src/lib/fabric-task-execution-authority.ts +7 -0
  438. package/src/lib/fabric-task-host.ts +29 -0
  439. package/src/lib/lab-activation.ts +223 -0
  440. package/src/lib/lab-live-execution-authority.ts +13 -0
  441. package/src/lib/lab-live-host.ts +30 -0
  442. package/src/lib/lab-live-pinned-sender.ts +56 -0
  443. package/src/lib/lab-live-route-production.ts +130 -0
  444. package/src/lib/lab-passive-linker-registration.ts +26 -0
  445. package/src/lib/local-management-attestation.ts +51 -0
  446. package/src/lib/local-management-capability.ts +100 -0
  447. package/src/lib/local-provider-reload-contract.ts +100 -0
  448. package/src/lib/optional-shutdown-hooks.ts +57 -0
  449. package/src/lib/package-tree-integrity.ts +101 -0
  450. package/src/lib/pinned-http.ts +145 -26
  451. package/src/lib/process-control.ts +6 -2
  452. package/src/lib/provider-outbound.ts +49 -9
  453. package/src/lib/redact.ts +419 -3
  454. package/src/lib/self-launch-argv.ts +15 -0
  455. package/src/lib/server-resource-ownership.ts +71 -0
  456. package/src/lib/service-secrets.ts +15 -0
  457. package/src/lib/shadow-call.ts +35 -4
  458. package/src/lib/sse-decoder.ts +41 -0
  459. package/src/lib/state-store-registrations.ts +10 -2
  460. package/src/lib/system-restart-contract.ts +73 -0
  461. package/src/lib/token-estimate.ts +19 -2
  462. package/src/lib/tool-argument-integers.ts +253 -0
  463. package/src/lib/translator-budget.ts +44 -0
  464. package/src/lib/upstream-http-version.ts +57 -0
  465. package/src/lib/upstream-reachability.ts +95 -0
  466. package/src/lib/upstream-retry.ts +156 -3
  467. package/src/lib/windows-atomic-replace.ts +155 -0
  468. package/src/lib/windows-elevation.ts +70 -2
  469. package/src/lib/windows-secret-acl.ts +409 -69
  470. package/src/lib/windows-service-wrappers.ts +72 -0
  471. package/src/lib/windows-text.ts +106 -0
  472. package/src/lib/windows-user-principal.ts +341 -0
  473. package/src/lib/winsw.ts +33 -5
  474. package/src/oauth/account-import/google-antigravity-adapter.ts +74 -0
  475. package/src/oauth/account-import/index.ts +15 -0
  476. package/src/oauth/account-import/parser.ts +83 -0
  477. package/src/oauth/account-import/registry.ts +18 -0
  478. package/src/oauth/account-import/service.ts +75 -0
  479. package/src/oauth/account-import/types.ts +91 -0
  480. package/src/oauth/anthropic.ts +12 -1
  481. package/src/oauth/callback-server.ts +30 -4
  482. package/src/oauth/chatgpt.ts +12 -1
  483. package/src/oauth/cline.ts +203 -0
  484. package/src/oauth/command-code.ts +239 -0
  485. package/src/oauth/cursor.ts +46 -5
  486. package/src/oauth/generic-account-failover.ts +231 -0
  487. package/src/oauth/google-antigravity.ts +35 -3
  488. package/src/oauth/health.ts +20 -12
  489. package/src/oauth/index.ts +462 -71
  490. package/src/oauth/key-providers.ts +25 -0
  491. package/src/oauth/kimi.ts +25 -3
  492. package/src/oauth/kiro.ts +95 -6
  493. package/src/oauth/local-token-detect.ts +13 -2
  494. package/src/oauth/log.ts +3 -1
  495. package/src/oauth/login-cli.ts +88 -28
  496. package/src/oauth/nous.ts +798 -0
  497. package/src/oauth/open-browser-choice.ts +26 -0
  498. package/src/oauth/store.ts +133 -26
  499. package/src/oauth/token-guardian.ts +9 -3
  500. package/src/oauth/types.ts +15 -0
  501. package/src/pi/models.ts +2 -2
  502. package/src/providers/alibaba-region-migration.ts +1 -1
  503. package/src/providers/antigravity-models.ts +521 -31
  504. package/src/providers/auto-compact-budget.ts +65 -0
  505. package/src/providers/base-url-choices.ts +10 -0
  506. package/src/providers/codex-capacity.ts +292 -0
  507. package/src/providers/command-code-efforts.ts +176 -0
  508. package/src/providers/context-cap.ts +26 -8
  509. package/src/providers/cursor-pool.ts +72 -0
  510. package/src/providers/default-aliases.ts +65 -0
  511. package/src/providers/derive.ts +281 -6
  512. package/src/providers/fastwire.ts +507 -0
  513. package/src/providers/free-directory.ts +10 -7
  514. package/src/providers/google-vertex-location.ts +14 -0
  515. package/src/providers/key-failover.ts +71 -3
  516. package/src/providers/label.ts +35 -2
  517. package/src/providers/model-discovery-limits.ts +16 -0
  518. package/src/providers/model-discovery.ts +115 -22
  519. package/src/providers/model-presets.ts +119 -0
  520. package/src/providers/model-rename-migration.ts +255 -0
  521. package/src/providers/model-rename-startup.ts +28 -0
  522. package/src/providers/new-model-policy.ts +146 -0
  523. package/src/providers/openai-sidecar.ts +72 -4
  524. package/src/providers/openai-tier-startup.ts +31 -2
  525. package/src/providers/openai-tiers.ts +119 -4
  526. package/src/providers/openai-virtual-models.ts +1 -0
  527. package/src/providers/opencode-zen-rate-limit.ts +102 -0
  528. package/src/providers/provider-id-rewrite.ts +30 -0
  529. package/src/providers/quota.ts +1379 -40
  530. package/src/providers/registry.ts +1545 -112
  531. package/src/providers/request-pacing.ts +310 -0
  532. package/src/providers/service-tier.ts +277 -0
  533. package/src/providers/slug-codec.ts +94 -6
  534. package/src/providers/static-model-discovery.ts +86 -0
  535. package/src/providers/xai-responses-opt-in.ts +15 -0
  536. package/src/providers/xai-transport.ts +32 -4
  537. package/src/reasoning-effort.ts +68 -3
  538. package/src/responses/apply-patch-envelope.ts +63 -0
  539. package/src/responses/code-mode-helper-compat.ts +50 -0
  540. package/src/responses/compaction.ts +26 -1
  541. package/src/responses/custom-tool-compat.ts +384 -0
  542. package/src/responses/hosted-tool-policy.ts +9 -0
  543. package/src/responses/namespace-tool-compat.ts +435 -0
  544. package/src/responses/parser.ts +225 -38
  545. package/src/responses/provider-continuation.ts +98 -0
  546. package/src/responses/provider-opaque-metadata.ts +73 -0
  547. package/src/responses/reasoning-envelope.ts +9 -1
  548. package/src/responses/reasoning-replay-cache.ts +504 -0
  549. package/src/responses/schema.ts +12 -2
  550. package/src/responses/spill-store.ts +75 -10
  551. package/src/responses/state.ts +659 -31
  552. package/src/responses/thought-signature-replay.ts +364 -0
  553. package/src/responses/tool-search-compat.ts +301 -0
  554. package/src/responses/truncated-stop-reason.ts +60 -0
  555. package/src/responses/turn-termination.ts +107 -0
  556. package/src/router.ts +405 -31
  557. package/src/routing/analytics.ts +379 -0
  558. package/src/routing/capability.ts +244 -0
  559. package/src/routing/compatibility/assemble.ts +73 -0
  560. package/src/routing/compatibility/behavior.ts +278 -0
  561. package/src/routing/compatibility/catalog.ts +99 -0
  562. package/src/routing/compatibility/endpoint.ts +52 -0
  563. package/src/routing/compatibility/lab-evidence-provider.ts +130 -0
  564. package/src/routing/compatibility/policy.ts +181 -0
  565. package/src/routing/compatibility/provider-slot.ts +56 -0
  566. package/src/routing/compatibility/reader.ts +110 -0
  567. package/src/routing/compatibility/subject.ts +191 -0
  568. package/src/routing/compatibility/types.ts +64 -0
  569. package/src/routing/compatibility/version.ts +104 -0
  570. package/src/routing/cost.ts +77 -0
  571. package/src/routing/evaluator.ts +495 -0
  572. package/src/routing/health.ts +412 -0
  573. package/src/routing/history/cursor.ts +43 -0
  574. package/src/routing/history/indexer.ts +605 -0
  575. package/src/routing/history/schema.ts +72 -0
  576. package/src/routing/profile-namespace.ts +15 -0
  577. package/src/routing/profile.ts +547 -0
  578. package/src/routing/quota.ts +155 -0
  579. package/src/routing/request-evidence.ts +45 -0
  580. package/src/routing/trace.ts +776 -0
  581. package/src/server/adapter-resolve.ts +2 -29
  582. package/src/server/auth-cors.ts +289 -48
  583. package/src/server/background-lifecycle.ts +182 -0
  584. package/src/server/chat-completions.ts +154 -72
  585. package/src/server/chat-native-sse.ts +331 -0
  586. package/src/server/chat-native.ts +445 -0
  587. package/src/server/claude-messages.ts +160 -44
  588. package/src/server/direct-local-http.ts +347 -0
  589. package/src/server/effort-policy.ts +18 -0
  590. package/src/server/github-copilot-responses-repair.ts +338 -0
  591. package/src/server/gui-static.ts +39 -10
  592. package/src/server/images.ts +94 -12
  593. package/src/server/index.ts +1021 -190
  594. package/src/server/lifecycle.ts +336 -14
  595. package/src/server/live.ts +136 -17
  596. package/src/server/local-management-read-client.ts +90 -0
  597. package/src/server/local-provider-reload-client.ts +137 -0
  598. package/src/server/management/agent-settings-routes.ts +433 -116
  599. package/src/server/management/api-key-usage.ts +31 -5
  600. package/src/server/management/body.ts +6 -0
  601. package/src/server/management/codex-prompt-routes.ts +570 -0
  602. package/src/server/management/combo-routes.ts +64 -25
  603. package/src/server/management/config-routes.ts +511 -51
  604. package/src/server/management/context.ts +89 -2
  605. package/src/server/management/integration-routes.ts +498 -0
  606. package/src/server/management/lab-automation-routes.ts +206 -0
  607. package/src/server/management/lab-routes.ts +563 -0
  608. package/src/server/management/logs-usage-routes.ts +127 -52
  609. package/src/server/management/model-routes.ts +461 -133
  610. package/src/server/management/model-rows.ts +167 -0
  611. package/src/server/management/native-integration-routes.ts +770 -0
  612. package/src/server/management/oauth-account-routes.ts +90 -8
  613. package/src/server/management/provider-capability-config.ts +48 -0
  614. package/src/server/management/provider-routes.ts +836 -159
  615. package/src/server/management/request-history-routes.ts +191 -0
  616. package/src/server/management/route-registry.ts +332 -0
  617. package/src/server/management/routing-analytics-routes.ts +74 -0
  618. package/src/server/management/routing-profile-routes.ts +380 -0
  619. package/src/server/management/shared.ts +27 -11
  620. package/src/server/management/sidebar-routes.ts +47 -31
  621. package/src/server/management/storage-log-guard-routes.ts +186 -0
  622. package/src/server/management/sync-response.ts +69 -0
  623. package/src/server/management/system-restart.ts +289 -33
  624. package/src/server/management/system-routes.ts +77 -0
  625. package/src/server/management/usage-summary-cache.ts +9 -1
  626. package/src/server/management/vision-sidecar-options.ts +167 -0
  627. package/src/server/management/web-search-sidecar-options.ts +120 -0
  628. package/src/server/management-api.ts +126 -23
  629. package/src/server/management-auth.ts +220 -5
  630. package/src/server/passive-route-linker.ts +66 -0
  631. package/src/server/port-reclaim.ts +1 -1
  632. package/src/server/ports.ts +41 -1
  633. package/src/server/proxy-liveness.ts +161 -10
  634. package/src/server/readiness.ts +99 -0
  635. package/src/server/relay-eager.ts +221 -54
  636. package/src/server/relay.ts +479 -87
  637. package/src/server/request-decompress.ts +113 -6
  638. package/src/server/request-log-conversation.ts +51 -0
  639. package/src/server/request-log.ts +298 -31
  640. package/src/server/responses/agent-task-recovery-cache.ts +143 -0
  641. package/src/server/responses/agent-task-recovery.ts +465 -0
  642. package/src/server/responses/codex-auth-error.ts +55 -0
  643. package/src/server/responses/collaboration.ts +239 -36
  644. package/src/server/responses/combo-stream-preflight.ts +178 -0
  645. package/src/server/responses/compact.ts +444 -72
  646. package/src/server/responses/core.ts +3579 -436
  647. package/src/server/responses/empty-completion-guard.ts +333 -0
  648. package/src/server/responses/encrypted-payload.ts +62 -39
  649. package/src/server/responses/fetch-helpers.ts +89 -99
  650. package/src/server/responses/input-admission.ts +187 -0
  651. package/src/server/responses/pacing-overload.ts +13 -0
  652. package/src/server/responses/passthrough-error.ts +33 -9
  653. package/src/server/responses/policy-fallback.ts +178 -0
  654. package/src/server/responses/responses-field-backfill.ts +343 -0
  655. package/src/server/responses/terminal-guard.ts +26 -5
  656. package/src/server/responses/upstream-error.ts +5 -0
  657. package/src/server/responses/ws-upstream.ts +381 -0
  658. package/src/server/responses-custom-tool-repair.ts +363 -0
  659. package/src/server/responses-item-id-repair.ts +54 -6
  660. package/src/server/responses-json-events.ts +90 -0
  661. package/src/server/responses-model-rewrite.ts +29 -0
  662. package/src/server/responses-reasoning-summary-rewrite.ts +178 -0
  663. package/src/server/responses-snapshot-repair.ts +621 -0
  664. package/src/server/responses-terminal-repair.ts +363 -0
  665. package/src/server/responses-tool-search-repair.ts +267 -0
  666. package/src/server/responses-undeclared-tool-guard.ts +376 -0
  667. package/src/server/responses.ts +18 -2
  668. package/src/server/search.ts +78 -13
  669. package/src/server/sse-frame-buffer.ts +319 -0
  670. package/src/server/sse-payload-rewrite.ts +110 -22
  671. package/src/server/startup-action-control.ts +8 -1
  672. package/src/server/startup-health-cache.ts +19 -1
  673. package/src/server/system-env.ts +80 -9
  674. package/src/server/ws-bridge.ts +53 -40
  675. package/src/service-manager-probe.ts +892 -0
  676. package/src/service.ts +1153 -94
  677. package/src/sidecar/auth.ts +92 -0
  678. package/src/sidecar/candidates.ts +83 -0
  679. package/src/storage/cleanup.ts +2 -2
  680. package/src/storage/policy-job.ts +14 -4
  681. package/src/storage/policy.ts +88 -23
  682. package/src/storage/scanner.ts +1 -1
  683. package/src/storage/worker-lifecycle.ts +14 -14
  684. package/src/tray/windows-tray.ps1 +83 -9
  685. package/src/tray/windows.ts +43 -16
  686. package/src/types/accounts.ts +37 -0
  687. package/src/types/config.ts +911 -0
  688. package/src/types/provider.ts +614 -0
  689. package/src/types/request.ts +397 -0
  690. package/src/types/tools.ts +236 -0
  691. package/src/types/wire.ts +80 -0
  692. package/src/types.ts +107 -1237
  693. package/src/update/index.ts +37 -23
  694. package/src/update/job.ts +445 -68
  695. package/src/update/notify.ts +12 -6
  696. package/src/update/npm-cache-preflight.d.mts +47 -0
  697. package/src/update/npm-cache-preflight.mjs +201 -0
  698. package/src/update/transactional-install.d.mts +22 -0
  699. package/src/update/transactional-install.mjs +266 -0
  700. package/src/usage/cost.ts +0 -0
  701. package/src/usage/expected-prices.ts +287 -16
  702. package/src/usage/log.ts +666 -50
  703. package/src/usage/summary.ts +388 -17
  704. package/src/usage/user-cost-overlay-reconciler.ts +313 -0
  705. package/src/usage/user-cost-overlays.ts +314 -0
  706. package/src/vision/anthropic-describe.ts +10 -6
  707. package/src/vision/backends.ts +97 -0
  708. package/src/vision/describe.ts +27 -16
  709. package/src/vision/eligibility.ts +250 -0
  710. package/src/vision/index.ts +238 -24
  711. package/src/vision/reasoning.ts +55 -0
  712. package/src/vision/routed-describe.ts +175 -0
  713. package/src/vision/timeout-bounds.ts +9 -0
  714. package/src/web-search/anthropic-executor.ts +13 -7
  715. package/src/web-search/backends.ts +108 -0
  716. package/src/web-search/exa-executor.ts +119 -0
  717. package/src/web-search/executor.ts +21 -6
  718. package/src/web-search/gemini-executor.ts +141 -0
  719. package/src/web-search/index.ts +150 -15
  720. package/src/web-search/loop.ts +284 -54
  721. package/src/web-search/parse.ts +125 -30
  722. package/src/web-search/sources.ts +60 -0
  723. package/src/web-search/xai-executor.ts +219 -0
  724. package/gui/dist/assets/index-DTpMHS4F.js +0 -67
  725. package/gui/dist/assets/index-ZNVDE3C7.css +0 -1
  726. package/gui/dist/provider-icons/antigravity.svg +0 -1
  727. package/gui/dist/provider-icons/claude.svg +0 -1
  728. package/gui/dist/provider-icons/copilot.svg +0 -1
  729. package/gui/dist/provider-icons/cursor.svg +0 -2
  730. package/gui/dist/provider-icons/gemini.svg +0 -1
  731. package/gui/dist/provider-icons/grok-color.svg +0 -1
  732. package/gui/dist/provider-icons/kiro.svg +0 -14
  733. package/src/cli/internal-dispatch.ts +0 -20
@@ -0,0 +1,230 @@
1
+ /**
2
+ * Durable desired state for the native Codex integration.
3
+ *
4
+ * The switch itself was never the hard part — `ocx restore` already returns Codex
5
+ * to its native path without stopping the proxy. What was missing is that the
6
+ * decision did not survive: `ocx start` force-synced unconditionally, so an OFF
7
+ * lasted exactly until the next start. That is the defect this module closes, and
8
+ * it is the same one Grok's shipped toggle still has.
9
+ *
10
+ * ABSENT MEANS ON. A user who never touched a switch, a config written by an
11
+ * older binary, and an explicit `true` are the same state, and none of them may
12
+ * be read as "the user turned this off". Only an explicit `false` is OFF.
13
+ *
14
+ * This module does NOT own linearization. The plan that predates
15
+ * `src/codex/user-identity.ts` proposed a second per-home lock at
16
+ * `tmpdir()/opencodex-native-locks/sha256(home).sqlite`; that keys on the home
17
+ * alone, carries no proof of effective user, and would collide or split
18
+ * depending on the temp root. Convergence takes the write lock; this module only
19
+ * records intent through the config coordinator.
20
+ *
21
+ * Design record: devlog/_fin/260803_codex_desktop_toggle/030_desired_state.md.
22
+ */
23
+ import { deleteConfigTopLevelKey, loadConfig, mutatePersistedConfig } from "../config";
24
+ import type { OcxClientIntegrationsConfig, OcxConfig } from "../types";
25
+ import { runStartupReadinessSync, type ReadinessGate, type SyncOutcomeLike } from "../server/readiness";
26
+
27
+ /** Clients whose durable intent this module owns. */
28
+ export type DurableIntentClientId = keyof OcxClientIntegrationsConfig;
29
+
30
+ /** Injectable for tests; production passes the real sync. */
31
+ /**
32
+ * The startup sync result the caller needs to decide whether anything was
33
+ * actually written (#1046). It used to be `unknown`, so "a write happened" was
34
+ * not observable at the startup boundary and no post-write action could be
35
+ * gated on it.
36
+ */
37
+ export interface CodexStartupSyncOutcome {
38
+ catalogWritten: boolean;
39
+ cacheSynced: boolean;
40
+ /** Readiness-observable fields carried by the raw startup sync. */
41
+ ok?: boolean;
42
+ warning?: string;
43
+ }
44
+ export type CodexStartupSync = (port: number) => Promise<SyncOutcomeLike | undefined>;
45
+
46
+ export type CodexDesiredStateResult =
47
+ | { readonly ok: true; readonly status: "committed" | "unchanged"; readonly enabled: boolean }
48
+ | {
49
+ readonly ok: false;
50
+ readonly reason: "missing" | "invalid" | "conflict";
51
+ readonly retryable: boolean;
52
+ readonly message: string;
53
+ };
54
+
55
+ /**
56
+ * Is native Codex integration wanted?
57
+ *
58
+ * Takes the config rather than reading it, so a caller that already holds an
59
+ * admitted snapshot cannot accidentally answer from a fresher one — the whole
60
+ * point of admission is that one decision uses one set of bytes.
61
+ */
62
+ export function integrationEnabled(
63
+ config: Pick<OcxConfig, "clientIntegrations">,
64
+ client: DurableIntentClientId,
65
+ ): boolean {
66
+ return config.clientIntegrations?.[client] !== false;
67
+ }
68
+
69
+ export function codexIntegrationEnabled(config: Pick<OcxConfig, "clientIntegrations">): boolean {
70
+ return integrationEnabled(config, "codex");
71
+ }
72
+
73
+ /** Whether a Codex sync is permitted for this admitted config snapshot. */
74
+ export function shouldSyncCodexOnStart(config: Pick<OcxConfig, "clientIntegrations">): boolean {
75
+ return codexIntegrationEnabled(config);
76
+ }
77
+
78
+ /**
79
+ * Grok's toggle SHIPPED without this, which is the bug: it strips the fence in
80
+ * `~/.grok/config.toml` and records nothing, so the next `ocx start` calls
81
+ * `syncGrokConfig` unconditionally and writes the fence straight back.
82
+ */
83
+ export function grokIntegrationEnabled(config: Pick<OcxConfig, "clientIntegrations">): boolean {
84
+ return integrationEnabled(config, "grok");
85
+ }
86
+
87
+ /** The same question when no snapshot is in hand. Reads the persisted config. */
88
+ export function codexIntegrationEnabledNow(): boolean {
89
+ return codexIntegrationEnabled(loadConfig());
90
+ }
91
+
92
+ /**
93
+ * Persist the desired state, touching only that one field.
94
+ *
95
+ * Field-scoped on purpose: the callback runs on a freshly rebased snapshot inside
96
+ * the config coordinator, so a concurrent provider or model edit is preserved
97
+ * instead of being clobbered by a whole-config write built from a stale read.
98
+ */
99
+ export function setIntegrationEnabled(
100
+ client: DurableIntentClientId,
101
+ enabled: boolean,
102
+ ): CodexDesiredStateResult {
103
+ const outcome = mutatePersistedConfig(config => {
104
+ const current = integrationEnabled(config, client);
105
+ if (current === enabled) return { changed: false, value: enabled };
106
+ const integrations = { ...(config.clientIntegrations ?? {}) };
107
+ if (enabled) {
108
+ // ON is the absence, not a stored `true`: writing `true` would make an
109
+ // untouched config and a re-enabled one differ in bytes for no reason, and
110
+ // every later reader has to treat them identically anyway.
111
+ delete integrations[client];
112
+ } else {
113
+ integrations[client] = false;
114
+ }
115
+ // Drop the key entirely once nothing is left in it, so enabling twice does
116
+ // not leave `"clientIntegrations": {}` behind in the user's file.
117
+ if (Object.keys(integrations).length === 0) deleteConfigTopLevelKey(config, "clientIntegrations");
118
+ else config.clientIntegrations = integrations;
119
+ return { changed: true, value: enabled };
120
+ });
121
+
122
+ if (outcome.status !== "unavailable") {
123
+ return { ok: true, status: outcome.status, enabled };
124
+ }
125
+ // `conflict` is the only retryable one: it means a competing writer won the
126
+ // rebase, so the same call can succeed. A missing or malformed config will fail
127
+ // identically forever, and telling a caller to retry that is how a UI ends up
128
+ // spinning on a problem only the user can fix.
129
+ const retryable = outcome.reason === "conflict";
130
+ return {
131
+ ok: false,
132
+ reason: outcome.reason,
133
+ retryable,
134
+ message: outcome.reason === "conflict"
135
+ ? "Another process changed the config while this switch was being written."
136
+ : outcome.reason === "missing"
137
+ ? "No config file exists to record the switch in."
138
+ : "The config file is malformed; refusing to overwrite it.",
139
+ };
140
+ }
141
+
142
+ export function setCodexIntegrationEnabled(enabled: boolean): CodexDesiredStateResult {
143
+ return setIntegrationEnabled("codex", enabled);
144
+ }
145
+
146
+ export function setGrokIntegrationEnabled(enabled: boolean): CodexDesiredStateResult {
147
+ return setIntegrationEnabled("grok", enabled);
148
+ }
149
+
150
+ /** Whether Claude Desktop's managed gateway profile is wanted. */
151
+ export function claudeDesktopIntegrationEnabled(config: Pick<OcxConfig, "clientIntegrations">): boolean {
152
+ return integrationEnabled(config, "claude-desktop");
153
+ }
154
+
155
+ /** The same question when no admitted config snapshot is in hand. */
156
+ export function claudeDesktopIntegrationEnabledNow(): boolean {
157
+ return claudeDesktopIntegrationEnabled(loadConfig());
158
+ }
159
+
160
+ export function setClaudeDesktopIntegrationEnabled(enabled: boolean): CodexDesiredStateResult {
161
+ return setIntegrationEnabled("claude-desktop", enabled);
162
+ }
163
+
164
+ /**
165
+ * The startup gate, as a function rather than an `if` buried in `handleStart`.
166
+ *
167
+ * `ocx start` used to call `syncModelsToCodex(port).catch(() => {})`
168
+ * unconditionally, which is exactly why turning Codex off lasted until the next
169
+ * start: the restore worked, and then start put the routing straight back. It
170
+ * lived inline in a 600-line startup function that opens sockets and installs
171
+ * services, so nothing could test it — and an untestable gate is how the
172
+ * unconditional version survived this long.
173
+ *
174
+ * The `.catch` is deliberate and stays: a provider fetch failing at startup must
175
+ * not stop the proxy from coming up. Swallowing a failure to APPLY is tolerable.
176
+ * Swallowing the user's decision was not.
177
+ *
178
+ * Returns whether the sync ran, so a caller — or a test — can tell "skipped
179
+ * because the user turned it off" from "ran and quietly failed", plus what it
180
+ * wrote when it did run (#1046 — the caller warns about stale app-servers only
181
+ * after a real write).
182
+ */
183
+ export async function syncCodexOnStartIfEnabled(
184
+ port: number,
185
+ config: Pick<OcxConfig, "clientIntegrations">,
186
+ sync: CodexStartupSync = defaultStartupSync,
187
+ readinessGate?: ReadinessGate,
188
+ ): Promise<{ ran: boolean; catalogWritten: boolean; cacheSynced: boolean }> {
189
+ if (!shouldSyncCodexOnStart(config)) {
190
+ // The user explicitly turned Codex off: there is nothing to sync, so the
191
+ // proxy is ready as soon as it is up. The gate is driven here so /readyz
192
+ // does not stay pending forever for a deployment that deliberately disabled
193
+ // the native Codex integration.
194
+ readinessGate?.markReady();
195
+ return { ran: false, catalogWritten: false, cacheSynced: false };
196
+ }
197
+ // The `.catch` is deliberate and stays: a failure to APPLY must not stop the
198
+ // proxy from coming up. A failed sync simply reports no writes. The readiness
199
+ // gate observes the real outcome so /readyz reflects the sync state exactly as
200
+ // the PR contract defines (ready only on ok=true with no warning).
201
+ const outcome = readinessGate
202
+ ? await runStartupReadinessSync(readinessGate, async () => (await sync(port)) ?? null)
203
+ : await sync(port).catch(() => undefined);
204
+ return {
205
+ ran: true,
206
+ catalogWritten: outcome?.catalogWritten === true,
207
+ cacheSynced: outcome?.cacheSynced === true,
208
+ };
209
+ }
210
+
211
+ async function defaultStartupSync(port: number): Promise<CodexStartupSyncOutcome> {
212
+ const { syncModelsToCodex } = await import("./sync");
213
+ return syncModelsToCodex(port);
214
+ }
215
+
216
+ /**
217
+ * The Grok startup gate.
218
+ *
219
+ * Grok's toggle shipped and then `ocx start` called `syncGrokConfig`
220
+ * unconditionally, so switching Grok off lasted exactly one restart — the fence
221
+ * came out of `~/.grok/config.toml` and the next start wrote it straight back.
222
+ * Same defect as Codex had, in a different file.
223
+ *
224
+ * The caller keeps its own try/catch, because a Grok failure must never block
225
+ * startup and its diagnostic is worth printing. This only answers whether to
226
+ * attempt the sync at all.
227
+ */
228
+ export function shouldSyncGrokOnStart(config: Pick<OcxConfig, "clientIntegrations">): boolean {
229
+ return grokIntegrationEnabled(config);
230
+ }
@@ -0,0 +1,355 @@
1
+ /**
2
+ * Full restart of the Codex desktop app (the Electron shell), Windows only.
3
+ *
4
+ * `--restart-codex` deliberately signals only `codex app-server` /
5
+ * `codex-code-mode-host` processes: `isCodexAppServerCommandLine` requires a
6
+ * `codex` executable token, so the shell that owns the model picker is never a
7
+ * match. On macOS that is enough, because the respawned app-server re-emits
8
+ * `codex-app-server-initialized` and the renderer drops its cached
9
+ * `model/list`. On Windows MSIX it is not: externally terminating the child
10
+ * does not reliably re-emit that event in the surviving shell, so the picker
11
+ * keeps showing the old catalog until the app itself is restarted (#2292).
12
+ *
13
+ * This is therefore a SEPARATE opt-in flag rather than a widening of
14
+ * `--restart-codex`. Quitting the desktop app ends live conversations, which is
15
+ * a different consent from restarting a background helper, and the CLI contract
16
+ * for `--restart-codex` promises the narrow behavior.
17
+ *
18
+ * Everything here fails CLOSED: if the package cannot be identified, if a
19
+ * target is part of our own ancestry, or if any target survives termination,
20
+ * nothing is relaunched and the caller is told to restart manually. A stale
21
+ * picker is a much smaller problem than a wrongly killed process.
22
+ */
23
+ import { resolveTrustedWindowsPowerShellExe, resolveTrustedWindowsTaskkillExe } from "../lib/windows-elevation";
24
+ import { execFileSync } from "node:child_process";
25
+
26
+ /** Bounded subprocess options. A hung Appx/CIM probe must never wedge `ocx sync`. */
27
+ export interface DesktopAppExecOptions {
28
+ timeout?: number;
29
+ windowsHide?: boolean;
30
+ }
31
+
32
+ export interface DesktopAppRestartIo {
33
+ platform?: NodeJS.Platform;
34
+ /** Returns stdout. Options are part of the seam so the timeout is testable. */
35
+ execFile?: (file: string, args: readonly string[], options?: DesktopAppExecOptions) => string;
36
+ /** Process ancestry of the current process, innermost first. Used for the self-kill guard. */
37
+ ancestryPids?: () => number[];
38
+ isAlive?: (pid: number) => boolean;
39
+ sleep?: (ms: number) => void;
40
+ now?: () => number;
41
+ }
42
+
43
+ export type DesktopAppRestartReason =
44
+ | "windows_only"
45
+ | "package_discovery_failed"
46
+ | "process_probe_failed"
47
+ | "no_targets"
48
+ | "self_ancestry"
49
+ | "targets_survived";
50
+
51
+ export interface DesktopAppRestartResult {
52
+ attempted: boolean;
53
+ stopped: number[];
54
+ surviving: number[];
55
+ relaunch: "started" | "skipped";
56
+ reason?: DesktopAppRestartReason;
57
+ }
58
+
59
+ /** How long a graceful close is given before the forced pass. */
60
+ const GRACEFUL_EXIT_TIMEOUT_MS = 15_000;
61
+ /** How long a forced kill is given before the target counts as surviving. */
62
+ const FORCED_EXIT_TIMEOUT_MS = 5_000;
63
+ /** Every probe is bounded; PowerShell module loading is the slow part. */
64
+ const PROBE_TIMEOUT_MS = 10_000;
65
+
66
+ interface DesktopPackage {
67
+ family: string;
68
+ installLocation: string;
69
+ aumid: string;
70
+ }
71
+
72
+ /**
73
+ * Runtime discovery, never a hardcoded identifier. The beta MSIX package family
74
+ * changes between builds, so a literal AUMID would silently stop matching and
75
+ * then either do nothing or — worse — match a package we did not mean.
76
+ */
77
+ function discoverPackage(exec: NonNullable<DesktopAppRestartIo["execFile"]>): DesktopPackage | null {
78
+ const script = [
79
+ "$ErrorActionPreference='SilentlyContinue'",
80
+ "Import-Module Appx -ErrorAction SilentlyContinue",
81
+ "$p = Get-AppxPackage -Name OpenAI.Codex",
82
+ "if (-not $p) { $p = Get-AppxPackage -Name OpenAI.CodexBeta }",
83
+ "if (-not $p -or -not $p.InstallLocation) { 'MISS' } else {",
84
+ " $p.PackageFamilyName; $p.InstallLocation; \"$($p.PackageFamilyName)!App\"",
85
+ "}",
86
+ ].join("; ");
87
+ let stdout: string;
88
+ try {
89
+ stdout = exec(resolveTrustedWindowsPowerShellExe(), ["-NoProfile", "-NonInteractive", "-Command", script], {
90
+ timeout: PROBE_TIMEOUT_MS,
91
+ windowsHide: true,
92
+ });
93
+ } catch {
94
+ return null;
95
+ }
96
+ const lines = stdout.split(/\r?\n/).map(line => line.trim()).filter(line => line.length > 0);
97
+ if (lines.length < 3 || lines[0] === "MISS") return null;
98
+ const [family, installLocation, aumid] = lines;
99
+ if (!family || !installLocation || !aumid) return null;
100
+ return { family, installLocation, aumid };
101
+ }
102
+
103
+ interface DesktopProcess {
104
+ pid: number;
105
+ parentPid: number;
106
+ /** Win32_Process CreationDate. Guards against PID reuse across the wait window. */
107
+ createdAt: string;
108
+ }
109
+
110
+ /**
111
+ * Only `ChatGPT.exe` processes whose image lives under the discovered install
112
+ * location AND owned by the current user. The install location alone is not
113
+ * enough: an MSIX package under `WindowsApps` is shared, so on a multi-user
114
+ * machine another account's Codex desktop matches the same path. The app-server
115
+ * collector already pays for `GetOwner` for exactly this reason.
116
+ *
117
+ * `CreationDate` is captured so a PID can be re-verified before it is signalled;
118
+ * a graceful-close window is long enough for Windows to recycle a PID.
119
+ */
120
+ function listPackageProcesses(
121
+ exec: NonNullable<DesktopAppRestartIo["execFile"]>,
122
+ installLocation: string,
123
+ ): DesktopProcess[] | null {
124
+ const literal = installLocation.replace(/'/g, "''");
125
+ const script = [
126
+ "$ErrorActionPreference='SilentlyContinue'",
127
+ `$root = '${literal}'`,
128
+ "$me = ([Security.Principal.WindowsIdentity]::GetCurrent()).Name",
129
+ "Get-CimInstance Win32_Process -Filter \"Name='ChatGPT.exe'\" |",
130
+ " Where-Object { $_.ExecutablePath -and $_.ExecutablePath.StartsWith($root, 'OrdinalIgnoreCase') } |",
131
+ " ForEach-Object {",
132
+ " $o = Invoke-CimMethod -InputObject $_ -MethodName GetOwner",
133
+ " if ($o -and $o.ReturnValue -eq 0 -and $o.User) {",
134
+ " $owner = if ($o.Domain) { \"$($o.Domain)\\$($o.User)\" } else { $o.User }",
135
+ " if ($owner -ieq $me) {",
136
+ " \"$($_.ProcessId) $($_.ParentProcessId) $($_.CreationDate.ToString('o'))\"",
137
+ " }",
138
+ " }",
139
+ " }",
140
+ // Statements must be newline-separated. Joining with a space concatenates
141
+ // `$ErrorActionPreference='SilentlyContinue' $root = '...'` into one malformed statement,
142
+ // which PowerShell rejects — so the probe threw and every caller read "not running" (#2557).
143
+ ].join("\n");
144
+ let stdout: string;
145
+ try {
146
+ stdout = exec(resolveTrustedWindowsPowerShellExe(), ["-NoProfile", "-NonInteractive", "-Command", script], {
147
+ timeout: PROBE_TIMEOUT_MS,
148
+ windowsHide: true,
149
+ });
150
+ } catch {
151
+ // A probe that could not run is NOT proof the app is absent. Returning [] here made a
152
+ // failed enumeration indistinguishable from "no targets", so the CLI reported the app as
153
+ // not running and skipped a restart the user had explicitly asked for.
154
+ return null;
155
+ }
156
+ const processes: DesktopProcess[] = [];
157
+ for (const line of stdout.split(/\r?\n/)) {
158
+ const match = /^\s*(\d+)\s+(\d+)\s+(\S+)\s*$/.exec(line);
159
+ if (!match) continue;
160
+ const pid = Number(match[1]);
161
+ const parentPid = Number(match[2]);
162
+ const createdAt = match[3]!;
163
+ if (Number.isSafeInteger(pid) && Number.isSafeInteger(parentPid)) {
164
+ processes.push({ pid, parentPid, createdAt });
165
+ }
166
+ }
167
+ return processes;
168
+ }
169
+
170
+ /**
171
+ * True when the PID still names the same process we verified. Between listing
172
+ * and signalling there is a graceful-close window, and a `taskkill /T /F` on a
173
+ * recycled PID would tear down an unrelated process tree.
174
+ */
175
+ function stillSameProcess(
176
+ exec: NonNullable<DesktopAppRestartIo["execFile"]>,
177
+ installLocation: string,
178
+ target: DesktopProcess,
179
+ ): boolean {
180
+ const processes = listPackageProcesses(exec, installLocation);
181
+ // Fail CLOSED on a failed re-probe: this guards a kill, and "we could not look" must not be
182
+ // read as "the pid was recycled and is now someone else's process".
183
+ if (processes === null) return false;
184
+ const current = processes.find(p => p.pid === target.pid);
185
+ return current !== undefined && current.createdAt === target.createdAt;
186
+ }
187
+
188
+ /** Roots are the package processes whose parent is not itself in the package tree. */
189
+ function rootProcesses(processes: readonly DesktopProcess[]): DesktopProcess[] {
190
+ const inTree = new Set(processes.map(p => p.pid));
191
+ return processes.filter(p => !inTree.has(p.parentPid));
192
+ }
193
+
194
+ /**
195
+ * Full Windows parent chain for this process, innermost first.
196
+ *
197
+ * `process.ppid` is one level, which is not enough: a terminal hosted inside the
198
+ * desktop app sits several hops below `ChatGPT.exe`, so a one-level check would
199
+ * miss the exact case the guard exists for and we would terminate our own host.
200
+ * The chain therefore comes from CIM, with a bound so a corrupted parent cycle
201
+ * cannot spin.
202
+ */
203
+ function windowsAncestryPids(exec: NonNullable<DesktopAppRestartIo["execFile"]>): number[] {
204
+ const chain: number[] = [process.pid];
205
+ let current = process.pid;
206
+ for (let hop = 0; hop < 16; hop++) {
207
+ let stdout: string;
208
+ try {
209
+ stdout = exec(resolveTrustedWindowsPowerShellExe(), [
210
+ "-NoProfile", "-NonInteractive", "-Command",
211
+ `$ErrorActionPreference='SilentlyContinue'; (Get-CimInstance Win32_Process -Filter "ProcessId=${current}").ParentProcessId`,
212
+ ], { timeout: PROBE_TIMEOUT_MS, windowsHide: true });
213
+ } catch {
214
+ // An unreadable chain must not be read as "not our ancestor".
215
+ return [];
216
+ }
217
+ const parent = Number(stdout.trim());
218
+ if (!Number.isSafeInteger(parent) || parent <= 0 || chain.includes(parent)) break;
219
+ chain.push(parent);
220
+ current = parent;
221
+ }
222
+ return chain;
223
+ }
224
+
225
+ function defaultExecFile(file: string, args: readonly string[], options?: DesktopAppExecOptions): string {
226
+ return execFileSync(file, [...args], {
227
+ encoding: "utf-8",
228
+ timeout: options?.timeout ?? PROBE_TIMEOUT_MS,
229
+ windowsHide: options?.windowsHide ?? true,
230
+ });
231
+ }
232
+
233
+ function defaultIsAlive(pid: number): boolean {
234
+ try {
235
+ process.kill(pid, 0);
236
+ return true;
237
+ } catch {
238
+ return false;
239
+ }
240
+ }
241
+
242
+ function defaultSleep(ms: number): void {
243
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
244
+ }
245
+
246
+ function waitForExit(
247
+ pid: number,
248
+ timeoutMs: number,
249
+ isAlive: (pid: number) => boolean,
250
+ sleep: (ms: number) => void,
251
+ now: () => number,
252
+ ): boolean {
253
+ const deadline = now() + timeoutMs;
254
+ while (now() < deadline) {
255
+ if (!isAlive(pid)) return true;
256
+ sleep(250);
257
+ }
258
+ return !isAlive(pid);
259
+ }
260
+
261
+ /**
262
+ * Stop every package-tree root gracefully, force the stragglers, then relaunch
263
+ * through the discovered AUMID. Returns without relaunching if anything
264
+ * survived, because launching a second shell beside a stuck one is worse than
265
+ * leaving the user to restart it.
266
+ */
267
+ export function restartCodexDesktopApp(io: DesktopAppRestartIo = {}): DesktopAppRestartResult {
268
+ const platform = io.platform ?? process.platform;
269
+ const skipped = (reason: DesktopAppRestartReason): DesktopAppRestartResult => ({
270
+ attempted: false, stopped: [], surviving: [], relaunch: "skipped", reason,
271
+ });
272
+ if (platform !== "win32") return skipped("windows_only");
273
+
274
+ const exec = io.execFile ?? defaultExecFile;
275
+ const pkg = discoverPackage(exec);
276
+ if (!pkg) return skipped("package_discovery_failed");
277
+
278
+ const processes = listPackageProcesses(exec, pkg.installLocation);
279
+ // A probe that could not run is not evidence of absence. Reporting it as `no_targets` told
280
+ // the user the app was not running and silently skipped the restart they asked for (#2557).
281
+ if (processes === null) return skipped("process_probe_failed");
282
+ const roots = rootProcesses(processes);
283
+ if (roots.length === 0) return skipped("no_targets");
284
+
285
+ const ancestryPids = io.ancestryPids ? io.ancestryPids() : windowsAncestryPids(exec);
286
+ if (ancestryPids.length === 0) {
287
+ // Fail closed: an unreadable ancestry chain cannot prove we are outside the
288
+ // tree we are about to terminate.
289
+ return skipped("self_ancestry");
290
+ }
291
+ const ancestry = new Set(ancestryPids);
292
+ if (processes.some(p => ancestry.has(p.pid))) {
293
+ // Terminating our own tree would kill this command mid-flight and leave the
294
+ // user with neither a restarted app nor an explanation.
295
+ return skipped("self_ancestry");
296
+ }
297
+
298
+ const isAlive = io.isAlive ?? defaultIsAlive;
299
+ const sleep = io.sleep ?? defaultSleep;
300
+ const now = io.now ?? (() => Date.now());
301
+ const stopped: number[] = [];
302
+ const surviving: number[] = [];
303
+
304
+ for (const root of roots) {
305
+ const pid = root.pid;
306
+ // Re-verify immediately before the graceful close: the listing is already
307
+ // one probe old.
308
+ if (!stillSameProcess(exec, pkg.installLocation, root)) {
309
+ stopped.push(pid);
310
+ continue;
311
+ }
312
+ try {
313
+ exec(resolveTrustedWindowsPowerShellExe(), [
314
+ "-NoProfile", "-NonInteractive", "-Command",
315
+ `$p = Get-Process -Id ${pid} -ErrorAction SilentlyContinue; if ($p) { [void]$p.CloseMainWindow() }`,
316
+ ], { timeout: PROBE_TIMEOUT_MS, windowsHide: true });
317
+ } catch {
318
+ /* a refused graceful close still gets the forced pass below */
319
+ }
320
+ if (waitForExit(pid, GRACEFUL_EXIT_TIMEOUT_MS, isAlive, sleep, now)) {
321
+ stopped.push(pid);
322
+ continue;
323
+ }
324
+ // The wait window is long enough for Windows to recycle a PID, and the next
325
+ // step is `/T /F` against a whole tree. Confirm the PID is still the process
326
+ // we verified, or leave it alone.
327
+ if (!stillSameProcess(exec, pkg.installLocation, root)) {
328
+ stopped.push(pid);
329
+ continue;
330
+ }
331
+ try {
332
+ exec(resolveTrustedWindowsTaskkillExe(), ["/PID", String(pid), "/T", "/F"], {
333
+ timeout: PROBE_TIMEOUT_MS, windowsHide: true,
334
+ });
335
+ } catch {
336
+ /* fall through to the liveness check: the process state decides, not the exit code */
337
+ }
338
+ if (waitForExit(pid, FORCED_EXIT_TIMEOUT_MS, isAlive, sleep, now)) stopped.push(pid);
339
+ else surviving.push(pid);
340
+ }
341
+
342
+ if (surviving.length > 0) {
343
+ return { attempted: true, stopped, surviving, relaunch: "skipped", reason: "targets_survived" };
344
+ }
345
+
346
+ try {
347
+ exec(resolveTrustedWindowsPowerShellExe(), [
348
+ "-NoProfile", "-NonInteractive", "-Command",
349
+ `Start-Process 'shell:AppsFolder\\${pkg.aumid}'`,
350
+ ], { timeout: PROBE_TIMEOUT_MS, windowsHide: true });
351
+ } catch {
352
+ return { attempted: true, stopped, surviving, relaunch: "skipped", reason: "targets_survived" };
353
+ }
354
+ return { attempted: true, stopped, surviving, relaunch: "started" };
355
+ }