@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,314 @@
1
+ /**
2
+ * Runtime registry for user-configured provider cost overlays
3
+ * (`providers.<name>.modelCosts` in config.json — per-model prices in ocx's
4
+ * flat `modelXxx` convention, mirroring opencode's per-model pricing).
5
+ *
6
+ * The usage cost estimator stays pure: it receives overlays as parameters and
7
+ * defaults to this registry, which is refreshed at the config chokepoints
8
+ * (loadConfig and every persist path). A refresh that actually changes the
9
+ * rows replaces the active array with a NEW identity and bumps a version
10
+ * counter, so the estimator's memo and the /api/usage summary cache skip stale
11
+ * rows without cross-module invalidation. Refreshes with byte-identical rows
12
+ * are no-ops: config is reloaded at many chokepoints and an unchanged reload
13
+ * must not churn the version (see refreshUserCostOverlays). The configured
14
+ * provider-name set is part of the change identity: adding or removing a
15
+ * provider changes which names may collapse to a label base in the resolver,
16
+ * so it bumps the version even when no overlay row changed.
17
+ *
18
+ * Display-time estimation only — these rows never affect billing.
19
+ */
20
+ import type { OcxConfig, OcxProviderConfig, ProviderCostOverlay } from "../types";
21
+ import { MAX_COST4_RATE, type ExpectedPriceOverlay } from "./expected-prices";
22
+ import { redactSecretString } from "../lib/redact";
23
+
24
+ const EMPTY: readonly ExpectedPriceOverlay[] = [];
25
+
26
+ let active: readonly ExpectedPriceOverlay[] = EMPTY;
27
+ let activeSignature = "";
28
+ let activeConfigured = new Set<string>();
29
+ let version = 0;
30
+ let preservedDiskOnlyProviders: Record<string, OcxProviderConfig> | null = null;
31
+
32
+ /**
33
+ * Preservation owner metadata rides through the shallow config projections in
34
+ * config.ts via an enumerable SYMBOL key. JSON.stringify ignores symbol keys,
35
+ * so the tag is process-local only and can never reach config.json or a DTO.
36
+ */
37
+ const PRESERVATION_OWNER_STATE = Symbol("opencodex.user-cost-overlay-preservation-owner");
38
+ const PERSISTED_PROVIDER_DELETIONS = Symbol("opencodex.persisted-provider-deletions");
39
+
40
+ type PreservationOwnerState = {
41
+ refs: number;
42
+ config: OcxConfig;
43
+ ownedProviders: Set<string>;
44
+ };
45
+
46
+ type PreservationTaggedConfig = OcxConfig & {
47
+ [PRESERVATION_OWNER_STATE]?: PreservationOwnerState;
48
+ [PERSISTED_PROVIDER_DELETIONS]?: readonly string[];
49
+ };
50
+
51
+ const preservationOwnerStates = new Set<PreservationOwnerState>();
52
+
53
+ function providerNames(config: OcxConfig): Set<string> {
54
+ return new Set(Object.keys(config.providers ?? {}));
55
+ }
56
+
57
+ /** Register one active live-config owner. Multiple server leases may share one config object. */
58
+ export function registerPreservedProviderOwner(config: OcxConfig): void {
59
+ const tagged = config as PreservationTaggedConfig;
60
+ const existing = tagged[PRESERVATION_OWNER_STATE];
61
+ if (existing && preservationOwnerStates.has(existing)) {
62
+ existing.refs += 1;
63
+ existing.ownedProviders = providerNames(config);
64
+ return;
65
+ }
66
+ const state: PreservationOwnerState = {
67
+ refs: 1,
68
+ config,
69
+ ownedProviders: providerNames(config),
70
+ };
71
+ // Enumerable is deliberate: projectCustomModelCatalogMigration and the live
72
+ // binding guard use object spread, which must carry this process-local tag to
73
+ // the serialization view. Symbol keys are still omitted by JSON.stringify.
74
+ Object.defineProperty(tagged, PRESERVATION_OWNER_STATE, {
75
+ value: state,
76
+ enumerable: true,
77
+ configurable: true,
78
+ });
79
+ preservationOwnerStates.add(state);
80
+ }
81
+
82
+ /**
83
+ * Refresh a registered owner's provider snapshot from a successful disk read.
84
+ *
85
+ * A provider still present on disk remains owned even if the live object
86
+ * temporarily omits it: management DELETE has an async-import gap between
87
+ * mutating the live config and committing the write, and a reconciler tick in
88
+ * that gap must not erase the deletion authority. Conversely, providers that
89
+ * disappeared from disk are no longer considered owned, and newly present
90
+ * providers are adopted only when this live config actually has the row.
91
+ */
92
+ export function refreshPreservedProviderOwner(config: OcxConfig, disk: OcxConfig): void {
93
+ const state = (config as PreservationTaggedConfig)[PRESERVATION_OWNER_STATE];
94
+ if (!state || !preservationOwnerStates.has(state)) return;
95
+ const diskNames = providerNames(disk);
96
+ for (const name of [...state.ownedProviders]) {
97
+ if (!diskNames.has(name)) state.ownedProviders.delete(name);
98
+ }
99
+ for (const name of Object.keys(config.providers ?? {})) {
100
+ if (diskNames.has(name)) state.ownedProviders.add(name);
101
+ }
102
+ }
103
+
104
+ /** Release one active live-config owner lease. */
105
+ export function unregisterPreservedProviderOwner(config: OcxConfig): void {
106
+ const tagged = config as PreservationTaggedConfig;
107
+ const state = tagged[PRESERVATION_OWNER_STATE];
108
+ if (!state || !preservationOwnerStates.has(state)) return;
109
+ state.refs -= 1;
110
+ if (state.refs > 0) return;
111
+ preservationOwnerStates.delete(state);
112
+ if (tagged[PRESERVATION_OWNER_STATE] === state) {
113
+ delete tagged[PRESERVATION_OWNER_STATE];
114
+ }
115
+ }
116
+
117
+ /**
118
+ * Remember provider rows that exist on disk but are intentionally absent from
119
+ * the live routing config (added by an external editor after the proxy booted).
120
+ * They are merged back at the config serialization boundary so an unrelated
121
+ * in-process save cannot erase the external provider and its overlay.
122
+ */
123
+ export function setPreservedDiskOnlyProviders(
124
+ providers: Record<string, OcxProviderConfig> | null,
125
+ ): void {
126
+ preservedDiskOnlyProviders = providers;
127
+ }
128
+
129
+ /**
130
+ * Commit an explicit provider deletion only after the persisted serialization
131
+ * view has been accepted by the config write path. This clears the stale
132
+ * preservation row and removes the provider from every active live projection,
133
+ * so a second owner cannot resurrect it on a later unrelated save.
134
+ */
135
+ function commitPersistedProviderDeletions(config: OcxConfig): void {
136
+ const tagged = config as PreservationTaggedConfig;
137
+ const deletions = tagged[PERSISTED_PROVIDER_DELETIONS];
138
+ if (!deletions || deletions.length === 0) return;
139
+
140
+ const deleted = new Set(deletions);
141
+ if (preservedDiskOnlyProviders) {
142
+ const next = { ...preservedDiskOnlyProviders };
143
+ for (const name of deleted) delete next[name];
144
+ preservedDiskOnlyProviders = Object.keys(next).length > 0 ? next : null;
145
+ }
146
+
147
+ for (const state of preservationOwnerStates) {
148
+ for (const name of deleted) {
149
+ state.ownedProviders.delete(name);
150
+ if (state.config.providers) delete state.config.providers[name];
151
+ }
152
+ }
153
+ delete tagged[PERSISTED_PROVIDER_DELETIONS];
154
+ }
155
+
156
+ /**
157
+ * A serialization view of `config` that keeps externally added providers on
158
+ * disk without adding them to live routing state. Live providers win when a
159
+ * name exists in both maps.
160
+ *
161
+ * A registered owner also carries the provider names it previously owned. If
162
+ * that owner now omits one of those providers, the omission is an intentional
163
+ * in-process deletion rather than an old projection that never knew the row.
164
+ * Suppress only those names from preservation for this write. The suppression
165
+ * is committed globally only after refreshUserCostOverlays receives the
166
+ * successfully persisted view, so a failed atomic write cannot destroy the
167
+ * preservation safety net.
168
+ */
169
+ export function withPreservedDiskOnlyProviders(config: OcxConfig): OcxConfig {
170
+ const tagged = config as PreservationTaggedConfig;
171
+ const owner = tagged[PRESERVATION_OWNER_STATE];
172
+ const deletedProviders = owner && preservationOwnerStates.has(owner)
173
+ ? [...owner.ownedProviders].filter(name => !Object.hasOwn(config.providers ?? {}, name))
174
+ : [];
175
+ const deletedSet = new Set(deletedProviders);
176
+
177
+ let preserved: Record<string, OcxProviderConfig> | null = null;
178
+ if (preservedDiskOnlyProviders) {
179
+ const filtered = Object.entries(preservedDiskOnlyProviders)
180
+ .filter(([name]) => !deletedSet.has(name));
181
+ if (filtered.length > 0) preserved = Object.fromEntries(filtered);
182
+ }
183
+
184
+ let persisted: OcxConfig;
185
+ if (preserved && Object.keys(preserved).length > 0) {
186
+ persisted = {
187
+ ...config,
188
+ providers: {
189
+ ...preserved,
190
+ ...config.providers,
191
+ },
192
+ };
193
+ } else if (deletedProviders.length > 0) {
194
+ // Return a distinct object so the persisted-deletion marker remains scoped
195
+ // to this serialization attempt rather than the long-lived live config.
196
+ persisted = { ...config };
197
+ } else {
198
+ return config;
199
+ }
200
+
201
+ if (deletedProviders.length > 0) {
202
+ Object.defineProperty(persisted as PreservationTaggedConfig, PERSISTED_PROVIDER_DELETIONS, {
203
+ value: deletedProviders,
204
+ enumerable: false,
205
+ configurable: true,
206
+ });
207
+ }
208
+ return persisted;
209
+ }
210
+
211
+ /** Test-only reset for the preserved disk-only provider registry. */
212
+ export function resetPreservedDiskOnlyProvidersForTests(): void {
213
+ preservedDiskOnlyProviders = null;
214
+ preservationOwnerStates.clear();
215
+ }
216
+
217
+ /** Exact four fields accepted for a user-configured cost tuple. */
218
+ export const COST4_RATE_KEYS = ["input", "output", "cacheRead", "cacheWrite"] as const;
219
+
220
+ /** Shared per-rate predicate used by config validation, display sanitization, and runtime lifting. */
221
+ export function isValidCost4Rate(rate: unknown): rate is number {
222
+ return typeof rate === "number"
223
+ && Number.isFinite(rate)
224
+ && rate >= 0
225
+ && rate <= MAX_COST4_RATE;
226
+ }
227
+
228
+ /** True when `value` is a complete cost entry: all four rates are non-negative finite numbers. */
229
+ function validCost4(value: unknown): value is ProviderCostOverlay {
230
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
231
+ const entry = value as Record<string, unknown>;
232
+ return COST4_RATE_KEYS.every(key => isValidCost4Rate(entry[key]));
233
+ }
234
+
235
+ /**
236
+ * Rebuild the active user-overlay rows from the current config. Malformed rows
237
+ * are skipped (config validation reports them separately); a provider with no
238
+ * overlay contributes nothing.
239
+ */
240
+ export function refreshUserCostOverlays(config: OcxConfig): void {
241
+ // Only persisted serialization views carry PERSISTED_PROVIDER_DELETIONS.
242
+ // Committing here means changed writes update owner state only after the
243
+ // atomic write succeeds, while byte-identical successful saves also converge.
244
+ commitPersistedProviderDeletions(config);
245
+
246
+ const rows: ExpectedPriceOverlay[] = [];
247
+ const providers = config.providers;
248
+ if (providers) {
249
+ for (const [providerName, provider] of Object.entries(providers)) {
250
+ const costs = provider?.modelCosts;
251
+ if (!costs || typeof costs !== "object" || Array.isArray(costs)) continue;
252
+ for (const [modelId, cost4] of Object.entries(costs)) {
253
+ if (!modelId.trim() || !validCost4(cost4)) continue;
254
+ rows.push({
255
+ provider: providerName,
256
+ modelId,
257
+ // Copy ONLY the four validated rate fields: a hand-edited row may
258
+ // carry extra properties (e.g. a misplaced apiKey) that must never
259
+ // reach display estimates or /api/logs through the registry.
260
+ cost4: {
261
+ input: cost4.input,
262
+ output: cost4.output,
263
+ cacheRead: cost4.cacheRead,
264
+ cacheWrite: cost4.cacheWrite,
265
+ },
266
+ // Display provenance only: redact token-shaped provider/model ids so
267
+ // the source string can never echo a pasted key. Matching still uses
268
+ // the raw fields, and the change-detection signature below MUST keep
269
+ // them raw — distinct ids would otherwise collapse to "[REDACTED]"
270
+ // and skip the version bump.
271
+ source: `config:providers.${redactSecretString(providerName)}.modelCosts[${redactSecretString(modelId)}]`,
272
+ verifiedAt: "user-configured",
273
+ status: "verified",
274
+ });
275
+ }
276
+ }
277
+ }
278
+ // Config is re-loaded at many chokepoints (server start, migrations, persist
279
+ // paths). Bumping the version on every load — even when nothing changed —
280
+ // would invalidate the /api/usage summary cache on unrelated reloads and
281
+ // churn the cost memo. Only a real overlay change bumps the version, so the
282
+ // cache survives reloads of an unchanged config.
283
+ // The signature MUST compare the raw matching fields: two different
284
+ // secret-shaped ids would both redact to "[REDACTED]" and falsely look
285
+ // unchanged, skipping the version bump and serving stale estimates. The
286
+ // signature is process-local state and is never serialized to a response;
287
+ // only the display `source` above is redacted.
288
+ // The configured provider-name set is part of the identity too: adding or
289
+ // removing a provider (even one without an overlay) changes which names are
290
+ // allowed to collapse to a label base, so the resolver memo and the
291
+ // /api/usage summary cache must be invalidated on that change as well.
292
+ const configuredNames = Object.keys(providers ?? {}).sort();
293
+ const signature = `${JSON.stringify(configuredNames)}\u0000${JSON.stringify(rows)}`;
294
+ if (signature === activeSignature) return;
295
+ activeSignature = signature;
296
+ active = rows;
297
+ activeConfigured = new Set(configuredNames);
298
+ version++;
299
+ }
300
+
301
+ /** Active user-configured overlay rows (stable identity until the next refresh). */
302
+ export function activeUserCostOverlays(): readonly ExpectedPriceOverlay[] {
303
+ return active;
304
+ }
305
+
306
+ /** Monotonic version bumped on every refresh; used by the estimator memo key. */
307
+ export function userCostOverlayVersion(): number {
308
+ return version;
309
+ }
310
+
311
+ /** Configured provider names from the last refresh (pricing-namespace identity). */
312
+ export function activeConfiguredProviders(): ReadonlySet<string> {
313
+ return activeConfigured;
314
+ }
@@ -3,7 +3,7 @@ import { CLAUDE_CODE_HEADERS, claudeCodeSessionId } from "../adapters/client-fin
3
3
  import { signalWithTimeout, cancelBodyOnAbort } from "../lib/abort";
4
4
  import { sidecarEnter } from "../lib/sidecar-tracker";
5
5
  import { fetchWithResetRetry } from "../lib/upstream-retry";
6
- import { getValidAccessToken } from "../oauth";
6
+ import { getValidAccessToken, publicOAuthAuthenticationErrorMessage } from "../oauth";
7
7
  import { ANTHROPIC_OAUTH_BETA, CLAUDE_CODE_SYSTEM_INSTRUCTION } from "../oauth/anthropic";
8
8
  import type { DescribeOutcome, VisionSettings } from "./describe";
9
9
 
@@ -67,8 +67,8 @@ export async function parseAnthropicVisionSSE(res: Response): Promise<DescribeOu
67
67
  const delta = isRecord(data.delta) ? data.delta : {};
68
68
  if (delta.type === "text_delta" && typeof delta.text === "string") text += delta.text;
69
69
  } else if (data.type === "error") {
70
- const error = isRecord(data.error) ? data.error : {};
71
- terminalError = typeof error.message === "string" ? error.message : "anthropic vision sidecar stream error";
70
+ // Provider-authored stream errors can contain credentials, paths, or response bodies.
71
+ terminalError = "anthropic vision sidecar stream error";
72
72
  }
73
73
  };
74
74
 
@@ -116,7 +116,7 @@ export async function describeImageAnthropic(
116
116
  try {
117
117
  token = await getValidAccessToken(providerName);
118
118
  } catch (error) {
119
- return { text: "", error: `anthropic vision sidecar auth failed: ${error instanceof Error ? error.message : String(error)}` };
119
+ return { text: "", error: `anthropic vision sidecar auth failed: ${publicOAuthAuthenticationErrorMessage(error)}` };
120
120
  }
121
121
 
122
122
  const headers: Record<string, string> = {
@@ -166,7 +166,11 @@ export async function describeImageAnthropic(
166
166
  if (!res.ok) {
167
167
  const responseText = await res.text().catch(() => "");
168
168
  console.warn(`[vision] anthropic sidecar HTTP ${res.status} (${Date.now() - startedAt}ms)`);
169
- return { text: "", error: `anthropic vision sidecar HTTP ${res.status}: ${responseText.slice(0, 200)}` };
169
+ if (res.status === 401) {
170
+ return { text: "", error: `anthropic vision sidecar auth failed: ${publicOAuthAuthenticationErrorMessage(new Error(responseText))}` };
171
+ }
172
+ // Upstream bodies are untrusted and may contain credentials, paths, or provider diagnostics.
173
+ return { text: "", error: `anthropic vision sidecar HTTP ${res.status}` };
170
174
  }
171
175
  const detachBodyGuard = cancelBodyOnAbort(res.body, linkedSignal.signal);
172
176
  try {
@@ -177,7 +181,7 @@ export async function describeImageAnthropic(
177
181
  } catch (error) {
178
182
  const kind = error instanceof Error && error.name === "TimeoutError" ? "timeout" : "connect_error";
179
183
  console.warn(`[vision] anthropic sidecar ${kind} (${Date.now() - startedAt}ms)`);
180
- return { text: "", error: error instanceof Error ? error.message : String(error) };
184
+ return { text: "", error: `anthropic vision sidecar ${kind}` };
181
185
  } finally {
182
186
  sidecarExit();
183
187
  linkedSignal.cleanup();
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Which backends may DESCRIBE images for the vision sidecar, and which
3
+ * candidate rows each can describe through (#2188 vision rules; roadmap 170
4
+ * REVISED: the "routed" backend).
5
+ *
6
+ * A SIBLING of WEB_SEARCH_BACKENDS, not a shared table: vision has no
7
+ * per-model probe (rule 2 is "− provably text-only", enforced by
8
+ * modelAcceptsImageInput, not here), carries per-side baseline models, and
9
+ * excludes non-LLM backends like exa.
10
+ *
11
+ * Three backends, not one per provider: "openai" and "anthropic" carry auth
12
+ * semantics loopback routing cannot replicate (forwarded ChatGPT headers,
13
+ * OAuth beta fences) and their defaults must not drift. Every OTHER
14
+ * picker-visible provider row reaches the describer through "routed" — a
15
+ * loopback self-fetch of the proxy's own /v1/chat/completions, where the
16
+ * router and adapters already speak each provider's wire. That is what makes
17
+ * this table closed under provider growth: a new provider needs no new
18
+ * describe executor.
19
+ */
20
+ import type { OcxConfig } from "../types";
21
+ import type { SidecarAuthState } from "../sidecar/auth";
22
+ import { listOpenAiForwardSidecarCandidates } from "../providers/openai-sidecar";
23
+ import type { VisionCandidateModel, VisionSidecarBackend } from "./eligibility";
24
+
25
+ export interface VisionBackendDescriptor {
26
+ backend: VisionSidecarBackend;
27
+ /** Liveness signal for this backend. */
28
+ isActive(auth: SidecarAuthState, config: OcxConfig): boolean;
29
+ /** Which candidate rows this backend's describe executor can actually run. */
30
+ candidateMatch(candidate: VisionCandidateModel, auth: SidecarAuthState): boolean;
31
+ /**
32
+ * Default entry for this side: cheap, image-capable, present in every
33
+ * deployment. Only the two universal sides carry one — "routed" has no
34
+ * universal model to name.
35
+ */
36
+ baseline?: string;
37
+ /** Stable option ordering (baselines first within a side). */
38
+ rank: number;
39
+ }
40
+
41
+ export const VISION_BACKENDS: readonly VisionBackendDescriptor[] = [
42
+ {
43
+ backend: "openai",
44
+ // The OpenAI describer needs a CANONICAL ChatGPT forward provider, not
45
+ // merely a provider keyed "openai" — same predicate the runtime sidecar
46
+ // resolver uses. Deliberately NOT auth.isCodexAuth: tightening to a live
47
+ // credential here would change which options a fresh install sees, and
48
+ // the options list is a suggestion surface, not the write gate.
49
+ isActive: (_auth, config) => listOpenAiForwardSidecarCandidates(config).length > 0,
50
+ candidateMatch: candidate => candidate.native === true || candidate.provider === "openai",
51
+ baseline: "gpt-5.6-luna",
52
+ rank: 0,
53
+ },
54
+ {
55
+ backend: "anthropic",
56
+ isActive: auth => auth.isAnthropicAuth,
57
+ // The runtime dispatches through exactly ONE Anthropic provider — the
58
+ // resolved OAuth row. Same-adapter keyed rows are unreachable (see
59
+ // visionBackendForCandidate's original stance).
60
+ candidateMatch: (candidate, auth) => candidate.provider === auth.anthropicProviderName,
61
+ baseline: "claude-haiku-4-5",
62
+ rank: 1,
63
+ },
64
+ {
65
+ backend: "routed",
66
+ // Always offered: options only materialize when a matching picker row
67
+ // exists, and the row's own provider config is the liveness signal — the
68
+ // loopback request fails closed through ordinary routing errors.
69
+ isActive: () => true,
70
+ // Any row the other two executors do NOT own. Auth-slot rows are
71
+ // entitlements of the openai/anthropic sides and never route here.
72
+ candidateMatch: (candidate, auth) =>
73
+ candidate.native !== true
74
+ && candidate.provider !== "openai"
75
+ && candidate.provider !== auth.anthropicProviderName,
76
+ rank: 2,
77
+ },
78
+ ];
79
+
80
+ export function visionBackendDescriptor(backend: VisionSidecarBackend): VisionBackendDescriptor {
81
+ const descriptor = VISION_BACKENDS.find(entry => entry.backend === backend);
82
+ if (!descriptor) throw new Error(`unknown vision backend "${backend}"`);
83
+ return descriptor;
84
+ }
85
+
86
+ /**
87
+ * The active backend set for option generation. Falls back to the two
88
+ * UNIVERSAL sides when neither is active (fresh install: picker stays
89
+ * populated, permissive-unknown rule); "routed" is active by construction.
90
+ */
91
+ export function activeVisionBackends(auth: SidecarAuthState, config: OcxConfig): VisionSidecarBackend[] {
92
+ const active = VISION_BACKENDS.filter(entry => entry.isActive(auth, config)).map(entry => entry.backend);
93
+ return active.includes("openai") || active.includes("anthropic")
94
+ ? active
95
+ : ["openai", "anthropic", ...active.filter(backend => backend === "routed")];
96
+ }
97
+
@@ -1,4 +1,5 @@
1
1
  import type { OcxProviderConfig } from "../types";
2
+ import type { VisionReasoningEffort } from "../reasoning-effort";
2
3
  import { FORWARD_HEADERS } from "../adapters/openai-responses";
3
4
  import { signalWithTimeout, cancelBodyOnAbort } from "../lib/abort";
4
5
  import { redactSecretString } from "../lib/redact";
@@ -9,6 +10,7 @@ import type { SidecarOutcomeRecorder } from "../web-search/executor";
9
10
 
10
11
  export interface VisionSettings {
11
12
  model: string;
13
+ reasoning: VisionReasoningEffort;
12
14
  timeoutMs: number;
13
15
  }
14
16
 
@@ -77,9 +79,9 @@ export async function describeImage(
77
79
  "verbatim, and note UI/layout, colors, branding/logos, charts, and notable details. Focus on " +
78
80
  "what's relevant to the user's request. Output only the description.",
79
81
  input: [{ type: "message", role: "user", content }],
80
- reasoning: { effort: "low" },
81
- // The ChatGPT (codex) backend rejects `max_output_tokens` ("Unsupported parameter"); the
82
- // description is clamped downstream (DESC_MAX_CHARS) instead.
82
+ reasoning: { effort: settings.reasoning },
83
+ // The ChatGPT (codex) backend rejects `max_output_tokens` ("Unsupported parameter"); the shared
84
+ // SSE parser bounds raw response bytes before DESC_MAX_CHARS applies its display clamp.
83
85
  store: false,
84
86
  stream: true,
85
87
  };
@@ -93,6 +95,10 @@ export async function describeImage(
93
95
  headers,
94
96
  body: JSON.stringify(body),
95
97
  signal: linkedSignal.signal,
98
+ // Credential-bearing: do not follow a cross-origin 3xx. Bun strips `Authorization`
99
+ // across origins but forwards nonstandard headers such as `chatgpt-account-id`,
100
+ // `session_id`, and `x-codex-turn-metadata` to the redirect target.
101
+ redirect: "manual",
96
102
  }),
97
103
  { abortSignal: linkedSignal.signal, label: "vision-sidecar" },
98
104
  );