@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,358 @@
1
+ /**
2
+ * Source-preserving mutation for one plain block-map YAML fragment.
3
+ *
4
+ * Shared client settings are not ours to re-render. This scanner accepts only
5
+ * the small, unambiguous source shape we can patch byte-for-byte around the
6
+ * owned leaf. Every candidate is parsed again and compared with the complete
7
+ * expected document; unsupported YAML fails closed and never falls back to a
8
+ * whole-document serializer.
9
+ */
10
+ import { renderYaml } from "./serialize";
11
+
12
+ interface SourceLine {
13
+ start: number;
14
+ end: number;
15
+ body: string;
16
+ }
17
+
18
+ interface LocatedEntry {
19
+ lines: readonly SourceLine[];
20
+ index: number;
21
+ indent: number;
22
+ endIndex: number;
23
+ }
24
+
25
+ interface MissingEntry {
26
+ lines: readonly SourceLine[];
27
+ missingDepth: number;
28
+ indent: number;
29
+ insertAt: number;
30
+ }
31
+
32
+ type LocatedPath = { kind: "existing"; entry: LocatedEntry } | { kind: "missing"; entry: MissingEntry };
33
+
34
+ export type YamlFragmentMutation =
35
+ | { kind: "upsert"; value: unknown }
36
+ | { kind: "remove"; createdContainers: readonly string[] };
37
+
38
+ export type OmpYamlMutation =
39
+ | { kind: "upsert"; value: unknown }
40
+ | { kind: "remove"; removeEmptyProviders: boolean };
41
+
42
+ function sourceLines(text: string): SourceLine[] {
43
+ const lines: SourceLine[] = [];
44
+ const matcher = /[^\r\n]*(?:\r\n|\n|$)/gu;
45
+ for (const match of text.matchAll(matcher)) {
46
+ const raw = match[0];
47
+ if (raw.length === 0) continue;
48
+ const start = match.index;
49
+ const body = raw.endsWith("\r\n")
50
+ ? raw.slice(0, -2)
51
+ : raw.endsWith("\n") ? raw.slice(0, -1) : raw;
52
+ lines.push({ start, end: start + raw.length, body });
53
+ }
54
+ return lines;
55
+ }
56
+
57
+ function leadingSpaces(line: string): number | null {
58
+ const leading = line.match(/^[ \t]*/u)?.[0] ?? "";
59
+ return leading.includes("\t") ? null : leading.length;
60
+ }
61
+
62
+ function isBlank(line: string): boolean {
63
+ return line.trim().length === 0;
64
+ }
65
+
66
+ function isComment(line: string): boolean {
67
+ return line.trimStart().startsWith("#");
68
+ }
69
+
70
+ function hasInlineComment(line: string): boolean {
71
+ return line.includes("#");
72
+ }
73
+
74
+ function regexpEscape(value: string): string {
75
+ return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
76
+ }
77
+
78
+ function isPlainBlockKey(line: string, indent: number, key: string): boolean {
79
+ const spaces = leadingSpaces(line);
80
+ if (spaces !== indent) return false;
81
+ const rest = line.slice(indent);
82
+ return new RegExp(`^${regexpEscape(key)}:[ ]*(?:#.*)?$`, "u").test(rest);
83
+ }
84
+
85
+ function containerEnd(lines: readonly SourceLine[], start: number, indent: number): number | null {
86
+ for (let index = start + 1; index < lines.length; index += 1) {
87
+ const body = lines[index]!.body;
88
+ const spaces = leadingSpaces(body);
89
+ if (spaces === null) return null;
90
+ if (isBlank(body)) continue;
91
+ if (isComment(body)) {
92
+ if (spaces <= indent) return index;
93
+ continue;
94
+ }
95
+ if (spaces <= indent) return index;
96
+ }
97
+ return lines.length;
98
+ }
99
+
100
+ function childEnd(
101
+ lines: readonly SourceLine[],
102
+ start: number,
103
+ parentEnd: number,
104
+ indent: number,
105
+ ): number | null {
106
+ for (let index = start + 1; index < parentEnd; index += 1) {
107
+ const body = lines[index]!.body;
108
+ const spaces = leadingSpaces(body);
109
+ if (spaces === null) return null;
110
+ // Blank lines and same-level comments remain outside our replacement.
111
+ // Deeper comments belong to the leaf and would be destroyed, so refuse.
112
+ if (isBlank(body)) return index;
113
+ if (isComment(body)) return spaces <= indent ? index : null;
114
+ if (spaces <= indent) return index;
115
+ if (hasInlineComment(body)) return null;
116
+ }
117
+ return parentEnd;
118
+ }
119
+
120
+ function canonicalValue(value: unknown): unknown {
121
+ if (Array.isArray(value)) return value.map(canonicalValue);
122
+ if (value && typeof value === "object") {
123
+ return Object.fromEntries(
124
+ Object.entries(value as Record<string, unknown>)
125
+ .sort(([left], [right]) => left.localeCompare(right))
126
+ .map(([key, child]) => [key, canonicalValue(child)]),
127
+ );
128
+ }
129
+ return value;
130
+ }
131
+
132
+ function semanticallyMatches(text: string, expected: unknown): boolean {
133
+ try {
134
+ const parsed = text.trim().length === 0 ? {} : Bun.YAML.parse(text);
135
+ return JSON.stringify(canonicalValue(parsed)) === JSON.stringify(canonicalValue(expected));
136
+ } catch {
137
+ return false;
138
+ }
139
+ }
140
+
141
+ function lineEnding(text: string): "\n" | "\r\n" {
142
+ return text.includes("\r\n") ? "\r\n" : "\n";
143
+ }
144
+
145
+ function readPath(doc: unknown, path: readonly string[]): unknown {
146
+ let cursor = doc;
147
+ for (const key of path) {
148
+ if (cursor === null || typeof cursor !== "object" || Array.isArray(cursor)) return undefined;
149
+ cursor = (cursor as Record<string, unknown>)[key];
150
+ if (cursor === undefined) return undefined;
151
+ }
152
+ return cursor;
153
+ }
154
+
155
+ function isPlainRecord(value: unknown): value is Record<string, unknown> {
156
+ return value !== null && typeof value === "object" && !Array.isArray(value);
157
+ }
158
+
159
+ function immediateIndent(
160
+ lines: readonly SourceLine[],
161
+ start: number,
162
+ end: number,
163
+ parentIndent: number,
164
+ ): number | null {
165
+ let indent: number | null = null;
166
+ for (let index = start; index < end; index += 1) {
167
+ const body = lines[index]!.body;
168
+ const spaces = leadingSpaces(body);
169
+ if (spaces === null) return null;
170
+ if (isBlank(body) || isComment(body) || spaces <= parentIndent) continue;
171
+ indent = indent === null ? spaces : Math.min(indent, spaces);
172
+ }
173
+ return indent ?? parentIndent + 2;
174
+ }
175
+
176
+ /** Locate a path only when every present segment is a plain block-map key. */
177
+ function locatePath(text: string, parsed: unknown, path: readonly string[]): LocatedPath | null {
178
+ if (path.length === 0 || !isPlainRecord(parsed)) return null;
179
+ const lines = sourceLines(text);
180
+ if (lines.some(line => leadingSpaces(line.body) === null)) return null;
181
+
182
+ let rangeStart = 0;
183
+ let rangeEnd = lines.length;
184
+ let parentIndent = -2;
185
+ const prefix: string[] = [];
186
+ for (let depth = 0; depth < path.length; depth += 1) {
187
+ const indent = depth === 0 ? 0 : immediateIndent(lines, rangeStart, rangeEnd, parentIndent);
188
+ if (indent === null) return null;
189
+ const matches: number[] = [];
190
+ for (let index = rangeStart; index < rangeEnd; index += 1) {
191
+ if (isPlainBlockKey(lines[index]!.body, indent, path[depth]!)) matches.push(index);
192
+ }
193
+ if (matches.length > 1) return null;
194
+ prefix.push(path[depth]!);
195
+ if (matches.length === 0) {
196
+ // The parser saw this key through syntax we do not patch (quoted/flow,
197
+ // merge aliases, or an ambiguous indentation shape).
198
+ if (readPath(parsed, prefix) !== undefined) return null;
199
+ const insertAt = rangeEnd < lines.length ? lines[rangeEnd]!.start : text.length;
200
+ return { kind: "missing", entry: { lines, missingDepth: depth, indent, insertAt } };
201
+ }
202
+
203
+ const index = matches[0]!;
204
+ const end = containerEnd(lines, index, indent);
205
+ if (end === null) return null;
206
+ if (depth === path.length - 1) {
207
+ if (hasInlineComment(lines[index]!.body)) return null;
208
+ const leafEnd = childEnd(lines, index, end, indent);
209
+ if (leafEnd === null) return null;
210
+ return { kind: "existing", entry: { lines, index, indent, endIndex: leafEnd } };
211
+ }
212
+ if (!isPlainRecord(readPath(parsed, prefix))) return null;
213
+ rangeStart = index + 1;
214
+ rangeEnd = end;
215
+ parentIndent = indent;
216
+ }
217
+ return null;
218
+ }
219
+
220
+ function nestedValue(path: readonly string[], value: unknown): Record<string, unknown> {
221
+ let nested: unknown = value;
222
+ for (let index = path.length - 1; index >= 0; index -= 1) nested = { [path[index]!]: nested };
223
+ return nested as Record<string, unknown>;
224
+ }
225
+
226
+ function rendered(value: unknown, indent: number, eol: "\n" | "\r\n"): string {
227
+ return renderYaml(value as Record<string, unknown>, indent).replaceAll("\n", eol);
228
+ }
229
+
230
+ function preserveFinalNewline(candidate: string, original: string, eol: "\n" | "\r\n"): string {
231
+ if (original.length > 0 && !original.endsWith("\n") && candidate.endsWith(eol)) {
232
+ return candidate.slice(0, -eol.length);
233
+ }
234
+ return candidate;
235
+ }
236
+
237
+ function upsertSource(
238
+ text: string,
239
+ parsed: unknown,
240
+ path: readonly string[],
241
+ value: unknown,
242
+ ): string | null {
243
+ const located = locatePath(text, parsed, path);
244
+ if (located === null) return null;
245
+ const eol = lineEnding(text);
246
+ if (located.kind === "existing") {
247
+ const { lines, index, indent, endIndex } = located.entry;
248
+ const startOffset = lines[index]!.start;
249
+ const endOffset = endIndex < lines.length ? lines[endIndex]!.start : text.length;
250
+ const candidate = `${text.slice(0, startOffset)}${rendered({ [path[path.length - 1]!]: value }, indent, eol)}${text.slice(endOffset)}`;
251
+ return preserveFinalNewline(candidate, text, eol);
252
+ }
253
+
254
+ const { missingDepth, indent, insertAt } = located.entry;
255
+ const prefix = insertAt > 0 && !text.slice(0, insertAt).endsWith("\n") ? eol : "";
256
+ const insertion = rendered(nestedValue(path.slice(missingDepth), value), indent, eol);
257
+ const candidate = `${text.slice(0, insertAt)}${prefix}${insertion}${text.slice(insertAt)}`;
258
+ return preserveFinalNewline(candidate, text, eol);
259
+ }
260
+
261
+ function removeExactPath(text: string, path: readonly string[], requireEmpty: boolean): string | null {
262
+ let parsed: unknown;
263
+ try {
264
+ parsed = text.trim().length === 0 ? {} : Bun.YAML.parse(text);
265
+ } catch {
266
+ return null;
267
+ }
268
+ const located = locatePath(text, parsed, path);
269
+ if (located === null || located.kind !== "existing") return null;
270
+ const { lines, index, endIndex } = located.entry;
271
+ const startOffset = lines[index]!.start;
272
+ const endOffset = endIndex < lines.length ? lines[endIndex]!.start : text.length;
273
+ if (requireEmpty) {
274
+ const sourceBody = text.slice(lines[index]!.end, endOffset);
275
+ if (sourceBody.trim().length > 0) return null;
276
+ }
277
+ return `${text.slice(0, startOffset)}${text.slice(endOffset)}`;
278
+ }
279
+
280
+ function planSourceRemoval(
281
+ text: string,
282
+ path: readonly string[],
283
+ createdContainers: readonly string[],
284
+ ): { text: string; prunedContainers: string[] } | null {
285
+ let next = removeExactPath(text, path, false);
286
+ if (next === null) return null;
287
+ const created = new Set(createdContainers);
288
+ const prunedContainers: string[] = [];
289
+ for (let depth = path.length - 1; depth >= 1; depth -= 1) {
290
+ const containerPath = path.slice(0, depth);
291
+ const encoded = containerPath.join("\u0000");
292
+ if (!created.has(encoded)) continue;
293
+ const pruned = removeExactPath(next, containerPath, true);
294
+ // A user-added sibling/comment makes this ancestor source-owned now. Keep
295
+ // it (and every parent) while still removing our leaf.
296
+ if (pruned === null) break;
297
+ next = pruned;
298
+ prunedContainers.push(encoded);
299
+ }
300
+ return { text: next, prunedContainers };
301
+ }
302
+
303
+ /** Containers whose source ranges are still empty and safe to prune. */
304
+ export function sourcePrunableYamlContainers(
305
+ text: string,
306
+ path: readonly string[],
307
+ createdContainers: readonly string[],
308
+ ): readonly string[] | null {
309
+ return planSourceRemoval(text, path, createdContainers)?.prunedContainers ?? null;
310
+ }
311
+
312
+ function removeSource(
313
+ text: string,
314
+ path: readonly string[],
315
+ createdContainers: readonly string[],
316
+ ): string | null {
317
+ return planSourceRemoval(text, path, createdContainers)?.text ?? null;
318
+ }
319
+
320
+ /**
321
+ * Patch one arbitrary plain block-map path, preserving every other byte.
322
+ */
323
+ export function patchYamlFragmentSource(
324
+ text: string,
325
+ path: readonly string[],
326
+ mutation: YamlFragmentMutation,
327
+ expected: unknown,
328
+ ): string | null {
329
+ let parsed: unknown;
330
+ try {
331
+ parsed = text.trim().length === 0 ? {} : Bun.YAML.parse(text);
332
+ } catch {
333
+ return null;
334
+ }
335
+ const patched = mutation.kind === "upsert"
336
+ ? upsertSource(text, parsed, path, mutation.value)
337
+ : removeSource(text, path, mutation.createdContainers);
338
+ return patched !== null && semanticallyMatches(patched, expected) ? patched : null;
339
+ }
340
+
341
+ /** Backward-compatible OMP wrapper around the generic path patcher. */
342
+ export function patchOmpYamlSource(
343
+ text: string,
344
+ mutation: OmpYamlMutation,
345
+ expected: unknown,
346
+ ): string | null {
347
+ return patchYamlFragmentSource(
348
+ text,
349
+ ["providers", "opencodex"],
350
+ mutation.kind === "upsert"
351
+ ? mutation
352
+ : {
353
+ kind: "remove",
354
+ createdContainers: mutation.removeEmptyProviders ? ["providers"] : [],
355
+ },
356
+ expected,
357
+ );
358
+ }
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Refresh a file integration only when OpenCodex already owns its block.
3
+ *
4
+ * This is the safe bridge between an implicit operation such as `ocx sync`
5
+ * and the explicit integration writer. An implicit sync may update a block
6
+ * the user previously enabled, but it must never claim an unowned block,
7
+ * recreate one the user removed, or overwrite edits made after our write.
8
+ */
9
+ import type { ExportModel } from "../clients/config-export";
10
+ import type { OcxConfig } from "../types";
11
+ import type { IntegrationIO } from "./config-io";
12
+ import type { IntegrationClientId } from "./registry";
13
+ import { runIntegrationMutationFlight } from "./mutation-flight";
14
+ import { createIntegrationStateStore, type IntegrationStateStore } from "./store";
15
+ import {
16
+ refreshIntegrationCoordinated,
17
+ type CoordinatedIntegrationOptions,
18
+ } from "./writer";
19
+
20
+ export interface OwnedIntegrationRefreshInput {
21
+ clientId: IntegrationClientId;
22
+ /** Lazy in `ocx sync`, so an unowned client does not even load the catalog. */
23
+ models: readonly ExportModel[] | (() => Promise<readonly ExportModel[]>);
24
+ config: OcxConfig;
25
+ port: number;
26
+ env?: NodeJS.ProcessEnv;
27
+ home?: string;
28
+ store?: IntegrationStateStore;
29
+ io?: IntegrationIO;
30
+ }
31
+
32
+ export interface OwnedIntegrationRefreshOutcome {
33
+ readonly client: IntegrationClientId;
34
+ readonly ok: boolean;
35
+ readonly changed?: boolean;
36
+ readonly reason?: string;
37
+ }
38
+
39
+ /**
40
+ * Returns `null` when the client has never been connected by OpenCodex.
41
+ * Callers use that distinction to report "left alone" rather than "skipped".
42
+ */
43
+ export async function refreshOwnedIntegration(
44
+ input: OwnedIntegrationRefreshInput,
45
+ options?: CoordinatedIntegrationOptions,
46
+ ): Promise<OwnedIntegrationRefreshOutcome | null> {
47
+ const store = input.store ?? createIntegrationStateStore();
48
+
49
+ // The ownership record is the user's durable opt-in. Do this check before
50
+ // reading or classifying the target so an implicit sync can never turn an
51
+ // unowned provider block into one of ours.
52
+ const record = store.readRecords()[input.clientId];
53
+ if (!record || record.clientId !== input.clientId) return null;
54
+
55
+ const { models: suppliedModels, ...rest } = input;
56
+ const models = typeof suppliedModels === "function"
57
+ ? await suppliedModels()
58
+ : suppliedModels;
59
+ const bound = { ...rest, models, store };
60
+ const result = await runIntegrationMutationFlight(
61
+ input.clientId,
62
+ "refresh",
63
+ input.io?.now ?? Date.now,
64
+ () => refreshIntegrationCoordinated(bound, options),
65
+ );
66
+ return result.ok
67
+ ? {
68
+ client: input.clientId,
69
+ ok: true,
70
+ changed: result.changed,
71
+ ...(result.state === "absent" ? { reason: result.message } : {}),
72
+ }
73
+ : { client: input.clientId, ok: false, reason: result.message };
74
+ }
@@ -0,0 +1,111 @@
1
+ /**
2
+ * What opencodex remembers about a client between operations.
3
+ *
4
+ * Two hashes, because the two questions are genuinely independent: the FILE
5
+ * hash identifies the exact result for restore and for clients whose writer
6
+ * re-serializes the whole document. The BLOCK hash identifies OMP's surgically
7
+ * patched fragment and detects catalog drift. One hash cannot safely answer
8
+ * both questions in a shared client config.
9
+ *
10
+ * Design of record: devlog/_fin/260802_client_toggle_api/021 §2.
11
+ */
12
+ import { createHash } from "node:crypto";
13
+ import { mkdirSync, readFileSync } from "node:fs";
14
+ import { dirname, join } from "node:path";
15
+ import type { ManagedContribution, ManagedFragment } from "../clients/config-export";
16
+ import { atomicWriteFile, getConfigDir } from "../config";
17
+ import type { IntegrationClientId } from "./registry";
18
+
19
+ /** 16 hex chars — the same shape as the Claude Desktop applied fingerprint. */
20
+ export function fingerprint(text: string): string {
21
+ return createHash("sha256").update(text).digest("hex").slice(0, 16);
22
+ }
23
+
24
+ /**
25
+ * Canonical bytes of a contribution. Fragments are sorted by path so two builds
26
+ * of the same contribution hash identically regardless of emission order.
27
+ */
28
+ export function canonicalContribution(contribution: ManagedContribution): string {
29
+ const sorted = [...contribution.fragments].sort((a, b) => {
30
+ const left = a.path.join("\u0000");
31
+ const right = b.path.join("\u0000");
32
+ return left < right ? -1 : left > right ? 1 : 0;
33
+ });
34
+ return JSON.stringify(sorted.map(fragment => [fragment.path, fragment.value]));
35
+ }
36
+
37
+ export interface OwnershipRecord {
38
+ clientId: IntegrationClientId;
39
+ configPath: string;
40
+ /** Hash of the WHOLE file as we left it — protects whole-file restore. */
41
+ fileFingerprint: string;
42
+ /** Hash of our contribution — detects catalog/port drift. */
43
+ blockFingerprint: string;
44
+ /** The exact paths we own. Removal touches these and nothing else. */
45
+ fragmentPaths: readonly (readonly string[])[];
46
+ /**
47
+ * Containers this apply had to CREATE, `\0`-joined.
48
+ *
49
+ * Kimi's `models` map exists only because our aliases need somewhere to
50
+ * live. Without this, disable left it behind as an empty container in a file
51
+ * the user never asked us to restructure — while a `providers: {}` the user
52
+ * wrote themselves must survive. Optional because records written before
53
+ * this field existed simply prune nothing, which is the old behavior.
54
+ */
55
+ createdContainers?: readonly string[];
56
+ appliedAt: string;
57
+ opId: string;
58
+ }
59
+
60
+ /**
61
+ * The integrations directory itself. Every primitive takes THIS path, never a
62
+ * config root, so a caller cannot accidentally produce
63
+ * `<root>/integrations/integrations` by passing an already-resolved value.
64
+ */
65
+ export function integrationsDir(configDir: string = getConfigDir()): string {
66
+ return join(configDir, "integrations");
67
+ }
68
+
69
+ /** `atomicWriteFile` does not create parents. */
70
+ export function ensureDir(filePath: string): void {
71
+ mkdirSync(dirname(filePath), { recursive: true, mode: 0o700 });
72
+ }
73
+
74
+ function recordsPath(dir: string): string {
75
+ return join(dir, "records.json");
76
+ }
77
+
78
+ export function readRecords(
79
+ dir: string = integrationsDir(),
80
+ ): Partial<Record<IntegrationClientId, OwnershipRecord>> {
81
+ try {
82
+ const parsed: unknown = JSON.parse(readFileSync(recordsPath(dir), "utf8"));
83
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
84
+ return parsed as Partial<Record<IntegrationClientId, OwnershipRecord>>;
85
+ }
86
+ } catch {
87
+ // Missing or corrupt records mean "we remember nothing", which the
88
+ // classifier reads as a conflict for an existing block. Fail closed: an
89
+ // unreadable memory is never permission to delete.
90
+ }
91
+ return {};
92
+ }
93
+
94
+ export function writeRecord(record: OwnershipRecord, dir: string = integrationsDir()): void {
95
+ const all = readRecords(dir);
96
+ all[record.clientId] = record;
97
+ ensureDir(recordsPath(dir));
98
+ atomicWriteFile(recordsPath(dir), `${JSON.stringify(all, null, 2)}\n`);
99
+ }
100
+
101
+ export function deleteRecord(clientId: IntegrationClientId, dir: string = integrationsDir()): void {
102
+ const all = readRecords(dir);
103
+ if (!(clientId in all)) return;
104
+ delete all[clientId];
105
+ ensureDir(recordsPath(dir));
106
+ atomicWriteFile(recordsPath(dir), `${JSON.stringify(all, null, 2)}\n`);
107
+ }
108
+
109
+ export function fragmentPathsOf(contribution: ManagedContribution): readonly (readonly string[])[] {
110
+ return contribution.fragments.map((fragment: ManagedFragment) => fragment.path);
111
+ }
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Where each file-toggle client keeps its config, and what we are allowed to do
3
+ * to it.
4
+ *
5
+ * The export registry (src/clients/config-export.ts) says how to RENDER a
6
+ * client's config. This one says where it lives, how to tell whether the client
7
+ * is installed at all, and whether a remote bind is safe for it.
8
+ *
9
+ * Design of record: devlog/_fin/260802_client_toggle_api/021 §1.
10
+ */
11
+ import { homedir } from "node:os";
12
+ import { join } from "node:path";
13
+ import {
14
+ EXPORT_CLIENTS,
15
+ dshConfigPath,
16
+ dshHomeDir,
17
+ gajaeConfigPath,
18
+ gajaeHomeDir,
19
+ hermesConfigPath,
20
+ hermesHomeDir,
21
+ kimiConfigPath,
22
+ kimiHomeDir,
23
+ mcodeConfigPath,
24
+ mcodeHomeDir,
25
+ ompAgentDir,
26
+ ompModelsConfigPath,
27
+ opencodeGlobalConfigPath,
28
+ openclawConfigPath,
29
+ openclawHomeDir,
30
+ piAgentDir,
31
+ piConfigPath,
32
+ primeAgentDir,
33
+ primeConfigPath,
34
+ zcodeConfigPath,
35
+ zcodeHomeDir,
36
+ type ExportClientId,
37
+ } from "../clients/config-export";
38
+
39
+ /**
40
+ * Readability alias. WP1 owns the type; this never introduces a second one, so
41
+ * the dependency only ever points backwards.
42
+ */
43
+ export type IntegrationClientId = ExportClientId;
44
+
45
+ export interface IntegrationClientSpec {
46
+ id: IntegrationClientId;
47
+ /** The client's config file, honoring that client's own environment override. */
48
+ configPath: (env?: NodeJS.ProcessEnv, home?: string) => string;
49
+ /** Directory whose existence is the cheap "is it installed?" signal. */
50
+ detectDir: (env?: NodeJS.ProcessEnv, home?: string) => string;
51
+ /** Patch only this block-map YAML leaf; never re-render the shared file. */
52
+ sourcePreservingYaml?: { path: readonly string[] };
53
+ /** Coordinate the complete mutation through a sibling config lock. */
54
+ writerLock?: { suffix: ".lock" };
55
+ }
56
+
57
+ /**
58
+ * True when the client has nowhere to put the dedicated admission header a
59
+ * non-loopback bind requires, so a generated config would simply be rejected.
60
+ *
61
+ * Read from the export registry rather than restated here: two lists of the
62
+ * same fact drift, and this one decides whether we write a file that 401s.
63
+ */
64
+ export function isLoopbackOnly(clientId: IntegrationClientId): boolean {
65
+ return EXPORT_CLIENTS[clientId].loopbackOnly;
66
+ }
67
+
68
+ function xdgConfigHome(env: NodeJS.ProcessEnv, home: string): string {
69
+ const xdg = env.XDG_CONFIG_HOME;
70
+ return xdg && xdg.length > 0 ? xdg : join(home, ".config");
71
+ }
72
+
73
+ export const INTEGRATION_CLIENTS: Record<IntegrationClientId, IntegrationClientSpec> = {
74
+ opencode: {
75
+ id: "opencode",
76
+ // These take `home` explicitly. The export registry's `destination` reads
77
+ // the real home directory, which is right for telling a user where their
78
+ // file lives and wrong for a writer that a test must be able to redirect.
79
+ configPath: (env = process.env, home = homedir()) => opencodeGlobalConfigPath(env, home),
80
+ detectDir: (env = process.env, home = homedir()) => join(xdgConfigHome(env, home), "opencode"),
81
+ },
82
+ pi: {
83
+ id: "pi",
84
+ configPath: (env = process.env, home = homedir()) => piConfigPath(env, home),
85
+ /*
86
+ * Deliberately not `piAgentDir` unconditionally. Without an override the
87
+ * install signal stays `~/.pi`, which is what it has always been: narrowing
88
+ * it to `~/.pi/agent` would flip a user who has the former without the
89
+ * latter from installed to absent, and this change is about honoring the
90
+ * override, not about redefining detection. With an override there is no
91
+ * parent worth testing — the variable names the agent directory itself — so
92
+ * that directory becomes the signal.
93
+ */
94
+ detectDir: (env = process.env, home = homedir()) =>
95
+ env.PI_CODING_AGENT_DIR?.trim() ? piAgentDir(env, home) : join(home, ".pi"),
96
+ },
97
+ omp: {
98
+ id: "omp",
99
+ configPath: (env = process.env, home = homedir()) => ompModelsConfigPath(env, home),
100
+ detectDir: (env = process.env, home = homedir()) => ompAgentDir(env, home),
101
+ sourcePreservingYaml: { path: ["providers", "opencodex"] },
102
+ },
103
+ hermes: {
104
+ id: "hermes",
105
+ configPath: (env = process.env, home = homedir()) => hermesConfigPath(env, home),
106
+ detectDir: (env = process.env, home = homedir()) => hermesHomeDir(env, home),
107
+ },
108
+ openclaw: {
109
+ id: "openclaw",
110
+ configPath: (env = process.env, home = homedir()) => openclawConfigPath(env, home),
111
+ detectDir: (env = process.env, home = homedir()) => openclawHomeDir(env, home),
112
+ },
113
+ kimi: {
114
+ id: "kimi",
115
+ configPath: (env = process.env, home = homedir()) => kimiConfigPath(env, home),
116
+ detectDir: (env = process.env, home = homedir()) => kimiHomeDir(env, home),
117
+ // Kimi reads credentials only from config.toml — it never consults the
118
+ // environment — so there is no way to point it at a remote bind without
119
+ // serializing the user's key.
120
+ },
121
+ gajae: {
122
+ id: "gajae",
123
+ configPath: (env = process.env, home = homedir()) => gajaeConfigPath(env, home),
124
+ detectDir: (env = process.env, home = homedir()) => gajaeHomeDir(env, home),
125
+ },
126
+ dsh: {
127
+ id: "dsh",
128
+ configPath: (env = process.env, home = homedir()) => dshConfigPath(env, home),
129
+ detectDir: (env = process.env, home = homedir()) => dshHomeDir(env, home),
130
+ sourcePreservingYaml: { path: ["llm-pi-ai", "providers", "opencodex"] },
131
+ writerLock: { suffix: ".lock" },
132
+ },
133
+ mcode: {
134
+ id: "mcode",
135
+ configPath: (env = process.env, home = homedir()) => mcodeConfigPath(env, home),
136
+ detectDir: (env = process.env, home = homedir()) => mcodeHomeDir(env, home),
137
+ writerLock: { suffix: ".lock" },
138
+ },
139
+ zcode: {
140
+ id: "zcode",
141
+ configPath: (env = process.env, home = homedir()) => zcodeConfigPath(env, home),
142
+ detectDir: (env = process.env, home = homedir()) => zcodeHomeDir(env, home),
143
+ },
144
+ prime: {
145
+ id: "prime",
146
+ configPath: (env = process.env, home = homedir()) => primeConfigPath(env, home),
147
+ // The agent directory, not its parent: `PRIME_AGENT_CODING_AGENT_DIR` names
148
+ // that directory directly, so there is no parent to test when the override
149
+ // is set. Same choice as OMP, whose detect signal is `ompAgentDir`.
150
+ detectDir: (env = process.env, home = homedir()) => primeAgentDir(env, home),
151
+ },
152
+ };
153
+
154
+ export const INTEGRATION_CLIENT_IDS: readonly IntegrationClientId[] =
155
+ Object.keys(INTEGRATION_CLIENTS) as IntegrationClientId[];
156
+
157
+ export function isIntegrationClientId(value: string): value is IntegrationClientId {
158
+ return Object.prototype.hasOwnProperty.call(INTEGRATION_CLIENTS, value);
159
+ }