@iislee/opencodex 2.11.0 → 2.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (675) hide show
  1. package/AGENTS_INSTALL.md +109 -0
  2. package/README.md +114 -19
  3. package/bin/ocx.mjs +164 -36
  4. package/bin/package-main.mjs +1 -1
  5. package/gui/dist/assets/index-BF38heuV.js +104 -0
  6. package/gui/dist/assets/index-DMiI18Kv.css +1 -0
  7. package/gui/dist/index.html +2 -2
  8. package/gui/dist/provider-icons/alibaba-color.svg +1 -1
  9. package/gui/dist/provider-icons/antigravity-color.svg +1 -1
  10. package/gui/dist/provider-icons/claude-color.svg +1 -1
  11. package/gui/dist/provider-icons/cline-color.svg +16 -0
  12. package/gui/dist/provider-icons/cloudflare-ai-gateway-color.svg +1 -1
  13. package/gui/dist/provider-icons/commandcode-color.svg +1 -0
  14. package/gui/dist/provider-icons/copilot-color.svg +1 -1
  15. package/gui/dist/provider-icons/cursor-color.svg +1 -1
  16. package/gui/dist/provider-icons/deepseek-color.svg +1 -1
  17. package/gui/dist/provider-icons/firepass-color.svg +1 -1
  18. package/gui/dist/provider-icons/fireworks-color.svg +1 -1
  19. package/gui/dist/provider-icons/gemini-color.svg +1 -1
  20. package/gui/dist/provider-icons/github-copilot-color.svg +1 -1
  21. package/gui/dist/provider-icons/gitlab-duo-color.svg +1 -1
  22. package/gui/dist/provider-icons/grok.svg +1 -1
  23. package/gui/dist/provider-icons/groq-color.svg +1 -1
  24. package/gui/dist/provider-icons/huggingface-color.svg +1 -1
  25. package/gui/dist/provider-icons/kimi-color.svg +1 -1
  26. package/gui/dist/provider-icons/kiro-color.svg +2 -2
  27. package/gui/dist/provider-icons/lm-studio-color.svg +1 -1
  28. package/gui/dist/provider-icons/mistral-color.svg +1 -1
  29. package/gui/dist/provider-icons/moonshot-color.svg +1 -1
  30. package/gui/dist/provider-icons/nvidia-color.svg +1 -1
  31. package/gui/dist/provider-icons/ollama-color.svg +1 -1
  32. package/gui/dist/provider-icons/openai.svg +1 -1
  33. package/gui/dist/provider-icons/opencode.svg +2 -1
  34. package/gui/dist/provider-icons/openrouter-color.svg +1 -1
  35. package/gui/dist/provider-icons/pi.svg +2 -2
  36. package/gui/dist/provider-icons/qianfan-color.svg +1 -1
  37. package/gui/dist/provider-icons/qwen-portal-color.svg +1 -1
  38. package/gui/dist/provider-icons/vercel-ai-gateway-color.svg +1 -1
  39. package/gui/dist/provider-icons/vllm-color.svg +1 -1
  40. package/gui/dist/provider-icons/xiaomi-color.svg +1 -1
  41. package/package.json +19 -10
  42. package/src/adapters/anthropic-output-schema.ts +137 -0
  43. package/src/adapters/anthropic.ts +376 -52
  44. package/src/adapters/base.ts +54 -7
  45. package/src/adapters/client-fingerprint.ts +18 -12
  46. package/src/adapters/cline-pass-deepseek-v4-tool-replay.ts +69 -0
  47. package/src/adapters/command-code.ts +601 -0
  48. package/src/adapters/cursor/checkpoint-store.ts +303 -0
  49. package/src/adapters/cursor/cursor-errors.ts +129 -6
  50. package/src/adapters/cursor/discovery.ts +61 -4
  51. package/src/adapters/cursor/effort-map.ts +27 -3
  52. package/src/adapters/cursor/framing.ts +39 -0
  53. package/src/adapters/cursor/h2-pool.ts +123 -0
  54. package/src/adapters/cursor/http1-bidi.ts +361 -0
  55. package/src/adapters/cursor/images.ts +704 -0
  56. package/src/adapters/cursor/live-models.ts +174 -58
  57. package/src/adapters/cursor/live-transport.ts +609 -170
  58. package/src/adapters/cursor/native-exec-common.ts +23 -2
  59. package/src/adapters/cursor/native-exec-fs.ts +10 -7
  60. package/src/adapters/cursor/native-exec-network.ts +1 -1
  61. package/src/adapters/cursor/native-exec-shell.ts +0 -1
  62. package/src/adapters/cursor/native-exec.ts +101 -14
  63. package/src/adapters/cursor/protobuf-events.ts +829 -11
  64. package/src/adapters/cursor/protobuf-request.ts +383 -65
  65. package/src/adapters/cursor/request-builder.ts +237 -31
  66. package/src/adapters/cursor/tool-definitions.ts +242 -12
  67. package/src/adapters/cursor/tool-result-normalize.ts +92 -0
  68. package/src/adapters/cursor/transport.ts +22 -0
  69. package/src/adapters/cursor/types.ts +28 -1
  70. package/src/adapters/cursor.ts +133 -7
  71. package/src/adapters/google-antigravity-replay.ts +558 -34
  72. package/src/adapters/google-antigravity-wire.ts +43 -10
  73. package/src/adapters/google-http.ts +38 -10
  74. package/src/adapters/google-tool-schema.ts +80 -15
  75. package/src/adapters/google-truncation.ts +11 -0
  76. package/src/adapters/google.ts +618 -74
  77. package/src/adapters/identity.ts +39 -6
  78. package/src/adapters/kiro-errors.ts +11 -0
  79. package/src/adapters/kiro-events.ts +19 -1
  80. package/src/adapters/kiro-thinking.ts +18 -2
  81. package/src/adapters/kiro-tools.ts +10 -1
  82. package/src/adapters/kiro.ts +92 -54
  83. package/src/adapters/mimo-free.ts +17 -0
  84. package/src/adapters/openai-chat-url.ts +11 -0
  85. package/src/adapters/openai-chat.ts +1311 -336
  86. package/src/adapters/openai-responses-url.ts +16 -0
  87. package/src/adapters/openai-responses.ts +830 -56
  88. package/src/adapters/registry.ts +175 -0
  89. package/src/adapters/responses-tool-schema.ts +67 -0
  90. package/src/adapters/tool-call-id.ts +119 -0
  91. package/src/adapters/tool-catalog-nudge.ts +104 -21
  92. package/src/adapters/xai-web-search.ts +185 -0
  93. package/src/bridge.ts +428 -61
  94. package/src/chat/inbound.ts +43 -19
  95. package/src/chat/outbound.ts +82 -26
  96. package/src/claude/agents-inject.ts +32 -9
  97. package/src/claude/context-windows.ts +21 -5
  98. package/src/claude/desktop-3p.ts +243 -9
  99. package/src/claude/gateway-cache.ts +41 -4
  100. package/src/claude/inbound.ts +72 -3
  101. package/src/claude/model-info.ts +38 -15
  102. package/src/claude/outbound.ts +70 -16
  103. package/src/cli/account-api.ts +35 -1
  104. package/src/cli/account-auth.ts +33 -6
  105. package/src/cli/account-catalog-refresh.ts +14 -0
  106. package/src/cli/account-extended.ts +389 -2
  107. package/src/cli/account-main.ts +317 -0
  108. package/src/cli/account.ts +30 -6
  109. package/src/cli/agent.ts +45 -1
  110. package/src/cli/claude-agent-startup-sync.ts +73 -0
  111. package/src/cli/claude-desktop.ts +29 -4
  112. package/src/cli/claude.ts +103 -34
  113. package/src/cli/codex-log-guard-doctor.ts +103 -0
  114. package/src/cli/codex-shim-autorestore.ts +2 -0
  115. package/src/cli/codex-shim-readiness.ts +76 -0
  116. package/src/cli/combo.ts +8 -0
  117. package/src/cli/config-command.ts +74 -10
  118. package/src/cli/dispatch.ts +593 -0
  119. package/src/cli/doctor.ts +315 -43
  120. package/src/cli/ensure-desired-integrations.ts +152 -0
  121. package/src/cli/export-command.ts +46 -20
  122. package/src/cli/help.ts +20 -271
  123. package/src/cli/index.ts +378 -519
  124. package/src/cli/init.ts +4 -17
  125. package/src/cli/integrations.ts +120 -2
  126. package/src/cli/lab.ts +607 -0
  127. package/src/cli/launcher-context.ts +77 -0
  128. package/src/cli/minimax.ts +497 -0
  129. package/src/cli/models-runtime.ts +35 -2
  130. package/src/cli/models.ts +100 -14
  131. package/src/cli/observe.ts +92 -3
  132. package/src/cli/opencode.ts +4 -2
  133. package/src/cli/provider-runtime.ts +18 -1
  134. package/src/cli/provider.ts +24 -3
  135. package/src/cli/ready.ts +301 -0
  136. package/src/cli/registry.ts +437 -0
  137. package/src/cli/root.ts +86 -0
  138. package/src/cli/route-policy.ts +92 -0
  139. package/src/cli/runtime-api.ts +6 -3
  140. package/src/cli/star-prompt.ts +71 -15
  141. package/src/cli/status.ts +10 -3
  142. package/src/cli/system-restart-client.ts +146 -0
  143. package/src/cli/tray-proxy.ts +153 -6
  144. package/src/cli/v2.ts +105 -10
  145. package/src/cli.ts +1 -1
  146. package/src/clients/config-export.ts +1358 -21
  147. package/src/codex/account-label.ts +14 -1
  148. package/src/codex/account-lifecycle.ts +130 -13
  149. package/src/codex/account-namespaces.ts +49 -3
  150. package/src/codex/account-priority.ts +83 -0
  151. package/src/codex/account-store.ts +29 -2
  152. package/src/codex/account-usability.ts +25 -2
  153. package/src/codex/admission.ts +256 -0
  154. package/src/codex/affinity-debug.ts +162 -0
  155. package/src/codex/app-server-processes.ts +493 -106
  156. package/src/codex/app-server-restart-service.ts +232 -0
  157. package/src/codex/auth-api.ts +849 -242
  158. package/src/codex/auth-collision.ts +5 -3
  159. package/src/codex/auth-context.ts +345 -32
  160. package/src/codex/autostart-health.ts +8 -1
  161. package/src/codex/catalog/account-models.ts +67 -0
  162. package/src/codex/catalog/aggregation.ts +68 -10
  163. package/src/codex/catalog/bundled.ts +331 -33
  164. package/src/codex/catalog/effort.ts +121 -30
  165. package/src/codex/catalog/filesystem-evidence.ts +302 -0
  166. package/src/codex/catalog/kinds.ts +2 -0
  167. package/src/codex/catalog/metadata.ts +529 -45
  168. package/src/codex/catalog/native-models.ts +72 -0
  169. package/src/codex/catalog/parsing.ts +224 -30
  170. package/src/codex/catalog/provider-fetch.ts +1460 -134
  171. package/src/codex/catalog/sync.ts +1449 -186
  172. package/src/codex/catalog-admission.ts +199 -0
  173. package/src/codex/catalog-refresh-status.ts +105 -0
  174. package/src/codex/catalog-write-serialization.ts +242 -0
  175. package/src/codex/catalog.ts +6 -3
  176. package/src/codex/codex-write-lock.ts +384 -0
  177. package/src/codex/convergence-types.ts +614 -0
  178. package/src/codex/convergence.ts +651 -0
  179. package/src/codex/coordinator-doctor.ts +332 -0
  180. package/src/codex/custom-model-catalog-migration.ts +176 -0
  181. package/src/codex/desired-state.ts +230 -0
  182. package/src/codex/features.ts +636 -39
  183. package/src/codex/generation.ts +202 -0
  184. package/src/codex/history-job.ts +407 -0
  185. package/src/codex/history-lock.ts +242 -0
  186. package/src/codex/history-migration-guardian.ts +26 -20
  187. package/src/codex/history-provider.ts +231 -28
  188. package/src/codex/history-transition.ts +105 -0
  189. package/src/codex/history-worker.ts +220 -0
  190. package/src/codex/inject-coordination.ts +290 -0
  191. package/src/codex/inject.ts +1073 -152
  192. package/src/codex/injected-marker.ts +37 -3
  193. package/src/codex/integration-record.ts +266 -0
  194. package/src/codex/internal/catalog-writer.ts +203 -0
  195. package/src/codex/internal/history-writer.ts +80 -0
  196. package/src/codex/journal.ts +66 -4
  197. package/src/codex/log-guard/inspect.ts +506 -0
  198. package/src/codex/log-guard/lock.ts +150 -0
  199. package/src/codex/log-guard/maintenance.ts +403 -0
  200. package/src/codex/log-guard/path-safety.ts +88 -0
  201. package/src/codex/log-guard/policy.ts +44 -0
  202. package/src/codex/log-guard/processes.ts +205 -0
  203. package/src/codex/log-guard/protection.ts +489 -0
  204. package/src/codex/log-guard/sqlite-errors.ts +9 -0
  205. package/src/codex/main-account-cache.ts +24 -0
  206. package/src/codex/main-account.ts +29 -1
  207. package/src/codex/management-convergence.ts +167 -0
  208. package/src/codex/model-cache.ts +56 -10
  209. package/src/codex/model-entitlements.ts +353 -0
  210. package/src/codex/native-main-admission.ts +47 -0
  211. package/src/codex/native-main-auth-temp.ts +187 -0
  212. package/src/codex/native-main-claim.ts +178 -0
  213. package/src/codex/native-main-lock-file.ts +162 -0
  214. package/src/codex/native-main-owner.ts +329 -0
  215. package/src/codex/native-profile-api.ts +247 -0
  216. package/src/codex/native-profile-manager.ts +1531 -0
  217. package/src/codex/native-profile-processes.ts +121 -0
  218. package/src/codex/native-profile-recovery.ts +99 -0
  219. package/src/codex/native-profile-stage-store.ts +387 -0
  220. package/src/codex/native-profile-startup.ts +492 -0
  221. package/src/codex/native-profile-store.ts +855 -0
  222. package/src/codex/native-profile-types.ts +120 -0
  223. package/src/codex/native-residue.ts +682 -0
  224. package/src/codex/paths.ts +80 -1
  225. package/src/codex/plan-from-token.ts +140 -0
  226. package/src/codex/plan.ts +40 -0
  227. package/src/codex/plugins-doctor.ts +1 -1
  228. package/src/codex/pool-rotation.ts +74 -4
  229. package/src/codex/project-config-warnings.ts +20 -6
  230. package/src/codex/prompt-journal.ts +352 -0
  231. package/src/codex/prompt-layers.ts +967 -0
  232. package/src/codex/prompt-lock.ts +143 -0
  233. package/src/codex/quota-rejection.ts +298 -0
  234. package/src/codex/quota.ts +175 -13
  235. package/src/codex/refresh.ts +11 -2
  236. package/src/codex/reset-credit-recovery.ts +1044 -0
  237. package/src/codex/routing.ts +505 -94
  238. package/src/codex/runtime.ts +159 -38
  239. package/src/codex/shim.ts +1009 -28
  240. package/src/codex/subagent-model-fallback.ts +350 -35
  241. package/src/codex/sync.ts +191 -2
  242. package/src/codex/transition-state.ts +612 -0
  243. package/src/codex/upstream-host-health.ts +368 -0
  244. package/src/codex/user-identity.ts +557 -0
  245. package/src/codex/warmup.ts +187 -81
  246. package/src/codex/write-coordination.ts +114 -0
  247. package/src/combos/failover.ts +20 -0
  248. package/src/combos/index.ts +4 -0
  249. package/src/combos/request.ts +32 -0
  250. package/src/combos/types.ts +81 -9
  251. package/src/config/provider-name.ts +24 -0
  252. package/src/config.ts +1762 -140
  253. package/src/generated/compatibility-version.json +3164 -0
  254. package/src/generated/{jawcode-model-metadata.ts → model-metadata.ts} +19 -17
  255. package/src/grok/inject.ts +16 -5
  256. package/src/grok/inspect.ts +45 -0
  257. package/src/grok/sync.ts +2 -2
  258. package/src/images/loop.ts +152 -29
  259. package/src/images/plan.ts +23 -13
  260. package/src/integrations/config-io.ts +269 -0
  261. package/src/integrations/journal.ts +315 -0
  262. package/src/integrations/merge.ts +135 -0
  263. package/src/integrations/mutation-flight.ts +71 -0
  264. package/src/integrations/native/ownership-preflight.ts +202 -0
  265. package/src/integrations/omp-yaml-source.ts +358 -0
  266. package/src/integrations/owned-refresh.ts +74 -0
  267. package/src/integrations/ownership.ts +111 -0
  268. package/src/integrations/registry.ts +159 -0
  269. package/src/integrations/serialize.ts +314 -0
  270. package/src/integrations/state.ts +361 -0
  271. package/src/integrations/store.ts +103 -0
  272. package/src/integrations/writer-lock.ts +98 -0
  273. package/src/integrations/writer.ts +691 -0
  274. package/src/lab/artifacts/sanitize.ts +586 -0
  275. package/src/lab/artifacts/secure-fs.ts +475 -0
  276. package/src/lab/artifacts/store.ts +310 -0
  277. package/src/lab/automation/budgets.ts +78 -0
  278. package/src/lab/automation/config-persistence.ts +256 -0
  279. package/src/lab/automation/constants.ts +39 -0
  280. package/src/lab/automation/cooldown.ts +103 -0
  281. package/src/lab/automation/dispatch.ts +211 -0
  282. package/src/lab/automation/index.ts +13 -0
  283. package/src/lab/automation/orchestrator.ts +499 -0
  284. package/src/lab/automation/persistence.ts +512 -0
  285. package/src/lab/automation/planner.ts +371 -0
  286. package/src/lab/automation/policy.ts +136 -0
  287. package/src/lab/automation/queue.ts +191 -0
  288. package/src/lab/automation/recovery.ts +24 -0
  289. package/src/lab/automation/route-context.ts +21 -0
  290. package/src/lab/automation/run-key.ts +44 -0
  291. package/src/lab/automation/runs-query.ts +34 -0
  292. package/src/lab/automation/types.ts +160 -0
  293. package/src/lab/conformance/assertion.ts +325 -0
  294. package/src/lab/conformance/digest.ts +22 -0
  295. package/src/lab/conformance/executor.ts +741 -0
  296. package/src/lab/conformance/fixture-provider.ts +27 -0
  297. package/src/lab/conformance/fixtures/live-v1-cases.json +175 -0
  298. package/src/lab/conformance/fixtures/protocol-v1-cases.json +461 -0
  299. package/src/lab/conformance/harness-budget.ts +47 -0
  300. package/src/lab/conformance/index.ts +5 -0
  301. package/src/lab/conformance/jcs.ts +64 -0
  302. package/src/lab/conformance/json-pointer.ts +39 -0
  303. package/src/lab/conformance/manifest.ts +180 -0
  304. package/src/lab/conformance/mcp-stub.ts +179 -0
  305. package/src/lab/conformance/negative-controls.ts +164 -0
  306. package/src/lab/conformance/observation.ts +355 -0
  307. package/src/lab/conformance/runner.ts +68 -0
  308. package/src/lab/conformance/sse-normalize.ts +59 -0
  309. package/src/lab/conformance/suite-manifest.ts +78 -0
  310. package/src/lab/conformance/types.ts +214 -0
  311. package/src/lab/constants.ts +126 -0
  312. package/src/lab/digest.ts +64 -0
  313. package/src/lab/events/errors.ts +9 -0
  314. package/src/lab/events/limits.ts +117 -0
  315. package/src/lab/events/types.ts +229 -0
  316. package/src/lab/events/validate.ts +781 -0
  317. package/src/lab/fabric/constants.ts +40 -0
  318. package/src/lab/fabric/executor.ts +492 -0
  319. package/src/lab/fabric/index.ts +80 -0
  320. package/src/lab/fabric/manifest.ts +222 -0
  321. package/src/lab/fabric/observe.ts +489 -0
  322. package/src/lab/fabric/patch.ts +79 -0
  323. package/src/lab/fabric/producer-child.ts +139 -0
  324. package/src/lab/fabric/producer-isolate.ts +276 -0
  325. package/src/lab/fabric/producer-protocol.ts +61 -0
  326. package/src/lab/fabric/scratch.ts +439 -0
  327. package/src/lab/fabric/subject.ts +106 -0
  328. package/src/lab/fabric/types.ts +134 -0
  329. package/src/lab/fabric/verifier.ts +98 -0
  330. package/src/lab/index.ts +54 -0
  331. package/src/lab/ledger/artifact-refs.ts +127 -0
  332. package/src/lab/ledger/invalidation.ts +136 -0
  333. package/src/lab/ledger/purge.ts +310 -0
  334. package/src/lab/ledger/store.ts +532 -0
  335. package/src/lab/live/credential-lease.ts +53 -0
  336. package/src/lab/live/destination.ts +155 -0
  337. package/src/lab/live/executor.ts +336 -0
  338. package/src/lab/live/inert-tools.ts +56 -0
  339. package/src/lab/live/manifest.ts +85 -0
  340. package/src/lab/live/mcp-loopback.ts +57 -0
  341. package/src/lab/live/runner.ts +19 -0
  342. package/src/lab/live/sandbox.ts +61 -0
  343. package/src/lab/live/suite-manifest.ts +41 -0
  344. package/src/lab/live/transport.ts +118 -0
  345. package/src/lab/live/types.ts +197 -0
  346. package/src/lab/observe/from-conformance.ts +301 -0
  347. package/src/lab/observe/from-live.ts +117 -0
  348. package/src/lab/paths.ts +153 -0
  349. package/src/lab/projection/rebuild.ts +495 -0
  350. package/src/lab/projection/schema.ts +135 -0
  351. package/src/lab/projection/verdicts.ts +474 -0
  352. package/src/lab/projection/verification.ts +412 -0
  353. package/src/lab/public/bundle.ts +217 -0
  354. package/src/lab/public/community-authority.ts +175 -0
  355. package/src/lab/public/community-files.ts +29 -0
  356. package/src/lab/public/community.ts +479 -0
  357. package/src/lab/public/file-safety.ts +155 -0
  358. package/src/lab/public/ids.ts +26 -0
  359. package/src/lab/public/index.ts +16 -0
  360. package/src/lab/public/mutation-lock.ts +424 -0
  361. package/src/lab/public/operator.ts +353 -0
  362. package/src/lab/public/origin-purge.ts +79 -0
  363. package/src/lab/public/origin.ts +203 -0
  364. package/src/lab/public/privacy.ts +143 -0
  365. package/src/lab/public/private-file.ts +261 -0
  366. package/src/lab/public/project.ts +124 -0
  367. package/src/lab/public/purge-test-fault.ts +21 -0
  368. package/src/lab/public/purge.ts +223 -0
  369. package/src/lab/public/registry.ts +44 -0
  370. package/src/lab/public/revocation.ts +252 -0
  371. package/src/lab/public/signature.ts +243 -0
  372. package/src/lab/public/storage.ts +105 -0
  373. package/src/lab/public/strict-json.ts +206 -0
  374. package/src/lab/public/time.ts +26 -0
  375. package/src/lab/public/types.ts +172 -0
  376. package/src/lab/public/validate.ts +391 -0
  377. package/src/lab/query/catalog.ts +101 -0
  378. package/src/lab/query/connection.ts +107 -0
  379. package/src/lab/query/constants.ts +4 -0
  380. package/src/lab/query/cursor.ts +132 -0
  381. package/src/lab/query/dto-map.ts +277 -0
  382. package/src/lab/query/errors.ts +22 -0
  383. package/src/lab/query/freshness.ts +53 -0
  384. package/src/lab/query/index.ts +45 -0
  385. package/src/lab/query/latest-observation.ts +59 -0
  386. package/src/lab/query/passive-production.ts +159 -0
  387. package/src/lab/query/queries.ts +444 -0
  388. package/src/lab/query/types.ts +266 -0
  389. package/src/lab/subject/behavior-fingerprint.ts +77 -0
  390. package/src/lab/subject/installation-salt.ts +112 -0
  391. package/src/lab/subject/protocol-subject.ts +80 -0
  392. package/src/lab/subject/route-subject.ts +74 -0
  393. package/src/lib/app-owned-memory-stores.ts +22 -0
  394. package/src/lib/bounded-body.ts +153 -9
  395. package/src/lib/bun-runtime.ts +125 -12
  396. package/src/lib/bun-stream-caps.ts +13 -9
  397. package/src/lib/codex-restart-contract.ts +120 -0
  398. package/src/lib/config-ownership.ts +6 -2
  399. package/src/lib/destination-policy.ts +65 -1
  400. package/src/lib/errors.ts +44 -2
  401. package/src/lib/fabric-task-execution-authority.ts +7 -0
  402. package/src/lib/fabric-task-host.ts +29 -0
  403. package/src/lib/lab-activation.ts +223 -0
  404. package/src/lib/lab-live-execution-authority.ts +13 -0
  405. package/src/lib/lab-live-host.ts +30 -0
  406. package/src/lib/lab-live-pinned-sender.ts +56 -0
  407. package/src/lib/lab-live-route-production.ts +130 -0
  408. package/src/lib/lab-passive-linker-registration.ts +26 -0
  409. package/src/lib/local-management-attestation.ts +51 -0
  410. package/src/lib/local-management-capability.ts +100 -0
  411. package/src/lib/local-provider-reload-contract.ts +100 -0
  412. package/src/lib/optional-shutdown-hooks.ts +57 -0
  413. package/src/lib/pinned-http.ts +145 -26
  414. package/src/lib/process-control.ts +4 -1
  415. package/src/lib/provider-outbound.ts +49 -9
  416. package/src/lib/redact.ts +419 -3
  417. package/src/lib/self-launch-argv.ts +15 -0
  418. package/src/lib/server-resource-ownership.ts +71 -0
  419. package/src/lib/shadow-call.ts +35 -4
  420. package/src/lib/sse-decoder.ts +41 -0
  421. package/src/lib/state-store-registrations.ts +10 -2
  422. package/src/lib/system-restart-contract.ts +73 -0
  423. package/src/lib/token-estimate.ts +19 -2
  424. package/src/lib/tool-argument-integers.ts +202 -0
  425. package/src/lib/translator-budget.ts +44 -0
  426. package/src/lib/upstream-http-version.ts +57 -0
  427. package/src/lib/upstream-reachability.ts +95 -0
  428. package/src/lib/upstream-retry.ts +156 -3
  429. package/src/lib/windows-atomic-replace.ts +155 -0
  430. package/src/lib/windows-elevation.ts +70 -2
  431. package/src/lib/windows-secret-acl.ts +409 -69
  432. package/src/lib/windows-service-wrappers.ts +72 -0
  433. package/src/lib/windows-text.ts +106 -0
  434. package/src/lib/windows-user-principal.ts +341 -0
  435. package/src/lib/winsw.ts +33 -5
  436. package/src/oauth/account-import/google-antigravity-adapter.ts +74 -0
  437. package/src/oauth/account-import/index.ts +15 -0
  438. package/src/oauth/account-import/parser.ts +83 -0
  439. package/src/oauth/account-import/registry.ts +18 -0
  440. package/src/oauth/account-import/service.ts +75 -0
  441. package/src/oauth/account-import/types.ts +91 -0
  442. package/src/oauth/anthropic.ts +12 -1
  443. package/src/oauth/callback-server.ts +8 -2
  444. package/src/oauth/chatgpt.ts +12 -1
  445. package/src/oauth/command-code.ts +239 -0
  446. package/src/oauth/cursor.ts +46 -5
  447. package/src/oauth/google-antigravity.ts +35 -3
  448. package/src/oauth/health.ts +20 -12
  449. package/src/oauth/index.ts +398 -66
  450. package/src/oauth/key-providers.ts +16 -0
  451. package/src/oauth/kimi.ts +16 -2
  452. package/src/oauth/kiro.ts +50 -6
  453. package/src/oauth/local-token-detect.ts +11 -2
  454. package/src/oauth/log.ts +3 -1
  455. package/src/oauth/login-cli.ts +88 -28
  456. package/src/oauth/nous.ts +798 -0
  457. package/src/oauth/store.ts +119 -21
  458. package/src/oauth/token-guardian.ts +9 -3
  459. package/src/pi/models.ts +2 -2
  460. package/src/providers/alibaba-region-migration.ts +1 -1
  461. package/src/providers/antigravity-models.ts +521 -31
  462. package/src/providers/base-url-choices.ts +10 -0
  463. package/src/providers/codex-capacity.ts +292 -0
  464. package/src/providers/command-code-efforts.ts +144 -0
  465. package/src/providers/context-cap.ts +22 -5
  466. package/src/providers/cursor-pool.ts +72 -0
  467. package/src/providers/derive.ts +253 -6
  468. package/src/providers/fastwire.ts +501 -0
  469. package/src/providers/free-directory.ts +10 -7
  470. package/src/providers/google-vertex-location.ts +14 -0
  471. package/src/providers/key-failover.ts +71 -3
  472. package/src/providers/label.ts +1 -1
  473. package/src/providers/model-discovery-limits.ts +16 -0
  474. package/src/providers/model-discovery.ts +115 -22
  475. package/src/providers/model-rename-migration.ts +255 -0
  476. package/src/providers/model-rename-startup.ts +28 -0
  477. package/src/providers/openai-sidecar.ts +72 -4
  478. package/src/providers/openai-tier-startup.ts +31 -2
  479. package/src/providers/openai-tiers.ts +119 -4
  480. package/src/providers/openai-virtual-models.ts +1 -0
  481. package/src/providers/opencode-zen-rate-limit.ts +102 -0
  482. package/src/providers/provider-id-rewrite.ts +29 -0
  483. package/src/providers/quota.ts +1319 -38
  484. package/src/providers/registry.ts +1429 -111
  485. package/src/providers/request-pacing.ts +310 -0
  486. package/src/providers/service-tier.ts +277 -0
  487. package/src/providers/slug-codec.ts +42 -6
  488. package/src/providers/static-model-discovery.ts +86 -0
  489. package/src/providers/xai-responses-opt-in.ts +15 -0
  490. package/src/providers/xai-transport.ts +11 -4
  491. package/src/reasoning-effort.ts +49 -1
  492. package/src/responses/compaction.ts +26 -1
  493. package/src/responses/custom-tool-compat.ts +266 -0
  494. package/src/responses/hosted-tool-policy.ts +9 -0
  495. package/src/responses/namespace-tool-compat.ts +355 -0
  496. package/src/responses/parser.ts +220 -38
  497. package/src/responses/provider-continuation.ts +98 -0
  498. package/src/responses/provider-opaque-metadata.ts +73 -0
  499. package/src/responses/reasoning-envelope.ts +9 -1
  500. package/src/responses/reasoning-replay-cache.ts +426 -0
  501. package/src/responses/schema.ts +7 -1
  502. package/src/responses/spill-store.ts +75 -10
  503. package/src/responses/state.ts +565 -27
  504. package/src/responses/thought-signature-replay.ts +347 -0
  505. package/src/responses/tool-search-compat.ts +301 -0
  506. package/src/responses/truncated-stop-reason.ts +60 -0
  507. package/src/router.ts +366 -30
  508. package/src/routing/analytics.ts +378 -0
  509. package/src/routing/capability.ts +244 -0
  510. package/src/routing/compatibility/assemble.ts +73 -0
  511. package/src/routing/compatibility/behavior.ts +278 -0
  512. package/src/routing/compatibility/catalog.ts +99 -0
  513. package/src/routing/compatibility/endpoint.ts +52 -0
  514. package/src/routing/compatibility/lab-evidence-provider.ts +130 -0
  515. package/src/routing/compatibility/policy.ts +181 -0
  516. package/src/routing/compatibility/provider-slot.ts +56 -0
  517. package/src/routing/compatibility/reader.ts +110 -0
  518. package/src/routing/compatibility/subject.ts +191 -0
  519. package/src/routing/compatibility/types.ts +64 -0
  520. package/src/routing/compatibility/version.ts +104 -0
  521. package/src/routing/cost.ts +77 -0
  522. package/src/routing/evaluator.ts +495 -0
  523. package/src/routing/health.ts +412 -0
  524. package/src/routing/history/cursor.ts +43 -0
  525. package/src/routing/history/indexer.ts +605 -0
  526. package/src/routing/history/schema.ts +72 -0
  527. package/src/routing/profile-namespace.ts +15 -0
  528. package/src/routing/profile.ts +547 -0
  529. package/src/routing/quota.ts +145 -0
  530. package/src/routing/request-evidence.ts +45 -0
  531. package/src/routing/trace.ts +776 -0
  532. package/src/server/adapter-resolve.ts +2 -29
  533. package/src/server/auth-cors.ts +267 -46
  534. package/src/server/background-lifecycle.ts +182 -0
  535. package/src/server/chat-completions.ts +130 -56
  536. package/src/server/chat-native-sse.ts +331 -0
  537. package/src/server/chat-native.ts +426 -0
  538. package/src/server/claude-messages.ts +159 -43
  539. package/src/server/direct-local-http.ts +347 -0
  540. package/src/server/effort-policy.ts +18 -0
  541. package/src/server/github-copilot-responses-repair.ts +338 -0
  542. package/src/server/gui-static.ts +39 -10
  543. package/src/server/images.ts +94 -12
  544. package/src/server/index.ts +865 -181
  545. package/src/server/lifecycle.ts +284 -13
  546. package/src/server/live.ts +136 -17
  547. package/src/server/local-management-read-client.ts +90 -0
  548. package/src/server/local-provider-reload-client.ts +137 -0
  549. package/src/server/management/agent-settings-routes.ts +398 -103
  550. package/src/server/management/api-key-usage.ts +31 -5
  551. package/src/server/management/body.ts +6 -0
  552. package/src/server/management/combo-routes.ts +62 -24
  553. package/src/server/management/config-routes.ts +464 -51
  554. package/src/server/management/context.ts +80 -2
  555. package/src/server/management/integration-routes.ts +498 -0
  556. package/src/server/management/lab-automation-routes.ts +206 -0
  557. package/src/server/management/lab-routes.ts +563 -0
  558. package/src/server/management/logs-usage-routes.ts +101 -32
  559. package/src/server/management/model-routes.ts +189 -131
  560. package/src/server/management/model-rows.ts +163 -0
  561. package/src/server/management/native-integration-routes.ts +769 -0
  562. package/src/server/management/oauth-account-routes.ts +80 -4
  563. package/src/server/management/provider-capability-config.ts +48 -0
  564. package/src/server/management/provider-routes.ts +764 -157
  565. package/src/server/management/request-history-routes.ts +191 -0
  566. package/src/server/management/routing-analytics-routes.ts +74 -0
  567. package/src/server/management/routing-profile-routes.ts +380 -0
  568. package/src/server/management/shared.ts +27 -11
  569. package/src/server/management/sidebar-routes.ts +47 -31
  570. package/src/server/management/storage-log-guard-routes.ts +186 -0
  571. package/src/server/management/sync-response.ts +69 -0
  572. package/src/server/management/system-restart.ts +288 -32
  573. package/src/server/management/system-routes.ts +77 -0
  574. package/src/server/management/usage-summary-cache.ts +9 -1
  575. package/src/server/management/vision-sidecar-options.ts +167 -0
  576. package/src/server/management/web-search-sidecar-options.ts +120 -0
  577. package/src/server/management-api.ts +115 -14
  578. package/src/server/management-auth.ts +220 -5
  579. package/src/server/passive-route-linker.ts +66 -0
  580. package/src/server/ports.ts +41 -1
  581. package/src/server/proxy-liveness.ts +132 -5
  582. package/src/server/readiness.ts +99 -0
  583. package/src/server/relay-eager.ts +82 -42
  584. package/src/server/relay.ts +236 -76
  585. package/src/server/request-decompress.ts +113 -6
  586. package/src/server/request-log.ts +235 -22
  587. package/src/server/responses/agent-task-recovery-cache.ts +143 -0
  588. package/src/server/responses/agent-task-recovery.ts +465 -0
  589. package/src/server/responses/collaboration.ts +204 -35
  590. package/src/server/responses/compact.ts +442 -55
  591. package/src/server/responses/core.ts +2872 -331
  592. package/src/server/responses/empty-completion-guard.ts +276 -0
  593. package/src/server/responses/encrypted-payload.ts +62 -39
  594. package/src/server/responses/fetch-helpers.ts +79 -4
  595. package/src/server/responses/input-admission.ts +185 -0
  596. package/src/server/responses/pacing-overload.ts +13 -0
  597. package/src/server/responses/policy-fallback.ts +178 -0
  598. package/src/server/responses/responses-field-backfill.ts +251 -0
  599. package/src/server/responses/terminal-guard.ts +26 -5
  600. package/src/server/responses/upstream-error.ts +5 -0
  601. package/src/server/responses/ws-upstream.ts +308 -0
  602. package/src/server/responses-custom-tool-repair.ts +282 -0
  603. package/src/server/responses-item-id-repair.ts +54 -6
  604. package/src/server/responses-json-events.ts +90 -0
  605. package/src/server/responses-model-rewrite.ts +29 -0
  606. package/src/server/responses-reasoning-summary-rewrite.ts +178 -0
  607. package/src/server/responses-snapshot-repair.ts +621 -0
  608. package/src/server/responses-terminal-repair.ts +342 -0
  609. package/src/server/responses-tool-search-repair.ts +267 -0
  610. package/src/server/responses-undeclared-tool-guard.ts +153 -0
  611. package/src/server/responses.ts +18 -2
  612. package/src/server/search.ts +78 -13
  613. package/src/server/sse-frame-buffer.ts +292 -0
  614. package/src/server/sse-payload-rewrite.ts +110 -22
  615. package/src/server/startup-action-control.ts +8 -1
  616. package/src/server/startup-health-cache.ts +19 -1
  617. package/src/server/system-env.ts +80 -9
  618. package/src/server/ws-bridge.ts +39 -38
  619. package/src/service-manager-probe.ts +892 -0
  620. package/src/service.ts +1111 -90
  621. package/src/sidecar/auth.ts +92 -0
  622. package/src/sidecar/candidates.ts +83 -0
  623. package/src/storage/cleanup.ts +2 -2
  624. package/src/storage/scanner.ts +1 -1
  625. package/src/storage/worker-lifecycle.ts +14 -14
  626. package/src/tray/windows-tray.ps1 +83 -9
  627. package/src/tray/windows.ts +43 -16
  628. package/src/types/accounts.ts +37 -0
  629. package/src/types/config.ts +845 -0
  630. package/src/types/provider.ts +545 -0
  631. package/src/types/request.ts +384 -0
  632. package/src/types/tools.ts +131 -0
  633. package/src/types/wire.ts +80 -0
  634. package/src/types.ts +104 -1236
  635. package/src/update/index.ts +32 -19
  636. package/src/update/job.ts +442 -67
  637. package/src/update/notify.ts +12 -6
  638. package/src/update/npm-cache-preflight.d.mts +47 -0
  639. package/src/update/npm-cache-preflight.mjs +201 -0
  640. package/src/update/transactional-install.d.mts +22 -0
  641. package/src/update/transactional-install.mjs +259 -0
  642. package/src/usage/cost.ts +0 -0
  643. package/src/usage/expected-prices.ts +268 -16
  644. package/src/usage/log.ts +606 -41
  645. package/src/usage/summary.ts +177 -9
  646. package/src/usage/user-cost-overlay-reconciler.ts +313 -0
  647. package/src/usage/user-cost-overlays.ts +314 -0
  648. package/src/vision/anthropic-describe.ts +10 -6
  649. package/src/vision/backends.ts +97 -0
  650. package/src/vision/describe.ts +9 -3
  651. package/src/vision/eligibility.ts +250 -0
  652. package/src/vision/index.ts +238 -24
  653. package/src/vision/reasoning.ts +55 -0
  654. package/src/vision/routed-describe.ts +175 -0
  655. package/src/vision/timeout-bounds.ts +9 -0
  656. package/src/web-search/anthropic-executor.ts +13 -7
  657. package/src/web-search/backends.ts +108 -0
  658. package/src/web-search/exa-executor.ts +88 -0
  659. package/src/web-search/executor.ts +11 -3
  660. package/src/web-search/gemini-executor.ts +141 -0
  661. package/src/web-search/index.ts +150 -15
  662. package/src/web-search/loop.ts +279 -50
  663. package/src/web-search/parse.ts +125 -30
  664. package/src/web-search/sources.ts +60 -0
  665. package/src/web-search/xai-executor.ts +219 -0
  666. package/gui/dist/assets/index-DTpMHS4F.js +0 -67
  667. package/gui/dist/assets/index-ZNVDE3C7.css +0 -1
  668. package/gui/dist/provider-icons/antigravity.svg +0 -1
  669. package/gui/dist/provider-icons/claude.svg +0 -1
  670. package/gui/dist/provider-icons/copilot.svg +0 -1
  671. package/gui/dist/provider-icons/cursor.svg +0 -2
  672. package/gui/dist/provider-icons/gemini.svg +0 -1
  673. package/gui/dist/provider-icons/grok-color.svg +0 -1
  674. package/gui/dist/provider-icons/kiro.svg +0 -14
  675. package/src/cli/internal-dispatch.ts +0 -20
@@ -0,0 +1,317 @@
1
+ import { isAbsolute } from "node:path";
2
+ import { codexExecInvocation } from "../codex/exec-invocation";
3
+ import { resolveAndPersistCodexRuntime, type ResolveCodexRuntimeDeps } from "../codex/runtime";
4
+ import type { ResolveDeps, SpawnInvocation } from "../lib/win-exec";
5
+ import {
6
+ apiError,
7
+ apiJson,
8
+ proxyUnreachable,
9
+ resolveBaseUrl,
10
+ type AccountDeps,
11
+ type NativeMainLoginChild,
12
+ type StageLeaseClock,
13
+ } from "./account-api";
14
+
15
+ const USAGE = `Usage:
16
+ ocx account main doctor [--json]
17
+ ocx account main list [--json]
18
+ ocx account main register <label> [--json]
19
+ ocx account main add <label>
20
+ ocx account main switch <profile-id-or-label> --yes [--json]
21
+ ocx account main recover [--rollback --yes] [--json]
22
+
23
+ Native main login profiles change the physical Codex App/CLI login in the effective CODEX_HOME.
24
+ They are independent from the OpenCodex Pool selected by 'ocx account use openai'.`;
25
+
26
+ const STAGE_LEASE_SAFETY_MARGIN_MS = 30_000;
27
+
28
+ const systemStageLeaseClock: StageLeaseClock = {
29
+ now: Date.now,
30
+ setTimeout: (callback, ms) => setTimeout(callback, ms),
31
+ clearTimeout: timer => clearTimeout(timer),
32
+ };
33
+
34
+ interface PublicProfile {
35
+ id: string;
36
+ label: string;
37
+ identityHint: string;
38
+ state: "active" | "inactive";
39
+ }
40
+
41
+ export type NativeMainCodexLoginInvocationDeps =
42
+ & ResolveDeps
43
+ & Pick<ResolveCodexRuntimeDeps, "existsSync" | "execFileSync" | "configDir" | "readFileSync">;
44
+
45
+ function flag(args: string[], name: string): boolean {
46
+ const index = args.indexOf(name);
47
+ if (index < 0) return false;
48
+ args.splice(index, 1);
49
+ return true;
50
+ }
51
+
52
+ function reject(args: string[]): number {
53
+ if (args.length > 0) console.error(`Unexpected argument(s): ${args.join(", ")}`);
54
+ console.error(USAGE);
55
+ return 1;
56
+ }
57
+
58
+ function effectiveCodexHome(result: Record<string, unknown>): string {
59
+ return typeof result.effectiveCodexHome === "string" && result.effectiveCodexHome.length > 0
60
+ ? result.effectiveCodexHome
61
+ : "the effective CODEX_HOME";
62
+ }
63
+
64
+ function printProfiles(profiles: PublicProfile[]): void {
65
+ if (profiles.length === 0) { console.log("No native main login profiles registered."); return; }
66
+ const rows = profiles.map(profile => [profile.state === "active" ? "*" : " ", profile.label, profile.id, profile.identityHint]);
67
+ const header = ["", "LABEL", "PROFILE ID", "IDENTITY"];
68
+ const widths = header.map((value, index) => Math.max(value.length, ...rows.map(row => row[index]!.length)));
69
+ const line = (columns: string[]) => columns.map((value, index) => value.padEnd(widths[index]!)).join(" ").trimEnd();
70
+ console.log([line(header), ...rows.map(line)].join("\n"));
71
+ }
72
+
73
+ export function nativeMainCodexLoginInvocation(
74
+ platform: NodeJS.Platform = process.platform,
75
+ deps: NativeMainCodexLoginInvocationDeps = {},
76
+ ): SpawnInvocation {
77
+ const command = resolveAndPersistCodexRuntime({
78
+ env: deps.env ?? process.env,
79
+ platform,
80
+ existsSync: deps.existsSync,
81
+ execFileSync: deps.execFileSync,
82
+ configDir: deps.configDir,
83
+ readFileSync: deps.readFileSync,
84
+ discoverAlternatives: false,
85
+ }).runtime.command || "codex";
86
+ return codexExecInvocation(command, ["login"], platform, deps);
87
+ }
88
+
89
+ function spawnOfficialCodexLogin(codexHome: string): NativeMainLoginChild {
90
+ const invocation = nativeMainCodexLoginInvocation();
91
+ const child = Bun.spawn([invocation.file, ...invocation.args], {
92
+ env: { ...process.env, CODEX_HOME: codexHome },
93
+ stdin: "inherit",
94
+ stdout: "inherit",
95
+ stderr: "inherit",
96
+ ...(invocation.options.windowsVerbatimArguments ? { windowsVerbatimArguments: true } : {}),
97
+ });
98
+ return child;
99
+ }
100
+
101
+ function abortableDelay(ms: number, signal: AbortSignal, clock: StageLeaseClock): Promise<void> {
102
+ if (signal.aborted) return Promise.resolve();
103
+ return new Promise(resolve => {
104
+ const timer = clock.setTimeout(done, ms);
105
+ signal.addEventListener("abort", done, { once: true });
106
+ function done(): void {
107
+ clock.clearTimeout(timer);
108
+ signal.removeEventListener("abort", done);
109
+ resolve();
110
+ }
111
+ });
112
+ }
113
+
114
+ async function maintainStageLease(
115
+ deps: AccountDeps,
116
+ baseUrl: string,
117
+ stageId: string,
118
+ writerToken: string,
119
+ initialLeaseExpiresAt: number,
120
+ heartbeatIntervalMs: number,
121
+ child: NativeMainLoginChild,
122
+ signal: AbortSignal,
123
+ clock: StageLeaseClock,
124
+ ): Promise<{ lost: boolean }> {
125
+ let leaseExpiresAt = initialLeaseExpiresAt;
126
+ for (;;) {
127
+ if (signal.aborted) return { lost: false };
128
+ const deadlineAt = leaseExpiresAt - STAGE_LEASE_SAFETY_MARGIN_MS;
129
+ if (clock.now() >= deadlineAt) {
130
+ try { child.kill(); } catch { /* child exit below is authoritative */ }
131
+ return { lost: true };
132
+ }
133
+
134
+ const heartbeatAbort = new AbortController();
135
+ const deadlineReached = Symbol("heartbeat-deadline");
136
+ const parentCancelled = Symbol("heartbeat-parent-cancelled");
137
+ let resolveInterruption!: (result: typeof deadlineReached | typeof parentCancelled) => void;
138
+ const interruption = new Promise<typeof deadlineReached | typeof parentCancelled>(resolve => {
139
+ resolveInterruption = resolve;
140
+ });
141
+ const cancelHeartbeat = () => {
142
+ heartbeatAbort.abort();
143
+ resolveInterruption(parentCancelled);
144
+ };
145
+ signal.addEventListener("abort", cancelHeartbeat, { once: true });
146
+ const deadlineTimer = clock.setTimeout(() => {
147
+ heartbeatAbort.abort();
148
+ resolveInterruption(deadlineReached);
149
+ }, Math.max(0, deadlineAt - clock.now()));
150
+ const heartbeatRequest = apiJson(
151
+ deps,
152
+ baseUrl,
153
+ "POST",
154
+ "/api/native-main-profiles/stage/heartbeat",
155
+ { stageId, writerToken },
156
+ { signal: heartbeatAbort.signal },
157
+ );
158
+ const heartbeat = await Promise.race([heartbeatRequest, interruption]);
159
+ clock.clearTimeout(deadlineTimer);
160
+ signal.removeEventListener("abort", cancelHeartbeat);
161
+ if (signal.aborted) return { lost: false };
162
+ if (heartbeat === parentCancelled) return { lost: false };
163
+ if (heartbeat === deadlineReached) {
164
+ try { child.kill(); } catch { /* child exit below is authoritative */ }
165
+ return { lost: true };
166
+ }
167
+ if (heartbeat.status === 200 && typeof heartbeat.json.leaseExpiresAt === "number") {
168
+ leaseExpiresAt = heartbeat.json.leaseExpiresAt;
169
+ await abortableDelay(Math.min(heartbeatIntervalMs, Math.max(0, leaseExpiresAt - STAGE_LEASE_SAFETY_MARGIN_MS - clock.now())), signal, clock);
170
+ continue;
171
+ }
172
+ if (heartbeat.status === 0 && clock.now() < leaseExpiresAt - STAGE_LEASE_SAFETY_MARGIN_MS) {
173
+ await abortableDelay(Math.min(5_000, heartbeatIntervalMs, leaseExpiresAt - STAGE_LEASE_SAFETY_MARGIN_MS - clock.now()), signal, clock);
174
+ continue;
175
+ }
176
+ try { child.kill(); } catch { /* child exit below is authoritative */ }
177
+ return { lost: true };
178
+ }
179
+ }
180
+
181
+ export async function cmdNativeMainAccount(args: string[], deps: AccountDeps): Promise<number> {
182
+ const sub = args.shift();
183
+ const wantsJson = flag(args, "--json");
184
+ const confirmed = flag(args, "--yes");
185
+ const rollback = flag(args, "--rollback");
186
+ const baseUrl = await resolveBaseUrl(deps);
187
+ if (!baseUrl) return proxyUnreachable();
188
+
189
+ if (sub === "doctor" || sub === "list") {
190
+ if (args.length > 0 || confirmed || rollback) return reject(args);
191
+ const path = sub === "doctor" ? "/api/native-main-profiles/doctor" : "/api/native-main-profiles";
192
+ const result = await apiJson(deps, baseUrl, "GET", path);
193
+ if (result.status === 0) return proxyUnreachable();
194
+ if (result.status !== 200) return apiError(result.json, `failed to ${sub} native profiles`);
195
+ if (wantsJson || sub === "doctor") console.log(JSON.stringify(result.json, null, 2));
196
+ else printProfiles(Array.isArray(result.json.profiles) ? result.json.profiles as PublicProfile[] : []);
197
+ return 0;
198
+ }
199
+
200
+ if (sub === "register") {
201
+ const label = args.shift();
202
+ if (!label || args.length > 0 || confirmed || rollback) return reject(args);
203
+ const result = await apiJson(deps, baseUrl, "POST", "/api/native-main-profiles/register", { label });
204
+ if (result.status === 0) return proxyUnreachable();
205
+ if (result.status !== 200) return apiError(result.json, "failed to register the current native login");
206
+ if (wantsJson) console.log(JSON.stringify(result.json, null, 2));
207
+ else console.log(`Registered '${label}' for ${effectiveCodexHome(result.json)}.`);
208
+ return 0;
209
+ }
210
+
211
+ if (sub === "add") {
212
+ const label = args.shift();
213
+ if (!label || args.length > 0 || wantsJson || confirmed || rollback) return reject(args);
214
+ const stage = await apiJson(deps, baseUrl, "POST", "/api/native-main-profiles/stage", {});
215
+ if (stage.status === 0) return proxyUnreachable();
216
+ if (stage.status !== 200) return apiError(stage.json, "failed to prepare native login staging");
217
+ const stageId = typeof stage.json.stageId === "string" ? stage.json.stageId : "";
218
+ const writerToken = typeof stage.json.writerToken === "string" ? stage.json.writerToken : "";
219
+ const stagingHome = typeof stage.json.stagingCodexHome === "string" ? stage.json.stagingCodexHome : "";
220
+ const leaseExpiresAt = typeof stage.json.leaseExpiresAt === "number" ? stage.json.leaseExpiresAt : 0;
221
+ const heartbeatIntervalMs = typeof stage.json.heartbeatIntervalMs === "number"
222
+ ? Math.min(5 * 60_000, Math.max(deps.stageHeartbeatIntervalMinMs ?? 5_000, stage.json.heartbeatIntervalMs))
223
+ : 60_000;
224
+ const stageLeaseClock = deps.stageLeaseClock ?? systemStageLeaseClock;
225
+ const stagedEffectiveHome = effectiveCodexHome(stage.json);
226
+ let exitCode = 1;
227
+ let finished = false;
228
+ let leaseLost = false;
229
+ try {
230
+ if (!stageId || !writerToken || !isAbsolute(stagingHome) || leaseExpiresAt <= stageLeaseClock.now()) {
231
+ throw new Error("The proxy returned an invalid staging session.");
232
+ }
233
+ console.error(`Effective CODEX_HOME: ${stagedEffectiveHome}`);
234
+ console.error(`Starting official Codex login in restricted staging home: ${stagingHome}`);
235
+ const child = deps.spawnCodexLoginImpl
236
+ ? deps.spawnCodexLoginImpl(stagingHome)
237
+ : deps.runCodexLoginImpl
238
+ ? { exited: deps.runCodexLoginImpl(stagingHome), kill: () => {} }
239
+ : spawnOfficialCodexLogin(stagingHome);
240
+ const heartbeatAbort = new AbortController();
241
+ const heartbeat = maintainStageLease(
242
+ deps,
243
+ baseUrl,
244
+ stageId,
245
+ writerToken,
246
+ leaseExpiresAt,
247
+ heartbeatIntervalMs,
248
+ child,
249
+ heartbeatAbort.signal,
250
+ stageLeaseClock,
251
+ );
252
+ try {
253
+ exitCode = await child.exited;
254
+ } finally {
255
+ heartbeatAbort.abort();
256
+ leaseLost = (await heartbeat).lost;
257
+ }
258
+ if (leaseLost) throw new Error("The native-login staging lease was lost before login completed.");
259
+ if (exitCode !== 0) throw new Error("Official Codex login did not complete successfully.");
260
+ const finish = await apiJson(deps, baseUrl, "POST", "/api/native-main-profiles/stage/finish", { stageId, writerToken, label });
261
+ if (finish.status === 0) return proxyUnreachable();
262
+ if (finish.status !== 200) return apiError(finish.json, "failed to encrypt the staged native login");
263
+ finished = true;
264
+ console.log(`Added encrypted native profile '${label}' for ${effectiveCodexHome(finish.json)}.`);
265
+ return 0;
266
+ } catch (error) {
267
+ console.error(`Error: ${error instanceof Error ? error.message : "Official Codex login failed."}`);
268
+ return exitCode === 0 ? 1 : exitCode;
269
+ } finally {
270
+ if (stageId && !finished) {
271
+ const cleanup = await apiJson(deps, baseUrl, "POST", "/api/native-main-profiles/stage/cancel", { stageId, writerToken });
272
+ if (cleanup.status !== 200 || cleanup.json.plaintextMayRemain === true) {
273
+ console.error("Error: the proxy could not confirm native-login staging cleanup; run account main doctor.");
274
+ }
275
+ }
276
+ }
277
+ }
278
+
279
+ if (sub === "switch") {
280
+ const target = args.shift();
281
+ if (!target || args.length > 0 || rollback || !confirmed) {
282
+ if (!confirmed) console.error("Close Codex App/CLI, then pass --yes to confirm it is stopped.");
283
+ return reject(args);
284
+ }
285
+ const result = await apiJson(deps, baseUrl, "POST", "/api/native-main-profiles/switch", { target, confirmedStopped: true });
286
+ if (result.status === 0) return proxyUnreachable();
287
+ if (result.status !== 200) return apiError(result.json, "failed to switch the native login");
288
+ if (wantsJson) console.log(JSON.stringify(result.json, null, 2));
289
+ else {
290
+ const profile = result.json.activeProfile as PublicProfile | undefined;
291
+ console.log(`Native Codex login for ${effectiveCodexHome(result.json)} is now '${profile?.label ?? target}'. Restart Codex App/CLI before continuing.`);
292
+ }
293
+ return 0;
294
+ }
295
+
296
+ if (sub === "recover") {
297
+ if (args.length > 0 || (rollback && !confirmed)) {
298
+ if (rollback && !confirmed) console.error("--rollback changes the native login and requires --yes.");
299
+ return reject(args);
300
+ }
301
+ const result = await apiJson(deps, baseUrl, "POST", "/api/native-main-profiles/recover", rollback
302
+ ? { rollback: true, confirmedStopped: true }
303
+ : { rollback: false });
304
+ if (result.status === 0) return proxyUnreachable();
305
+ if (result.status !== 200) return apiError(result.json, "failed to recover the native-profile transaction");
306
+ if (wantsJson) console.log(JSON.stringify(result.json, null, 2));
307
+ else {
308
+ const home = effectiveCodexHome(result.json);
309
+ console.log(result.json.recovered === true
310
+ ? `Recovery completed for ${home}: ${String(result.json.action ?? "converged")}.`
311
+ : `No recovery journal is pending for ${home}.`);
312
+ }
313
+ return 0;
314
+ }
315
+
316
+ return reject(args);
317
+ }
@@ -2,7 +2,7 @@
2
2
  import { loadConfig } from "../config";
3
3
  import { providerCodexAccountMode } from "../providers/registry";
4
4
  import type { OcxConfig } from "../types";
5
- import { cmdAddKey, cmdAlias, cmdAutoSwitch, cmdClearCooldown, cmdRefresh, cmdRemove } from "./account-extended";
5
+ import { cmdAddKey, cmdAlias, cmdAutoSwitch, cmdClearCooldown, cmdImport, cmdPriority, cmdRefresh, cmdRemove } from "./account-extended";
6
6
  import { apiError, apiJson, classifyAccount, fetchRows, proxyUnreachable, resolveBaseUrl, type AccountDeps, type AccountRow, type AccountType, type ApiResult }
7
7
  from "./account-api";
8
8
 
@@ -22,13 +22,16 @@ const ACCOUNT_USAGE = `Usage:
22
22
  ocx account refresh <provider> [--json]
23
23
  ocx account auto-switch <provider> <on|off|status|threshold <0-100>> [--json]
24
24
  ocx account alias <provider> <account-or-key-id> <display-name|-> [--json]
25
+ ocx account priority <provider> <account-id|main> [<-100..100|first|earlier|normal|later|last|reset>] [--json]
25
26
  ocx account remove <provider> <account-or-key-id|main> --yes [--json]
26
27
  ocx account clear-cooldown <provider> <account-id|main> [--json]
27
28
  ocx account add-key <provider> [--label <label>] [--json]
29
+ ocx account import <provider> --format <format> (--file <path>|--stdin) [--json]
28
30
  ocx account login <provider> [--id <account-id>] [--reauth] [--code -] [--no-wait] [--json]
29
31
  ocx account code <provider> [--flow <flow-id>] [--json] (reads the code from stdin)
30
32
  ocx account cancel <provider> [--flow <flow-id>] [--json]
31
33
  ocx account reset-credits <account-id|main> [--consume --yes] [--json]
34
+ ocx account main <doctor|list|register|add|switch|recover> ...
32
35
 
33
36
  List and switch provider accounts and API-key pools (masked output only).
34
37
  'main' selects the Codex App login for the openai account pool.`;
@@ -66,11 +69,24 @@ function statusText(row: AccountRow): string {
66
69
  return parts.join(" ");
67
70
  }
68
71
 
72
+ /** Signed so the sort direction reads off the column; "-" where ordering does not apply. */
73
+ function priorityText(row: AccountRow): string {
74
+ if (row.priority === undefined) return "-";
75
+ return row.priority > 0 ? `+${row.priority}` : String(row.priority);
76
+ }
77
+
69
78
  export function formatAccountTable(rows: AccountRow[]): string {
70
- const header = ["PROVIDER", "TYPE", "ID", "PLAN/LABEL", "STATUS"];
79
+ const header = ["PROVIDER", "TYPE", "ID", "PLAN/LABEL", "PRIORITY", "STATUS"];
71
80
  const data = rows.map(r => {
72
81
  const keyLabel = r.masked && r.label !== r.masked ? `${r.masked} (${r.label})` : r.masked;
73
- return [r.provider, r.type, displayId(r.id), r.type === "api-key" ? keyLabel ?? "-" : r.label ?? "-", statusText(r)];
82
+ return [
83
+ r.provider,
84
+ r.type,
85
+ displayId(r.id),
86
+ r.type === "api-key" ? keyLabel ?? "-" : r.label ?? "-",
87
+ priorityText(r),
88
+ statusText(r),
89
+ ];
74
90
  });
75
91
  const widths = header.map((h, i) => Math.max(h.length, ...data.map(d => d[i]!.length)));
76
92
  const line = (cols: string[]) => cols.map((c, i) => c.padEnd(widths[i]!)).join(" ").trimEnd();
@@ -243,9 +259,11 @@ async function cmdUse(rest: string[], deps: AccountDeps): Promise<number> {
243
259
  if (wantsJson) console.log(JSON.stringify({ ok: true, provider: name, type: c.type, activeId }, null, 2));
244
260
  else console.log(`${name}: active ${c.type === "api-key" ? "key" : "account"} is now ${displayId(activeId)}`);
245
261
  if (c.type === "codex") {
246
- console.error("Applies to the next request after clearing existing pool affinity; in-flight requests keep their captured account.");
247
- console.error("Note: pool strategy, quota/cooldown/reauthentication state, and failure recovery may later select another eligible account.");
248
- console.error("Conversation context is replayed after account changes, but the provider-side prompt cache may be cold.");
262
+ console.error("Takes effect immediately; running threads move on their next request, and in-flight requests keep the account they captured.");
263
+ const active = await apiJson(deps, baseUrl, "GET", "/api/codex-auth/active");
264
+ if (active.status === 200 && typeof active.json.autoSwitchThreshold === "number" && active.json.autoSwitchThreshold > 0) {
265
+ console.error(`Note: auto-switch (threshold ${active.json.autoSwitchThreshold}%) may override this pin.`);
266
+ }
249
267
  }
250
268
  return 0;
251
269
  }
@@ -259,9 +277,15 @@ export async function cmdAccount(args: string[], deps: AccountDeps = {}): Promis
259
277
  if (sub === "refresh") return await cmdRefresh(rest, deps);
260
278
  if (sub === "auto-switch") return await cmdAutoSwitch(rest, deps);
261
279
  if (sub === "alias" || sub === "rename") return await cmdAlias(rest, deps);
280
+ if (sub === "priority") return await cmdPriority(rest, deps);
262
281
  if (sub === "remove") return await cmdRemove(rest, deps);
263
282
  if (sub === "clear-cooldown") return await cmdClearCooldown(rest, deps);
264
283
  if (sub === "add-key") return await cmdAddKey(rest, deps);
284
+ if (sub === "import") return await cmdImport(rest, deps);
285
+ if (sub === "main") {
286
+ const { cmdNativeMainAccount } = await import("./account-main");
287
+ return await cmdNativeMainAccount(rest, deps);
288
+ }
265
289
  if (["login", "reauth", "code", "cancel", "reset-credits"].includes(sub ?? "")) {
266
290
  const { handleAccountAuthCommand } = await import("./account-auth");
267
291
  return await handleAccountAuthCommand(sub!, rest, deps) ?? 1;
package/src/cli/agent.ts CHANGED
@@ -13,6 +13,13 @@ import {
13
13
  type RuntimeApiDeps,
14
14
  } from "./runtime-api";
15
15
 
16
+ interface WebSearchModelOption {
17
+ value: string;
18
+ model: string;
19
+ backend: "openai" | "anthropic";
20
+ authSlot?: boolean;
21
+ }
22
+
16
23
  const USAGE = `Usage:
17
24
  ocx agent [status] [--json]
18
25
  ocx agent injection <status|set> [--model <id|->] [--effort <level|->]
@@ -20,7 +27,8 @@ const USAGE = `Usage:
20
27
  ocx agent effort <status|set> [--main <level|->] [--subagent <level|->] [--json]
21
28
  ocx agent subagents <status|set|clear> [model,model...] [--json]
22
29
  ocx agent fallback <status|set|clear> [model,model...] [--poll-ms <5000-600000>] [--json]
23
- ocx agent sidecar <status|web|vision> [--model <id|->] [--backend <openai|anthropic|->]
30
+ ocx agent sidecar <status|web|vision> [--list] [--model <id|->]
31
+ [--backend web:<openai|anthropic|xai|gemini|exa|-> vision:<openai|anthropic|routed|->]
24
32
  [--reasoning <level>] [--max-descriptions <n>] [--json]`;
25
33
 
26
34
  function clearable(value: string | undefined): string | null | undefined {
@@ -152,6 +160,29 @@ async function sidecar(argv: string[], deps: RuntimeApiDeps): Promise<void> {
152
160
  return;
153
161
  }
154
162
  if (section !== "web" && section !== "vision") throw new CliUsageError("sidecar must be web, vision, or status", USAGE);
163
+ // --list must be consumed BEFORE rejectArgs sees it. It prints the server's
164
+ // candidate set — the exact list the GUI picker shows (#2188): the server
165
+ // computes it once and every surface consumes it, so the CLI cannot drift.
166
+ const wantsList = takeFlag(args, "--list");
167
+ if (wantsList) {
168
+ rejectArgs(args, USAGE);
169
+ const settings = await runtimeRequest("/api/sidecar-settings", {}, deps) as {
170
+ webSearchModels?: WebSearchModelOption[];
171
+ visionModels?: Array<{ value: string; backend?: string; baseline?: boolean }>;
172
+ };
173
+ if (section === "web") {
174
+ const options = settings.webSearchModels ?? [];
175
+ printData(options, wantsJson, options.length === 0
176
+ ? ["no runnable web-search sidecar models (log in to ChatGPT or Anthropic)"]
177
+ : options.map(option => `${option.value} [${option.backend}]${option.authSlot ? " (auth slot)" : ""}`));
178
+ } else {
179
+ const options = settings.visionModels ?? [];
180
+ printData(options, wantsJson, options.length === 0
181
+ ? ["no eligible vision describers"]
182
+ : options.map(option => `${option.value}${option.backend ? ` [${option.backend}]` : ""}${option.baseline ? " (baseline)" : ""}`));
183
+ }
184
+ return;
185
+ }
155
186
  const model = takeOption(args, "--model");
156
187
  const backend = takeOption(args, "--backend");
157
188
  const reasoning = takeOption(args, "--reasoning");
@@ -163,6 +194,19 @@ async function sidecar(argv: string[], deps: RuntimeApiDeps): Promise<void> {
163
194
  if (reasoning !== undefined) settings.reasoning = reasoning;
164
195
  if (maxDescriptionsPerTurn !== undefined) settings.maxDescriptionsPerTurn = maxDescriptionsPerTurn;
165
196
  if (Object.keys(settings).length === 0) throw new CliUsageError("at least one sidecar option is required", USAGE);
197
+ if (section === "web" && model !== undefined && model !== "-") {
198
+ const offered = await runtimeRequest("/api/sidecar-settings", {}, deps) as {
199
+ webSearchModels?: WebSearchModelOption[];
200
+ };
201
+ const requestedBackend = backend === "-" ? "openai" : backend;
202
+ const option = offered.webSearchModels?.find(candidate =>
203
+ (candidate.value === model || candidate.model === model)
204
+ && (requestedBackend === undefined || candidate.backend === requestedBackend));
205
+ if (option) {
206
+ settings.model = option.model;
207
+ if (backend !== "-") settings.backend = option.backend;
208
+ }
209
+ }
166
210
  const body = section === "web" ? { webSearch: settings } : { vision: settings };
167
211
  const result = await runtimeRequest("/api/sidecar-settings", { method: "PUT", body: JSON.stringify(body) }, deps);
168
212
  printData(result, wantsJson, [`${section} sidecar settings updated.`]);
@@ -0,0 +1,73 @@
1
+ import type { OcxConfig } from "../types";
2
+ import { injectClaudeAgentDefs } from "../claude/agents-inject";
3
+ import { fetchClaudeContextWindows } from "./claude";
4
+ import type { ReadinessGate } from "../server/readiness";
5
+
6
+ export interface ClaudeAgentStartupSyncDeps {
7
+ fetchContextWindows?: typeof fetchClaudeContextWindows;
8
+ injectAgentDefs?: typeof injectClaudeAgentDefs;
9
+ warn?: (message: string) => void;
10
+ }
11
+
12
+ /**
13
+ * Keep the public readiness gate pending until both startup reconciliations have settled.
14
+ *
15
+ * The Codex sync remains the authority for ready versus failed. Claude roster repair is
16
+ * deliberately best-effort (#2200), but readiness must not become observable between the
17
+ * Codex write and that repair: a service manager could otherwise launch Claude Code against
18
+ * stale `ocx-*.md` files. A small forwarding gate delays only the successful transition;
19
+ * terminal Codex failure is still published immediately.
20
+ */
21
+ export async function reconcileClientStartupBeforeReady<T>(
22
+ readinessGate: ReadinessGate,
23
+ syncCodex: (deferredGate: ReadinessGate) => Promise<T>,
24
+ syncClaudeRoster: () => Promise<unknown>,
25
+ ): Promise<T> {
26
+ let codexReady = false;
27
+ const deferredGate: ReadinessGate = {
28
+ getStatus: () => readinessGate.getStatus(),
29
+ markReady: () => { codexReady = true; },
30
+ markFailed: () => readinessGate.markFailed(),
31
+ };
32
+
33
+ const result = await syncCodex(deferredGate);
34
+ await syncClaudeRoster();
35
+ if (codexReady) readinessGate.markReady();
36
+ return result;
37
+ }
38
+
39
+ /**
40
+ * Reconcile the generated Claude Code roster after the proxy listener is live.
41
+ *
42
+ * This belongs to the owning CLI lifecycle rather than `startServer`: the latter is also a
43
+ * library/test primitive and must not mutate a developer's real `~/.claude` directory merely
44
+ * because an in-process test server was created. The live Management API supplies the same bounded
45
+ * context-window map used by `ocx claude`; failure keeps startup available and falls back to an
46
+ * unmarked roster. Disabled integrations skip discovery and prune verified-owned definitions.
47
+ */
48
+ export async function syncClaudeAgentDefsAtProxyStartup(
49
+ config: OcxConfig,
50
+ port: number,
51
+ deps: ClaudeAgentStartupSyncDeps = {},
52
+ ): Promise<string[] | null> {
53
+ const inject = deps.injectAgentDefs ?? injectClaudeAgentDefs;
54
+ const warn = deps.warn ?? (message => console.warn(message));
55
+
56
+ try {
57
+ if (config.claudeCode?.enabled === false || config.claudeCode?.injectAgents === false) {
58
+ return inject(config, {});
59
+ }
60
+
61
+ let windows: Record<string, number> = {};
62
+ try {
63
+ windows = await (deps.fetchContextWindows ?? fetchClaudeContextWindows)(config, port);
64
+ } catch {
65
+ // Startup remains best-effort. The next management mutation or `ocx claude` launch can
66
+ // restore context markers after a transient catalog/Management API failure.
67
+ }
68
+ return inject(config, windows);
69
+ } catch (error) {
70
+ warn(`⚠ Claude agent definitions could not be synced at proxy startup: ${error instanceof Error ? error.message : String(error)}`);
71
+ return null;
72
+ }
73
+ }
@@ -1,6 +1,7 @@
1
1
  import { readFileSync, writeFileSync } from "node:fs";
2
2
  import { resolve } from "node:path";
3
3
  import { loadConfig, saveConfigPreservingClaudeCode } from "../config";
4
+ import { setIntegrationEnabled } from "../codex/desired-state";
4
5
  import {
5
6
  DESKTOP_FAMILIES,
6
7
  moveDesktopRoute,
@@ -10,10 +11,11 @@ import {
10
11
  type DesktopProfile,
11
12
  } from "../claude/desktop-profile";
12
13
  import { writeDesktop3pConfig, type Desktop3pConfigMode, parseDesktop3pModeArgs } from "../claude/desktop-3p";
13
- import { filterCatalogVisibleModels, desktopVisibleNativeSlugs } from "../codex/catalog";
14
+ import { filterCatalogVisibleModels, desktopVisibleNativeSlugs, nativeContextLimits } from "../codex/catalog";
14
15
  import { buildClaudeDesktopState, fetchAllModels } from "../server/management-api";
15
16
  import { findLiveProxy } from "../server/proxy-liveness";
16
17
  import { runtimeRequest } from "./runtime-api";
18
+ import { OPENAI_CODEX_PROVIDER_ID } from "../providers/openai-tiers";
17
19
 
18
20
  function isFamily(value: string | undefined): value is DesktopFamily {
19
21
  return !!value && (DESKTOP_FAMILIES as readonly string[]).includes(value);
@@ -41,7 +43,11 @@ export async function applyProfile(
41
43
  profile: DesktopProfile,
42
44
  mode: Desktop3pConfigMode,
43
45
  deps: ApplyProfileDeps = {},
44
- ): Promise<{ ok: boolean; path: string; reason?: string }> {
46
+ ): Promise<{ ok: boolean; path: string; reason?: string; warning?: string }> {
47
+ // Explicit apply is an enable action. Persist intent before any Desktop write
48
+ // so a process crash cannot leave a gateway profile that startup immediately removes.
49
+ const desired = setIntegrationEnabled("claude-desktop", true);
50
+ if (!desired.ok) return { ok: false, path: "", reason: desired.message };
45
51
  const config = loadConfig();
46
52
  const state = await buildClaudeDesktopState(config, profile);
47
53
  config.claudeCode = { ...(config.claudeCode ?? {}), desktopProfile: state.profile };
@@ -52,7 +58,7 @@ export async function applyProfile(
52
58
  // serving process installs the map there; a local-only write leaves the
53
59
  // daemon unable to decode aliases, and the provider rejects them (400).
54
60
  const post = deps.postApplyImpl ?? (async (m: Desktop3pConfigMode, p: DesktopProfile) =>
55
- runtimeRequest<{ ok?: boolean; path?: string; error?: string }>(
61
+ runtimeRequest<{ ok?: boolean; path?: string; error?: string; saved?: boolean; warning?: string }>(
56
62
  "/api/claude-desktop/apply",
57
63
  // The daemon's config may be older than what we just saved, so the
58
64
  // profile travels with the request instead of being re-read there.
@@ -61,12 +67,26 @@ export async function applyProfile(
61
67
  try {
62
68
  const applied = await post(mode, state.profile);
63
69
  if (applied.ok === false) return { ok: false, path: applied.path ?? "", reason: applied.error ?? "daemon apply failed" };
64
- return { ok: true, path: applied.path ?? "" };
70
+ // Partial success: Desktop was written but the applied marker was not
71
+ // persisted. Pass the degradation up instead of reporting a clean apply.
72
+ const partial = (applied as { saved?: boolean; warning?: string }).saved === false;
73
+ return {
74
+ ok: true,
75
+ path: applied.path ?? "",
76
+ ...(partial ? { warning: (applied as { warning?: string }).warning ?? "applied marker was not saved" } : {}),
77
+ };
65
78
  } catch (error) {
66
79
  return { ok: false, path: "", reason: error instanceof Error ? error.message : String(error) };
67
80
  }
68
81
  }
69
82
  const allModels = await fetchAllModels(config);
83
+ // The toggle can persist OFF while fetchAllModels was awaiting (same race the
84
+ // management writers fence). Re-read persisted intent immediately before the
85
+ // writer; a lost race is a discriminated skip, not a write.
86
+ const { claudeDesktopIntegrationEnabledNow } = await import("../codex/desired-state");
87
+ if (!claudeDesktopIntegrationEnabledNow()) {
88
+ return { ok: false, path: "", reason: "desired_state_changed" };
89
+ }
70
90
  const routed = filterCatalogVisibleModels(allModels, config).map(model => ({
71
91
  provider: model.provider,
72
92
  id: model.id,
@@ -79,6 +99,7 @@ export async function applyProfile(
79
99
  config.apiKeys?.[0]?.key,
80
100
  mode,
81
101
  state.profile,
102
+ nativeContextLimits(config),
82
103
  );
83
104
  return { ok: result.written, path: result.path, reason: result.reason };
84
105
  }
@@ -111,6 +132,9 @@ export async function handleClaudeDesktopCommand(argv: string[], deps: ApplyProf
111
132
  return 1;
112
133
  }
113
134
  console.log(`Claude Desktop 설정을 적용했습니다: ${result.path}`);
135
+ // The write landed; only the bookkeeping marker did not. Saying nothing
136
+ // would leave the saved-vs-applied display wrong with no explanation.
137
+ if (result.warning) console.warn(`⚠️ ${result.warning}`);
114
138
  console.log("Claude Desktop을 완전히 종료한 뒤 다시 열어 주세요.");
115
139
  return 0;
116
140
  } catch (error) {
@@ -175,6 +199,7 @@ export async function handleClaudeDesktopCommand(argv: string[], deps: ApplyProf
175
199
  if (flags.includes("--apply")) {
176
200
  const result = await applyProfile(reconciled, "static", deps);
177
201
  if (!result.ok) { console.error(`프로필은 저장했지만 Desktop 적용에 실패했습니다: ${result.reason ?? "unknown error"}`); return 1; }
202
+ if (result.warning) console.warn(`⚠️ ${result.warning}`);
178
203
  }
179
204
  console.log("Claude Desktop 프로필을 가져왔습니다.");
180
205
  return 0;