@yansigit/opencodex 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 (826) hide show
  1. package/AGENTS_INSTALL.md +109 -0
  2. package/LICENSE +21 -0
  3. package/README.md +303 -0
  4. package/assets/architecture.png +0 -0
  5. package/assets/banner.png +0 -0
  6. package/assets/claude-code-models.gif +0 -0
  7. package/assets/codex-app-picker.png +0 -0
  8. package/bin/ocx.mjs +587 -0
  9. package/bin/package-main.mjs +9 -0
  10. package/gui/dist/assets/index-BNJ7r4Gd.js +102 -0
  11. package/gui/dist/assets/index-CGoDO3uO.css +1 -0
  12. package/gui/dist/favicon.png +0 -0
  13. package/gui/dist/icons.svg +24 -0
  14. package/gui/dist/index.html +25 -0
  15. package/gui/dist/logo.png +0 -0
  16. package/gui/dist/provider-icons/alibaba-color.svg +1 -0
  17. package/gui/dist/provider-icons/antigravity-color.svg +1 -0
  18. package/gui/dist/provider-icons/claude-color.svg +1 -0
  19. package/gui/dist/provider-icons/cline-color.svg +16 -0
  20. package/gui/dist/provider-icons/cloudflare-ai-gateway-color.svg +1 -0
  21. package/gui/dist/provider-icons/commandcode-color.svg +1 -0
  22. package/gui/dist/provider-icons/copilot-color.svg +1 -0
  23. package/gui/dist/provider-icons/cursor-color.svg +2 -0
  24. package/gui/dist/provider-icons/deepseek-color.svg +1 -0
  25. package/gui/dist/provider-icons/discord.svg +1 -0
  26. package/gui/dist/provider-icons/firepass-color.svg +1 -0
  27. package/gui/dist/provider-icons/fireworks-color.svg +1 -0
  28. package/gui/dist/provider-icons/gemini-color.svg +1 -0
  29. package/gui/dist/provider-icons/github-copilot-color.svg +1 -0
  30. package/gui/dist/provider-icons/gitlab-duo-color.svg +1 -0
  31. package/gui/dist/provider-icons/grok.svg +1 -0
  32. package/gui/dist/provider-icons/groq-color.svg +1 -0
  33. package/gui/dist/provider-icons/huggingface-color.svg +1 -0
  34. package/gui/dist/provider-icons/kimi-color.svg +1 -0
  35. package/gui/dist/provider-icons/kiro-color.svg +15 -0
  36. package/gui/dist/provider-icons/lm-studio-color.svg +1 -0
  37. package/gui/dist/provider-icons/mistral-color.svg +1 -0
  38. package/gui/dist/provider-icons/moonshot-color.svg +1 -0
  39. package/gui/dist/provider-icons/nvidia-color.svg +1 -0
  40. package/gui/dist/provider-icons/ollama-color.svg +1 -0
  41. package/gui/dist/provider-icons/openai.svg +1 -0
  42. package/gui/dist/provider-icons/opencode.svg +2 -0
  43. package/gui/dist/provider-icons/openrouter-color.svg +1 -0
  44. package/gui/dist/provider-icons/pi.svg +21 -0
  45. package/gui/dist/provider-icons/qianfan-color.svg +1 -0
  46. package/gui/dist/provider-icons/qwen-portal-color.svg +1 -0
  47. package/gui/dist/provider-icons/telegram.svg +1 -0
  48. package/gui/dist/provider-icons/vercel-ai-gateway-color.svg +1 -0
  49. package/gui/dist/provider-icons/vllm-color.svg +1 -0
  50. package/gui/dist/provider-icons/xiaomi-color.svg +1 -0
  51. package/package.json +108 -0
  52. package/src/AGENTS.md +28 -0
  53. package/src/adapters/anthropic-image-guard.ts +251 -0
  54. package/src/adapters/anthropic-image-normalize.ts +518 -0
  55. package/src/adapters/anthropic-output-schema.ts +137 -0
  56. package/src/adapters/anthropic.ts +1327 -0
  57. package/src/adapters/azure.ts +36 -0
  58. package/src/adapters/base.ts +121 -0
  59. package/src/adapters/client-fingerprint.ts +65 -0
  60. package/src/adapters/cline-pass-deepseek-v4-tool-replay.ts +69 -0
  61. package/src/adapters/command-code.ts +601 -0
  62. package/src/adapters/cursor/arg-codec.ts +38 -0
  63. package/src/adapters/cursor/arg-normalize.ts +104 -0
  64. package/src/adapters/cursor/checkpoint-store.ts +303 -0
  65. package/src/adapters/cursor/cursor-errors.ts +288 -0
  66. package/src/adapters/cursor/discovery.ts +333 -0
  67. package/src/adapters/cursor/effort-map.ts +151 -0
  68. package/src/adapters/cursor/exec-policy.ts +88 -0
  69. package/src/adapters/cursor/framing.ts +250 -0
  70. package/src/adapters/cursor/gen/agent_pb.ts +15274 -0
  71. package/src/adapters/cursor/h2-pool.ts +123 -0
  72. package/src/adapters/cursor/http1-bidi.ts +361 -0
  73. package/src/adapters/cursor/images.ts +704 -0
  74. package/src/adapters/cursor/kv-store.ts +52 -0
  75. package/src/adapters/cursor/live-models.ts +269 -0
  76. package/src/adapters/cursor/live-smoke-gate.ts +41 -0
  77. package/src/adapters/cursor/live-transport.ts +1653 -0
  78. package/src/adapters/cursor/mcp-config.ts +42 -0
  79. package/src/adapters/cursor/mcp-manager.ts +333 -0
  80. package/src/adapters/cursor/message-mapper.ts +49 -0
  81. package/src/adapters/cursor/native-exec-common.ts +76 -0
  82. package/src/adapters/cursor/native-exec-desktop.ts +184 -0
  83. package/src/adapters/cursor/native-exec-fs.ts +332 -0
  84. package/src/adapters/cursor/native-exec-mcp.ts +153 -0
  85. package/src/adapters/cursor/native-exec-network.ts +43 -0
  86. package/src/adapters/cursor/native-exec-shell.ts +547 -0
  87. package/src/adapters/cursor/native-exec-tools.ts +118 -0
  88. package/src/adapters/cursor/native-exec.ts +663 -0
  89. package/src/adapters/cursor/protobuf-events.ts +1381 -0
  90. package/src/adapters/cursor/protobuf-request.ts +1032 -0
  91. package/src/adapters/cursor/request-builder.ts +461 -0
  92. package/src/adapters/cursor/thread-continuity.ts +67 -0
  93. package/src/adapters/cursor/tool-definitions.ts +735 -0
  94. package/src/adapters/cursor/tool-result-normalize.ts +92 -0
  95. package/src/adapters/cursor/transport-retry.ts +132 -0
  96. package/src/adapters/cursor/transport.ts +79 -0
  97. package/src/adapters/cursor/types.ts +79 -0
  98. package/src/adapters/cursor.ts +322 -0
  99. package/src/adapters/google-antigravity-hosts.ts +48 -0
  100. package/src/adapters/google-antigravity-replay.ts +827 -0
  101. package/src/adapters/google-antigravity-tools.ts +105 -0
  102. package/src/adapters/google-antigravity-wire.ts +141 -0
  103. package/src/adapters/google-errors.ts +92 -0
  104. package/src/adapters/google-http.ts +460 -0
  105. package/src/adapters/google-tool-schema.ts +238 -0
  106. package/src/adapters/google-truncation.ts +24 -0
  107. package/src/adapters/google-wire-compiler.ts +232 -0
  108. package/src/adapters/google.ts +1377 -0
  109. package/src/adapters/identity.ts +77 -0
  110. package/src/adapters/image.ts +23 -0
  111. package/src/adapters/kiro-constants.ts +16 -0
  112. package/src/adapters/kiro-errors.ts +208 -0
  113. package/src/adapters/kiro-events.ts +197 -0
  114. package/src/adapters/kiro-images.ts +129 -0
  115. package/src/adapters/kiro-retry.ts +312 -0
  116. package/src/adapters/kiro-thinking.ts +112 -0
  117. package/src/adapters/kiro-tool-fallback.ts +36 -0
  118. package/src/adapters/kiro-tools.ts +224 -0
  119. package/src/adapters/kiro-truncation.ts +33 -0
  120. package/src/adapters/kiro-wire.ts +129 -0
  121. package/src/adapters/kiro.ts +1936 -0
  122. package/src/adapters/mimo-free.ts +280 -0
  123. package/src/adapters/openai-chat-url.ts +11 -0
  124. package/src/adapters/openai-chat.ts +1980 -0
  125. package/src/adapters/openai-responses-url.ts +16 -0
  126. package/src/adapters/openai-responses.ts +1911 -0
  127. package/src/adapters/registry.ts +175 -0
  128. package/src/adapters/responses-tool-schema.ts +67 -0
  129. package/src/adapters/run-turn-queue.ts +114 -0
  130. package/src/adapters/tool-call-id.ts +119 -0
  131. package/src/adapters/tool-catalog-nudge.ts +154 -0
  132. package/src/adapters/upstream-http-error.ts +48 -0
  133. package/src/adapters/xai-web-search.ts +185 -0
  134. package/src/bridge.ts +1986 -0
  135. package/src/chat/inbound.ts +319 -0
  136. package/src/chat/outbound.ts +821 -0
  137. package/src/claude/agents-inject.ts +266 -0
  138. package/src/claude/alias.ts +149 -0
  139. package/src/claude/auth-detect.ts +229 -0
  140. package/src/claude/auth-mode-migration.ts +32 -0
  141. package/src/claude/auth-mode.ts +62 -0
  142. package/src/claude/context-windows.ts +205 -0
  143. package/src/claude/desktop-3p-guard.ts +35 -0
  144. package/src/claude/desktop-3p-paths.ts +84 -0
  145. package/src/claude/desktop-3p.ts +615 -0
  146. package/src/claude/desktop-health.ts +26 -0
  147. package/src/claude/desktop-profile.ts +263 -0
  148. package/src/claude/gateway-cache.ts +107 -0
  149. package/src/claude/inbound-debug.ts +163 -0
  150. package/src/claude/inbound.ts +578 -0
  151. package/src/claude/model-info.ts +174 -0
  152. package/src/claude/outbound.ts +926 -0
  153. package/src/cli/access.ts +108 -0
  154. package/src/cli/account-api.ts +302 -0
  155. package/src/cli/account-auth.ts +250 -0
  156. package/src/cli/account-catalog-refresh.ts +14 -0
  157. package/src/cli/account-extended.ts +737 -0
  158. package/src/cli/account-main.ts +317 -0
  159. package/src/cli/account.ts +299 -0
  160. package/src/cli/agent-driven.ts +70 -0
  161. package/src/cli/agent.ts +290 -0
  162. package/src/cli/catalog-prewarm.ts +27 -0
  163. package/src/cli/claude-agent-startup-sync.ts +73 -0
  164. package/src/cli/claude-desktop.ts +213 -0
  165. package/src/cli/claude.ts +355 -0
  166. package/src/cli/codex-log-guard-doctor.ts +103 -0
  167. package/src/cli/codex-shim-autorestore.ts +47 -0
  168. package/src/cli/codex-shim-readiness.ts +76 -0
  169. package/src/cli/combo.ts +127 -0
  170. package/src/cli/config-command.ts +209 -0
  171. package/src/cli/debug.ts +228 -0
  172. package/src/cli/dispatch.ts +585 -0
  173. package/src/cli/doctor.ts +1202 -0
  174. package/src/cli/ensure-desired-integrations.ts +152 -0
  175. package/src/cli/export-command.ts +213 -0
  176. package/src/cli/help.ts +101 -0
  177. package/src/cli/index.ts +973 -0
  178. package/src/cli/init.ts +211 -0
  179. package/src/cli/integrations.ts +260 -0
  180. package/src/cli/interactive-confirm.ts +133 -0
  181. package/src/cli/lab.ts +607 -0
  182. package/src/cli/launcher-context.ts +77 -0
  183. package/src/cli/minimax.ts +497 -0
  184. package/src/cli/models-runtime.ts +245 -0
  185. package/src/cli/models.ts +422 -0
  186. package/src/cli/observe.ts +206 -0
  187. package/src/cli/opencode.ts +588 -0
  188. package/src/cli/provider-replit.ts +232 -0
  189. package/src/cli/provider-runtime.ts +179 -0
  190. package/src/cli/provider.ts +492 -0
  191. package/src/cli/ready.ts +301 -0
  192. package/src/cli/registry.ts +422 -0
  193. package/src/cli/replit-gateway-key-input.ts +138 -0
  194. package/src/cli/root.ts +86 -0
  195. package/src/cli/route-policy.ts +92 -0
  196. package/src/cli/runtime-api.ts +328 -0
  197. package/src/cli/star-prompt.ts +211 -0
  198. package/src/cli/status-oauth.ts +78 -0
  199. package/src/cli/status.ts +328 -0
  200. package/src/cli/system-command.ts +112 -0
  201. package/src/cli/system-restart-client.ts +146 -0
  202. package/src/cli/tray-proxy.ts +199 -0
  203. package/src/cli/v2.ts +268 -0
  204. package/src/cli.ts +10 -0
  205. package/src/clients/config-export.ts +1704 -0
  206. package/src/codex/account-id.ts +34 -0
  207. package/src/codex/account-label.ts +47 -0
  208. package/src/codex/account-lifecycle.ts +172 -0
  209. package/src/codex/account-namespace-match.ts +63 -0
  210. package/src/codex/account-namespaces.ts +195 -0
  211. package/src/codex/account-pause.ts +20 -0
  212. package/src/codex/account-priority.ts +83 -0
  213. package/src/codex/account-runtime-state.ts +31 -0
  214. package/src/codex/account-store.ts +544 -0
  215. package/src/codex/account-usability.ts +43 -0
  216. package/src/codex/admission.ts +256 -0
  217. package/src/codex/affinity-debug.ts +162 -0
  218. package/src/codex/agent-roles-sync.ts +225 -0
  219. package/src/codex/agent-roles.ts +238 -0
  220. package/src/codex/app-server-processes.ts +1143 -0
  221. package/src/codex/app-server-restart-service.ts +232 -0
  222. package/src/codex/auth-api.ts +2147 -0
  223. package/src/codex/auth-collision.ts +109 -0
  224. package/src/codex/auth-context.ts +665 -0
  225. package/src/codex/autostart-health.ts +156 -0
  226. package/src/codex/catalog/account-models.ts +67 -0
  227. package/src/codex/catalog/aggregation.ts +436 -0
  228. package/src/codex/catalog/bundled.ts +549 -0
  229. package/src/codex/catalog/effort.ts +446 -0
  230. package/src/codex/catalog/filesystem-evidence.ts +302 -0
  231. package/src/codex/catalog/kinds.ts +2 -0
  232. package/src/codex/catalog/metadata.ts +664 -0
  233. package/src/codex/catalog/native-models.ts +72 -0
  234. package/src/codex/catalog/parsing.ts +650 -0
  235. package/src/codex/catalog/provider-fetch.ts +2064 -0
  236. package/src/codex/catalog/sync.ts +1883 -0
  237. package/src/codex/catalog-admission.ts +199 -0
  238. package/src/codex/catalog-refresh-status.ts +105 -0
  239. package/src/codex/catalog-write-serialization.ts +242 -0
  240. package/src/codex/catalog.ts +14 -0
  241. package/src/codex/codex-write-lock.ts +384 -0
  242. package/src/codex/convergence-types.ts +614 -0
  243. package/src/codex/convergence.ts +651 -0
  244. package/src/codex/coordinator-doctor.ts +332 -0
  245. package/src/codex/custom-model-catalog-migration.ts +176 -0
  246. package/src/codex/data/upstream-models.json +830 -0
  247. package/src/codex/desired-state.ts +230 -0
  248. package/src/codex/exec-invocation.ts +22 -0
  249. package/src/codex/features.ts +1566 -0
  250. package/src/codex/generation.ts +202 -0
  251. package/src/codex/history-job.ts +407 -0
  252. package/src/codex/history-lock.ts +242 -0
  253. package/src/codex/history-migration-guardian.ts +108 -0
  254. package/src/codex/history-provider.ts +979 -0
  255. package/src/codex/history-transition.ts +105 -0
  256. package/src/codex/history-worker.ts +220 -0
  257. package/src/codex/home.ts +206 -0
  258. package/src/codex/inject-coordination.ts +290 -0
  259. package/src/codex/inject.ts +1733 -0
  260. package/src/codex/injected-marker.ts +106 -0
  261. package/src/codex/integration-record.ts +266 -0
  262. package/src/codex/internal/catalog-writer.ts +203 -0
  263. package/src/codex/internal/history-writer.ts +80 -0
  264. package/src/codex/journal.ts +225 -0
  265. package/src/codex/log-guard/inspect.ts +506 -0
  266. package/src/codex/log-guard/lock.ts +150 -0
  267. package/src/codex/log-guard/maintenance.ts +403 -0
  268. package/src/codex/log-guard/path-safety.ts +88 -0
  269. package/src/codex/log-guard/policy.ts +44 -0
  270. package/src/codex/log-guard/processes.ts +205 -0
  271. package/src/codex/log-guard/protection.ts +489 -0
  272. package/src/codex/log-guard/sqlite-errors.ts +9 -0
  273. package/src/codex/main-account-cache.ts +56 -0
  274. package/src/codex/main-account.ts +68 -0
  275. package/src/codex/management-convergence.ts +167 -0
  276. package/src/codex/model-cache.ts +273 -0
  277. package/src/codex/model-entitlements.ts +353 -0
  278. package/src/codex/native-main-admission.ts +47 -0
  279. package/src/codex/native-main-auth-temp.ts +187 -0
  280. package/src/codex/native-main-claim.ts +178 -0
  281. package/src/codex/native-main-lock-file.ts +162 -0
  282. package/src/codex/native-main-owner.ts +329 -0
  283. package/src/codex/native-profile-api.ts +247 -0
  284. package/src/codex/native-profile-manager.ts +1531 -0
  285. package/src/codex/native-profile-processes.ts +121 -0
  286. package/src/codex/native-profile-recovery.ts +99 -0
  287. package/src/codex/native-profile-stage-store.ts +387 -0
  288. package/src/codex/native-profile-startup.ts +492 -0
  289. package/src/codex/native-profile-store.ts +855 -0
  290. package/src/codex/native-profile-types.ts +120 -0
  291. package/src/codex/native-residue.ts +682 -0
  292. package/src/codex/paths.ts +144 -0
  293. package/src/codex/plan-from-token.ts +140 -0
  294. package/src/codex/plan.ts +40 -0
  295. package/src/codex/plugins-doctor.ts +242 -0
  296. package/src/codex/pool-rotation.ts +295 -0
  297. package/src/codex/project-config-warnings.ts +425 -0
  298. package/src/codex/prompt-journal.ts +352 -0
  299. package/src/codex/prompt-layers.ts +967 -0
  300. package/src/codex/prompt-lock.ts +143 -0
  301. package/src/codex/quota-rejection.ts +298 -0
  302. package/src/codex/quota.ts +573 -0
  303. package/src/codex/refresh.ts +62 -0
  304. package/src/codex/reset-credit-recovery.ts +1044 -0
  305. package/src/codex/routing.ts +1888 -0
  306. package/src/codex/runtime.ts +659 -0
  307. package/src/codex/shim.ts +2170 -0
  308. package/src/codex/subagent-defaults.ts +550 -0
  309. package/src/codex/subagent-model-fallback.ts +784 -0
  310. package/src/codex/sync.ts +319 -0
  311. package/src/codex/transition-state.ts +612 -0
  312. package/src/codex/upstream-host-health.ts +368 -0
  313. package/src/codex/user-identity.ts +557 -0
  314. package/src/codex/warmup.ts +298 -0
  315. package/src/codex/websocket-registry.ts +100 -0
  316. package/src/codex/write-coordination.ts +114 -0
  317. package/src/combos/failover.ts +160 -0
  318. package/src/combos/index.ts +45 -0
  319. package/src/combos/request.ts +94 -0
  320. package/src/combos/resolve.ts +232 -0
  321. package/src/combos/types.ts +398 -0
  322. package/src/config/provider-name.ts +24 -0
  323. package/src/config.ts +4041 -0
  324. package/src/fork/register.ts +3 -0
  325. package/src/generated/compatibility-version.json +3116 -0
  326. package/src/generated/model-metadata.ts +106 -0
  327. package/src/github/star-state.ts +203 -0
  328. package/src/grok/inject.ts +530 -0
  329. package/src/grok/inspect.ts +45 -0
  330. package/src/grok/status.ts +121 -0
  331. package/src/grok/sync.ts +66 -0
  332. package/src/images/artifacts.ts +516 -0
  333. package/src/images/fulfill-video.ts +163 -0
  334. package/src/images/fulfill.ts +149 -0
  335. package/src/images/index.ts +4 -0
  336. package/src/images/loop.ts +955 -0
  337. package/src/images/plan.ts +143 -0
  338. package/src/images/synthetic-tool.ts +133 -0
  339. package/src/images/types.ts +41 -0
  340. package/src/images/xai-client.ts +141 -0
  341. package/src/images/xai-video-client.ts +163 -0
  342. package/src/index.ts +22 -0
  343. package/src/integrations/config-io.ts +269 -0
  344. package/src/integrations/journal.ts +315 -0
  345. package/src/integrations/merge.ts +135 -0
  346. package/src/integrations/mutation-flight.ts +71 -0
  347. package/src/integrations/native/ownership-preflight.ts +202 -0
  348. package/src/integrations/omp-yaml-source.ts +358 -0
  349. package/src/integrations/owned-refresh.ts +74 -0
  350. package/src/integrations/ownership.ts +111 -0
  351. package/src/integrations/registry.ts +159 -0
  352. package/src/integrations/serialize.ts +314 -0
  353. package/src/integrations/state.ts +361 -0
  354. package/src/integrations/store.ts +103 -0
  355. package/src/integrations/writer-lock.ts +98 -0
  356. package/src/integrations/writer.ts +691 -0
  357. package/src/lab/artifacts/sanitize.ts +586 -0
  358. package/src/lab/artifacts/secure-fs.ts +475 -0
  359. package/src/lab/artifacts/store.ts +310 -0
  360. package/src/lab/automation/budgets.ts +78 -0
  361. package/src/lab/automation/config-persistence.ts +256 -0
  362. package/src/lab/automation/constants.ts +39 -0
  363. package/src/lab/automation/cooldown.ts +103 -0
  364. package/src/lab/automation/dispatch.ts +211 -0
  365. package/src/lab/automation/index.ts +13 -0
  366. package/src/lab/automation/orchestrator.ts +499 -0
  367. package/src/lab/automation/persistence.ts +512 -0
  368. package/src/lab/automation/planner.ts +371 -0
  369. package/src/lab/automation/policy.ts +136 -0
  370. package/src/lab/automation/queue.ts +191 -0
  371. package/src/lab/automation/recovery.ts +24 -0
  372. package/src/lab/automation/route-context.ts +21 -0
  373. package/src/lab/automation/run-key.ts +44 -0
  374. package/src/lab/automation/runs-query.ts +34 -0
  375. package/src/lab/automation/types.ts +160 -0
  376. package/src/lab/conformance/assertion.ts +325 -0
  377. package/src/lab/conformance/digest.ts +22 -0
  378. package/src/lab/conformance/executor.ts +741 -0
  379. package/src/lab/conformance/fixture-provider.ts +27 -0
  380. package/src/lab/conformance/fixtures/live-v1-cases.json +175 -0
  381. package/src/lab/conformance/fixtures/protocol-v1-cases.json +461 -0
  382. package/src/lab/conformance/harness-budget.ts +47 -0
  383. package/src/lab/conformance/index.ts +5 -0
  384. package/src/lab/conformance/jcs.ts +64 -0
  385. package/src/lab/conformance/json-pointer.ts +39 -0
  386. package/src/lab/conformance/manifest.ts +180 -0
  387. package/src/lab/conformance/mcp-stub.ts +179 -0
  388. package/src/lab/conformance/negative-controls.ts +164 -0
  389. package/src/lab/conformance/observation.ts +355 -0
  390. package/src/lab/conformance/runner.ts +68 -0
  391. package/src/lab/conformance/sse-normalize.ts +59 -0
  392. package/src/lab/conformance/suite-manifest.ts +78 -0
  393. package/src/lab/conformance/types.ts +214 -0
  394. package/src/lab/constants.ts +126 -0
  395. package/src/lab/digest.ts +64 -0
  396. package/src/lab/events/errors.ts +9 -0
  397. package/src/lab/events/limits.ts +117 -0
  398. package/src/lab/events/types.ts +229 -0
  399. package/src/lab/events/validate.ts +781 -0
  400. package/src/lab/fabric/constants.ts +40 -0
  401. package/src/lab/fabric/executor.ts +492 -0
  402. package/src/lab/fabric/index.ts +80 -0
  403. package/src/lab/fabric/manifest.ts +222 -0
  404. package/src/lab/fabric/observe.ts +489 -0
  405. package/src/lab/fabric/patch.ts +79 -0
  406. package/src/lab/fabric/producer-child.ts +139 -0
  407. package/src/lab/fabric/producer-isolate.ts +276 -0
  408. package/src/lab/fabric/producer-protocol.ts +61 -0
  409. package/src/lab/fabric/scratch.ts +439 -0
  410. package/src/lab/fabric/subject.ts +106 -0
  411. package/src/lab/fabric/types.ts +134 -0
  412. package/src/lab/fabric/verifier.ts +98 -0
  413. package/src/lab/index.ts +54 -0
  414. package/src/lab/ledger/artifact-refs.ts +127 -0
  415. package/src/lab/ledger/invalidation.ts +136 -0
  416. package/src/lab/ledger/purge.ts +310 -0
  417. package/src/lab/ledger/store.ts +532 -0
  418. package/src/lab/live/credential-lease.ts +53 -0
  419. package/src/lab/live/destination.ts +155 -0
  420. package/src/lab/live/executor.ts +336 -0
  421. package/src/lab/live/inert-tools.ts +56 -0
  422. package/src/lab/live/manifest.ts +85 -0
  423. package/src/lab/live/mcp-loopback.ts +57 -0
  424. package/src/lab/live/runner.ts +19 -0
  425. package/src/lab/live/sandbox.ts +61 -0
  426. package/src/lab/live/suite-manifest.ts +41 -0
  427. package/src/lab/live/transport.ts +118 -0
  428. package/src/lab/live/types.ts +197 -0
  429. package/src/lab/observe/from-conformance.ts +301 -0
  430. package/src/lab/observe/from-live.ts +117 -0
  431. package/src/lab/paths.ts +153 -0
  432. package/src/lab/projection/rebuild.ts +495 -0
  433. package/src/lab/projection/schema.ts +135 -0
  434. package/src/lab/projection/verdicts.ts +474 -0
  435. package/src/lab/projection/verification.ts +412 -0
  436. package/src/lab/public/bundle.ts +217 -0
  437. package/src/lab/public/community-authority.ts +175 -0
  438. package/src/lab/public/community-files.ts +29 -0
  439. package/src/lab/public/community.ts +479 -0
  440. package/src/lab/public/file-safety.ts +155 -0
  441. package/src/lab/public/ids.ts +26 -0
  442. package/src/lab/public/index.ts +16 -0
  443. package/src/lab/public/mutation-lock.ts +424 -0
  444. package/src/lab/public/operator.ts +353 -0
  445. package/src/lab/public/origin-purge.ts +79 -0
  446. package/src/lab/public/origin.ts +203 -0
  447. package/src/lab/public/privacy.ts +143 -0
  448. package/src/lab/public/private-file.ts +261 -0
  449. package/src/lab/public/project.ts +124 -0
  450. package/src/lab/public/purge-test-fault.ts +21 -0
  451. package/src/lab/public/purge.ts +223 -0
  452. package/src/lab/public/registry.ts +44 -0
  453. package/src/lab/public/revocation.ts +252 -0
  454. package/src/lab/public/signature.ts +243 -0
  455. package/src/lab/public/storage.ts +105 -0
  456. package/src/lab/public/strict-json.ts +206 -0
  457. package/src/lab/public/time.ts +26 -0
  458. package/src/lab/public/types.ts +172 -0
  459. package/src/lab/public/validate.ts +391 -0
  460. package/src/lab/query/catalog.ts +101 -0
  461. package/src/lab/query/connection.ts +107 -0
  462. package/src/lab/query/constants.ts +4 -0
  463. package/src/lab/query/cursor.ts +132 -0
  464. package/src/lab/query/dto-map.ts +277 -0
  465. package/src/lab/query/errors.ts +22 -0
  466. package/src/lab/query/freshness.ts +53 -0
  467. package/src/lab/query/index.ts +45 -0
  468. package/src/lab/query/latest-observation.ts +59 -0
  469. package/src/lab/query/passive-production.ts +159 -0
  470. package/src/lab/query/queries.ts +444 -0
  471. package/src/lab/query/types.ts +266 -0
  472. package/src/lab/subject/behavior-fingerprint.ts +77 -0
  473. package/src/lab/subject/installation-salt.ts +112 -0
  474. package/src/lab/subject/protocol-subject.ts +80 -0
  475. package/src/lab/subject/route-subject.ts +74 -0
  476. package/src/lib/abort.ts +146 -0
  477. package/src/lib/admin-secrets.ts +25 -0
  478. package/src/lib/admission.ts +83 -0
  479. package/src/lib/app-owned-memory-stores.ts +195 -0
  480. package/src/lib/app-owned-memory.ts +265 -0
  481. package/src/lib/bounded-body.ts +346 -0
  482. package/src/lib/bun-binary-validator.d.mts +3 -0
  483. package/src/lib/bun-binary-validator.mjs +18 -0
  484. package/src/lib/bun-runtime.ts +184 -0
  485. package/src/lib/bun-stream-caps.ts +130 -0
  486. package/src/lib/codex-restart-contract.ts +120 -0
  487. package/src/lib/config-ownership.ts +364 -0
  488. package/src/lib/crash-guard.ts +344 -0
  489. package/src/lib/debug-log-buffer.ts +83 -0
  490. package/src/lib/debug-settings.ts +108 -0
  491. package/src/lib/debug.ts +31 -0
  492. package/src/lib/destination-policy.ts +380 -0
  493. package/src/lib/errors.ts +406 -0
  494. package/src/lib/eventstream-decoder.ts +253 -0
  495. package/src/lib/fabric-task-execution-authority.ts +7 -0
  496. package/src/lib/fabric-task-host.ts +29 -0
  497. package/src/lib/gcp-adc.ts +341 -0
  498. package/src/lib/injection-debug-log.ts +58 -0
  499. package/src/lib/lab-activation.ts +223 -0
  500. package/src/lib/lab-live-execution-authority.ts +13 -0
  501. package/src/lib/lab-live-host.ts +30 -0
  502. package/src/lib/lab-live-pinned-sender.ts +56 -0
  503. package/src/lib/lab-live-route-production.ts +130 -0
  504. package/src/lib/lab-passive-linker-registration.ts +26 -0
  505. package/src/lib/local-management-attestation.ts +51 -0
  506. package/src/lib/local-management-capability.ts +100 -0
  507. package/src/lib/local-provider-reload-contract.ts +100 -0
  508. package/src/lib/open-url.ts +25 -0
  509. package/src/lib/optional-shutdown-hooks.ts +57 -0
  510. package/src/lib/pinned-http.ts +270 -0
  511. package/src/lib/privacy.ts +20 -0
  512. package/src/lib/process-control.ts +168 -0
  513. package/src/lib/provider-outbound.ts +210 -0
  514. package/src/lib/provider-url.ts +14 -0
  515. package/src/lib/proxy-env.ts +18 -0
  516. package/src/lib/redact.ts +521 -0
  517. package/src/lib/retry-after.ts +55 -0
  518. package/src/lib/self-launch-argv.ts +15 -0
  519. package/src/lib/server-resource-ownership.ts +71 -0
  520. package/src/lib/service-secrets.ts +25 -0
  521. package/src/lib/shadow-call.ts +61 -0
  522. package/src/lib/sidecar-tracker.ts +52 -0
  523. package/src/lib/sse-decoder.ts +364 -0
  524. package/src/lib/state-store-registrations.ts +119 -0
  525. package/src/lib/state-store-sweeper.ts +184 -0
  526. package/src/lib/system-restart-contract.ts +73 -0
  527. package/src/lib/test-home-guard.ts +90 -0
  528. package/src/lib/token-estimate.ts +86 -0
  529. package/src/lib/tool-argument-integers.ts +202 -0
  530. package/src/lib/translator-budget.ts +400 -0
  531. package/src/lib/upstream-http-version.ts +57 -0
  532. package/src/lib/upstream-reachability.ts +95 -0
  533. package/src/lib/upstream-retry.ts +392 -0
  534. package/src/lib/win-exec.ts +115 -0
  535. package/src/lib/win-paths.ts +68 -0
  536. package/src/lib/windows-atomic-replace.ts +156 -0
  537. package/src/lib/windows-elevation.ts +773 -0
  538. package/src/lib/windows-secret-acl.ts +854 -0
  539. package/src/lib/windows-service-wrappers.ts +72 -0
  540. package/src/lib/windows-text.ts +106 -0
  541. package/src/lib/windows-user-principal.ts +341 -0
  542. package/src/lib/winsw.ts +403 -0
  543. package/src/oauth/account-import/google-antigravity-adapter.ts +74 -0
  544. package/src/oauth/account-import/index.ts +15 -0
  545. package/src/oauth/account-import/parser.ts +83 -0
  546. package/src/oauth/account-import/registry.ts +18 -0
  547. package/src/oauth/account-import/service.ts +75 -0
  548. package/src/oauth/account-import/types.ts +91 -0
  549. package/src/oauth/anthropic-routing.ts +594 -0
  550. package/src/oauth/anthropic.ts +188 -0
  551. package/src/oauth/antigravity-routing.ts +151 -0
  552. package/src/oauth/callback-server.ts +300 -0
  553. package/src/oauth/chatgpt.ts +161 -0
  554. package/src/oauth/command-code.ts +239 -0
  555. package/src/oauth/cursor.ts +252 -0
  556. package/src/oauth/github-copilot.ts +428 -0
  557. package/src/oauth/google-antigravity.ts +262 -0
  558. package/src/oauth/health.ts +407 -0
  559. package/src/oauth/index.ts +1504 -0
  560. package/src/oauth/key-providers.ts +124 -0
  561. package/src/oauth/kimi.ts +227 -0
  562. package/src/oauth/kiro-credentials.ts +726 -0
  563. package/src/oauth/kiro.ts +621 -0
  564. package/src/oauth/local-token-detect.ts +130 -0
  565. package/src/oauth/log.ts +50 -0
  566. package/src/oauth/login-cli.ts +223 -0
  567. package/src/oauth/nous.ts +798 -0
  568. package/src/oauth/pkce.ts +15 -0
  569. package/src/oauth/store.ts +728 -0
  570. package/src/oauth/token-guardian.ts +309 -0
  571. package/src/oauth/types.ts +62 -0
  572. package/src/oauth/xai.ts +241 -0
  573. package/src/providers/alibaba-region-backup.ts +75 -0
  574. package/src/providers/alibaba-region-migration.ts +156 -0
  575. package/src/providers/alibaba-region-startup.ts +36 -0
  576. package/src/providers/antigravity-models.ts +695 -0
  577. package/src/providers/antigravity-quota.ts +216 -0
  578. package/src/providers/api-keys.ts +140 -0
  579. package/src/providers/base-url-choices.ts +74 -0
  580. package/src/providers/codex-capacity.ts +292 -0
  581. package/src/providers/command-code-efforts.ts +144 -0
  582. package/src/providers/context-cap.ts +82 -0
  583. package/src/providers/cursor-pool.ts +72 -0
  584. package/src/providers/derive.ts +586 -0
  585. package/src/providers/fastwire.ts +501 -0
  586. package/src/providers/free-directory.ts +187 -0
  587. package/src/providers/github-copilot-transport.ts +56 -0
  588. package/src/providers/google-vertex-location.ts +14 -0
  589. package/src/providers/key-failover.ts +271 -0
  590. package/src/providers/kiro-models.ts +67 -0
  591. package/src/providers/label.ts +19 -0
  592. package/src/providers/model-discovery-limits.ts +16 -0
  593. package/src/providers/model-discovery.ts +449 -0
  594. package/src/providers/model-rename-migration.ts +255 -0
  595. package/src/providers/model-rename-startup.ts +28 -0
  596. package/src/providers/openai-sidecar.ts +243 -0
  597. package/src/providers/openai-tier-startup.ts +56 -0
  598. package/src/providers/openai-tiers.ts +423 -0
  599. package/src/providers/openai-virtual-models.ts +83 -0
  600. package/src/providers/opencode-zen-rate-limit.ts +102 -0
  601. package/src/providers/openrouter-routing.ts +102 -0
  602. package/src/providers/provider-id-rewrite.ts +185 -0
  603. package/src/providers/quota.ts +2345 -0
  604. package/src/providers/registry.ts +2918 -0
  605. package/src/providers/replit/constants.ts +27 -0
  606. package/src/providers/replit/derive.ts +85 -0
  607. package/src/providers/replit/headers.ts +28 -0
  608. package/src/providers/replit/origin.ts +55 -0
  609. package/src/providers/replit/pair-install-response.ts +72 -0
  610. package/src/providers/replit/probe.ts +199 -0
  611. package/src/providers/replit/setup.ts +350 -0
  612. package/src/providers/request-pacing.ts +310 -0
  613. package/src/providers/service-tier.ts +277 -0
  614. package/src/providers/slug-codec.ts +103 -0
  615. package/src/providers/static-model-discovery.ts +86 -0
  616. package/src/providers/xai-responses-opt-in.ts +15 -0
  617. package/src/providers/xai-transport.ts +148 -0
  618. package/src/reasoning-effort.ts +183 -0
  619. package/src/responses/compaction.ts +142 -0
  620. package/src/responses/custom-tool-compat.ts +266 -0
  621. package/src/responses/hosted-tool-policy.ts +9 -0
  622. package/src/responses/namespace-tool-compat.ts +355 -0
  623. package/src/responses/parser.ts +838 -0
  624. package/src/responses/provider-continuation.ts +98 -0
  625. package/src/responses/provider-opaque-metadata.ts +73 -0
  626. package/src/responses/reasoning-envelope.ts +60 -0
  627. package/src/responses/reasoning-replay-cache.ts +426 -0
  628. package/src/responses/schema.ts +165 -0
  629. package/src/responses/spill-store.ts +459 -0
  630. package/src/responses/state.ts +1433 -0
  631. package/src/responses/thought-signature-replay.ts +347 -0
  632. package/src/responses/tool-groups.ts +19 -0
  633. package/src/responses/tool-search-compat.ts +301 -0
  634. package/src/responses/truncated-stop-reason.ts +60 -0
  635. package/src/router.ts +761 -0
  636. package/src/routing/analytics.ts +378 -0
  637. package/src/routing/capability.ts +244 -0
  638. package/src/routing/compatibility/assemble.ts +73 -0
  639. package/src/routing/compatibility/behavior.ts +278 -0
  640. package/src/routing/compatibility/catalog.ts +99 -0
  641. package/src/routing/compatibility/endpoint.ts +52 -0
  642. package/src/routing/compatibility/lab-evidence-provider.ts +130 -0
  643. package/src/routing/compatibility/policy.ts +181 -0
  644. package/src/routing/compatibility/provider-slot.ts +56 -0
  645. package/src/routing/compatibility/reader.ts +110 -0
  646. package/src/routing/compatibility/subject.ts +191 -0
  647. package/src/routing/compatibility/types.ts +64 -0
  648. package/src/routing/compatibility/version.ts +104 -0
  649. package/src/routing/cost.ts +77 -0
  650. package/src/routing/evaluator.ts +495 -0
  651. package/src/routing/health.ts +412 -0
  652. package/src/routing/history/cursor.ts +43 -0
  653. package/src/routing/history/indexer.ts +605 -0
  654. package/src/routing/history/schema.ts +72 -0
  655. package/src/routing/profile-namespace.ts +15 -0
  656. package/src/routing/profile.ts +547 -0
  657. package/src/routing/quota.ts +145 -0
  658. package/src/routing/request-evidence.ts +45 -0
  659. package/src/routing/trace.ts +776 -0
  660. package/src/server/adapter-resolve.ts +53 -0
  661. package/src/server/auth-cors.ts +751 -0
  662. package/src/server/background-lifecycle.ts +182 -0
  663. package/src/server/chat-completions.ts +442 -0
  664. package/src/server/chat-native-sse.ts +331 -0
  665. package/src/server/chat-native.ts +426 -0
  666. package/src/server/claude-messages.ts +1030 -0
  667. package/src/server/direct-local-http.ts +347 -0
  668. package/src/server/effort-policy.ts +190 -0
  669. package/src/server/github-copilot-responses-repair.ts +338 -0
  670. package/src/server/gui-static.ts +152 -0
  671. package/src/server/image-retry.ts +42 -0
  672. package/src/server/images.ts +568 -0
  673. package/src/server/index.ts +1813 -0
  674. package/src/server/lifecycle.ts +498 -0
  675. package/src/server/live.ts +717 -0
  676. package/src/server/local-management-read-client.ts +90 -0
  677. package/src/server/local-provider-reload-client.ts +137 -0
  678. package/src/server/management/agent-settings-routes.ts +1433 -0
  679. package/src/server/management/api-access.ts +141 -0
  680. package/src/server/management/api-key-usage.ts +193 -0
  681. package/src/server/management/body.ts +41 -0
  682. package/src/server/management/combo-routes.ts +263 -0
  683. package/src/server/management/config-routes.ts +835 -0
  684. package/src/server/management/context.ts +113 -0
  685. package/src/server/management/integration-routes.ts +498 -0
  686. package/src/server/management/lab-automation-routes.ts +206 -0
  687. package/src/server/management/lab-routes.ts +563 -0
  688. package/src/server/management/logs-usage-routes.ts +586 -0
  689. package/src/server/management/model-routes.ts +560 -0
  690. package/src/server/management/model-rows.ts +163 -0
  691. package/src/server/management/native-integration-routes.ts +769 -0
  692. package/src/server/management/oauth-account-routes.ts +637 -0
  693. package/src/server/management/provider-capability-config.ts +48 -0
  694. package/src/server/management/provider-routes.ts +1033 -0
  695. package/src/server/management/replit-provider-routes.ts +86 -0
  696. package/src/server/management/request-history-routes.ts +191 -0
  697. package/src/server/management/routing-analytics-routes.ts +74 -0
  698. package/src/server/management/routing-profile-routes.ts +385 -0
  699. package/src/server/management/shared.ts +286 -0
  700. package/src/server/management/sidebar-routes.ts +106 -0
  701. package/src/server/management/storage-log-guard-routes.ts +186 -0
  702. package/src/server/management/sync-response.ts +69 -0
  703. package/src/server/management/system-restart.ts +435 -0
  704. package/src/server/management/system-routes.ts +194 -0
  705. package/src/server/management/usage-summary-cache.ts +94 -0
  706. package/src/server/management/vision-sidecar-options.ts +167 -0
  707. package/src/server/management/web-search-sidecar-options.ts +120 -0
  708. package/src/server/management-api.ts +314 -0
  709. package/src/server/management-auth.ts +482 -0
  710. package/src/server/memory-watchdog.ts +156 -0
  711. package/src/server/passive-route-linker.ts +66 -0
  712. package/src/server/port-reclaim.ts +307 -0
  713. package/src/server/ports.ts +156 -0
  714. package/src/server/proxy-liveness.ts +328 -0
  715. package/src/server/readiness.ts +99 -0
  716. package/src/server/relay-eager.ts +353 -0
  717. package/src/server/relay.ts +1209 -0
  718. package/src/server/request-decompress.ts +239 -0
  719. package/src/server/request-log-conversation.ts +168 -0
  720. package/src/server/request-log.ts +1259 -0
  721. package/src/server/responses/agent-task-recovery-cache.ts +143 -0
  722. package/src/server/responses/agent-task-recovery.ts +465 -0
  723. package/src/server/responses/collaboration.ts +551 -0
  724. package/src/server/responses/compact.ts +771 -0
  725. package/src/server/responses/core.ts +5389 -0
  726. package/src/server/responses/empty-completion-guard.ts +276 -0
  727. package/src/server/responses/encrypted-payload.ts +331 -0
  728. package/src/server/responses/fetch-helpers.ts +232 -0
  729. package/src/server/responses/input-admission.ts +185 -0
  730. package/src/server/responses/pacing-overload.ts +13 -0
  731. package/src/server/responses/passthrough-error.ts +78 -0
  732. package/src/server/responses/policy-fallback.ts +178 -0
  733. package/src/server/responses/responses-field-backfill.ts +251 -0
  734. package/src/server/responses/terminal-guard.ts +251 -0
  735. package/src/server/responses/upstream-error.ts +53 -0
  736. package/src/server/responses/ws-upstream.ts +308 -0
  737. package/src/server/responses-custom-tool-repair.ts +282 -0
  738. package/src/server/responses-image-gen-repair.ts +132 -0
  739. package/src/server/responses-item-id-repair.ts +272 -0
  740. package/src/server/responses-json-events.ts +90 -0
  741. package/src/server/responses-model-rewrite.ts +29 -0
  742. package/src/server/responses-reasoning-summary-rewrite.ts +178 -0
  743. package/src/server/responses-snapshot-repair.ts +621 -0
  744. package/src/server/responses-terminal-repair.ts +342 -0
  745. package/src/server/responses-tool-search-repair.ts +267 -0
  746. package/src/server/responses-undeclared-tool-guard.ts +153 -0
  747. package/src/server/responses.ts +25 -0
  748. package/src/server/search.ts +201 -0
  749. package/src/server/sse-frame-buffer.ts +292 -0
  750. package/src/server/sse-payload-rewrite.ts +263 -0
  751. package/src/server/startup-action-control.ts +315 -0
  752. package/src/server/startup-health-cache.ts +131 -0
  753. package/src/server/system-env.ts +484 -0
  754. package/src/server/windows-tcp-drop.ts +184 -0
  755. package/src/server/windows-tray-control.ts +41 -0
  756. package/src/server/ws-bridge.ts +472 -0
  757. package/src/service-manager-probe.ts +892 -0
  758. package/src/service.ts +3575 -0
  759. package/src/sidecar/auth.ts +92 -0
  760. package/src/sidecar/candidates.ts +83 -0
  761. package/src/stall-timeout.ts +20 -0
  762. package/src/storage/cleanup-job.ts +57 -0
  763. package/src/storage/cleanup.ts +3085 -0
  764. package/src/storage/policy-job.ts +457 -0
  765. package/src/storage/policy-scheduler.ts +40 -0
  766. package/src/storage/policy-worker.ts +59 -0
  767. package/src/storage/policy.ts +527 -0
  768. package/src/storage/restore-job.ts +299 -0
  769. package/src/storage/restore-worker.ts +58 -0
  770. package/src/storage/scanner.ts +238 -0
  771. package/src/storage/storage-mutation-coordinator.ts +139 -0
  772. package/src/storage/worker-lifecycle.ts +215 -0
  773. package/src/tray/assets/opencodex-tray-offline.ico +0 -0
  774. package/src/tray/assets/opencodex-tray-online.ico +0 -0
  775. package/src/tray/assets/opencodex-tray-warning.ico +0 -0
  776. package/src/tray/assets/opencodex-tray.png +0 -0
  777. package/src/tray/windows-tray.ps1 +364 -0
  778. package/src/tray/windows.ts +757 -0
  779. package/src/types/accounts.ts +37 -0
  780. package/src/types/config.ts +876 -0
  781. package/src/types/provider.ts +545 -0
  782. package/src/types/request.ts +384 -0
  783. package/src/types/tools.ts +131 -0
  784. package/src/types/wire.ts +80 -0
  785. package/src/types.ts +106 -0
  786. package/src/update/badge.ts +72 -0
  787. package/src/update/index.ts +415 -0
  788. package/src/update/job.ts +1887 -0
  789. package/src/update/notify.ts +263 -0
  790. package/src/update/npm-cache-preflight.d.mts +47 -0
  791. package/src/update/npm-cache-preflight.mjs +201 -0
  792. package/src/update/npm-invocation.d.mts +23 -0
  793. package/src/update/npm-invocation.mjs +94 -0
  794. package/src/update/transactional-install.d.mts +22 -0
  795. package/src/update/transactional-install.mjs +259 -0
  796. package/src/update/tray-update-plan.d.mts +18 -0
  797. package/src/update/tray-update-plan.mjs +38 -0
  798. package/src/usage/cost.ts +625 -0
  799. package/src/usage/debug.ts +97 -0
  800. package/src/usage/expected-prices.ts +416 -0
  801. package/src/usage/log.ts +1223 -0
  802. package/src/usage/summary.ts +753 -0
  803. package/src/usage/totals.ts +14 -0
  804. package/src/usage/user-cost-overlay-reconciler.ts +313 -0
  805. package/src/usage/user-cost-overlays.ts +314 -0
  806. package/src/vision/anthropic-describe.ts +189 -0
  807. package/src/vision/backends.ts +97 -0
  808. package/src/vision/describe.ts +131 -0
  809. package/src/vision/eligibility.ts +250 -0
  810. package/src/vision/index.ts +681 -0
  811. package/src/vision/reasoning.ts +55 -0
  812. package/src/vision/routed-describe.ts +175 -0
  813. package/src/vision/timeout-bounds.ts +9 -0
  814. package/src/web-search/anthropic-executor.ts +195 -0
  815. package/src/web-search/backends.ts +108 -0
  816. package/src/web-search/exa-executor.ts +88 -0
  817. package/src/web-search/executor.ts +113 -0
  818. package/src/web-search/format-result.ts +89 -0
  819. package/src/web-search/gemini-executor.ts +141 -0
  820. package/src/web-search/index.ts +331 -0
  821. package/src/web-search/loop.ts +896 -0
  822. package/src/web-search/parse.ts +315 -0
  823. package/src/web-search/progress-stream.ts +342 -0
  824. package/src/web-search/sources.ts +60 -0
  825. package/src/web-search/synthetic-tool.ts +47 -0
  826. package/src/web-search/xai-executor.ts +219 -0
@@ -0,0 +1,560 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { readFileSync } from "node:fs";
3
+
4
+ /**
5
+ * Codex parses a catalog entry's `input_modalities` as a closed enum, and one out-of-enum
6
+ * value makes it reject the ENTIRE catalog file — plugins, apps and MCP servers all stop
7
+ * loading over one model's metadata (#759).
8
+ *
9
+ * The catalog writer normalizes on the way out, but a rejected value stored here would still
10
+ * be handed back to the GUI and CLI as if it were real, and the offline `ocx models add` path
11
+ * already refuses it. Validate at ingress so all three paths agree.
12
+ */
13
+ const ALLOWED_INPUT_MODALITIES = new Set(["text", "image", "audio"]);
14
+
15
+ function readInputModalities(raw: unknown): { values?: string[]; error?: string } {
16
+ if (raw === undefined) return {};
17
+ if (!Array.isArray(raw)) return { error: "inputModalities must be an array" };
18
+ // Reject non-strings rather than filtering them out. Dropping them silently accepted a
19
+ // malformed POST and, worse, let a PUT of `[42]` clear the stored modalities while
20
+ // answering 200 — the opposite of the contract this validator exists to state. An empty
21
+ // array stays valid: that is how `ocx models edit --modalities -` clears the field.
22
+ const rejected: string[] = [];
23
+ for (const value of raw) {
24
+ if (typeof value !== "string") return { error: "inputModalities must contain only strings" };
25
+ if (!ALLOWED_INPUT_MODALITIES.has(value)) rejected.push(value);
26
+ }
27
+ if (rejected.length > 0) {
28
+ return { error: `unsupported input modality: ${rejected.join(", ")} (allowed: text, image, audio)` };
29
+ }
30
+ return { values: raw as string[] };
31
+ }
32
+
33
+ /**
34
+ * Custom-row reasoning ladder. Labels are validated against the Codex ladder (low..ultra)
35
+ * exactly like provider `modelReasoningEfforts` values; unknown labels would otherwise
36
+ * surface in a catalog the upstream never accepts. An empty array is meaningful (explicit
37
+ * "no reasoning" hides the effort control) and must be preserved, not cleared.
38
+ */
39
+ function readReasoningEfforts(raw: unknown): { values?: string[]; error?: string } {
40
+ if (raw === undefined) return {};
41
+ if (!Array.isArray(raw)) return { error: "reasoningEfforts must be an array" };
42
+ const rejected: string[] = [];
43
+ const values: string[] = [];
44
+ for (const value of raw) {
45
+ if (typeof value !== "string") return { error: "reasoningEfforts must contain only strings" };
46
+ if (!isDeclaredReasoningEffort(value)) { rejected.push(value); continue; }
47
+ if (!values.includes(value)) values.push(value);
48
+ }
49
+ if (rejected.length > 0) {
50
+ return { error: `unsupported reasoning effort: ${rejected.join(", ")} (allowed: none, minimal, low, medium, high, xhigh, max, ultra)` };
51
+ }
52
+ // Canonical order: the catalog writes supported_reasoning_levels in input order and the
53
+ // fallback default picks the first entry, so a caller-chosen order must not leak through.
54
+ return { values: canonicalizeReasoningEfforts(values) };
55
+ }
56
+
57
+ /** Default effort must be a ladder member that the declared ladder actually includes. */
58
+ function readDefaultReasoningEffort(raw: unknown, efforts: string[] | undefined): { value?: string; error?: string } {
59
+ if (raw === undefined) return {};
60
+ if (raw === null) return { value: undefined };
61
+ if (typeof raw !== "string" || !isDeclaredReasoningEffort(raw)) {
62
+ return { error: "defaultReasoningEffort must be one of: none, minimal, low, medium, high, xhigh, max, ultra" };
63
+ }
64
+ if (efforts === undefined || efforts.length === 0) {
65
+ return { error: "defaultReasoningEffort requires a non-empty reasoningEfforts ladder" };
66
+ }
67
+ if (!efforts.includes(raw)) {
68
+ return { error: `defaultReasoningEffort "${raw}" is not in the declared reasoningEfforts ladder` };
69
+ }
70
+ return { value: raw };
71
+ }
72
+ import type { CatalogModel } from "../../codex/catalog";
73
+ import { accountBoundNativeOpenAiSlugsBySelector, catalogModelSlug, configuredNativeAliasSlugs, disabledNativeSlugs, invalidateCodexModelsCache, nativeModelRows, shouldIncludeAccountBoundNativeOpenAi, uniqueCatalogModelsForPublicList } from "../../codex/catalog";
74
+ import { CatalogGatherBusyError } from "../../codex/catalog/provider-fetch";
75
+ import { getProviderLiveModelCount } from "../../codex/model-cache";
76
+ import {
77
+ DEFAULT_SUBAGENT_MODELS,
78
+ codexAutoStartEnabled,
79
+ hasOwnProvider,
80
+ isValidProviderName,
81
+ multiAgentGuidanceEnabled,
82
+ providerBaseUrlConfigError,
83
+ providerHeadersConfigError,
84
+ saveConfigPreservingClaudeCode,
85
+ } from "../../config";
86
+ import {
87
+ clearLoginState,
88
+ getLoginStatus,
89
+ isPublicOAuthProvider,
90
+ listOAuthProviders,
91
+ startLoginFlow,
92
+ submitManualLoginCode,
93
+ upsertOAuthProvider,
94
+ } from "../../oauth";
95
+ import { removeCredential } from "../../oauth/store";
96
+ import { providerDestinationResolvedError } from "../../lib/destination-policy";
97
+ import { enrichProviderFromCatalog, listKeyLoginProviders } from "../../oauth/key-providers";
98
+ import { deriveProviderPresets } from "../../providers/derive";
99
+ import { providerCodexAccountMode } from "../../providers/registry";
100
+ import { encodedModelIdCollides, routedSlug, slugEquals } from "../../providers/slug-codec";
101
+ import { knownModelIdsForProvider } from "../../router";
102
+ import { COMBO_NAMESPACE, comboDisabledModelSelectors, comboModelId, preservesPhysicalComboProvider } from "../../combos";
103
+ import { clearProviderQuotaCache, fetchProviderQuotaReports } from "../../providers/quota";
104
+ import { isCanonicalOpenAiForwardProvider } from "../../providers/openai-tiers";
105
+ import { clearThreadAccountMap } from "../../codex/routing";
106
+ import { primeCodexPoolQuotas } from "../../codex/auth-api";
107
+ import { DEFAULT_PROVIDER_CONTEXT_CAP, globalContextCapValue, providerContextCap, providerContextCaps, setAllProviderContextCaps, setGlobalContextCapValue, setProviderContextCap } from "../../providers/context-cap";
108
+ import { resolveCodexHomeDir } from "../../codex/home";
109
+ import { readUsageEntries } from "../../usage/log";
110
+ import { getUsageDebugLogEntries } from "../../usage/debug";
111
+ import { parseRange, parseUsageSurface, summarizeUsage } from "../../usage/summary";
112
+ import { stripCodexRuntimeProviderFields } from "../../codex/auth-context";
113
+ import { getProviderRegistryEntry } from "../../providers/registry";
114
+ import { getDebugLogEntries } from "../../lib/debug-log-buffer";
115
+ import { canonicalizeReasoningEfforts, isDeclaredReasoningEffort } from "../../reasoning-effort";
116
+ import { getInjectionDebugLogEntries } from "../../lib/injection-debug-log";
117
+ import {
118
+ clearDebugSettings,
119
+ clearDebugSetting,
120
+ getDebugSettings,
121
+ setDebugSettings,
122
+ type DebugFlag,
123
+ } from "../../lib/debug-settings";
124
+ import type { OcxClaudeCodeConfig, OcxConfig, OcxCustomModel, OcxProviderConfig } from "../../types";
125
+ import { drainAndShutdown } from "../lifecycle";
126
+ import { filterRequestLogs, getRequestLogEntries, type RequestLogEntry } from "../request-log";
127
+ import { estimateComboCost, estimateRequestCost, normalizeCostTokens, tokensPerSecond } from "../../usage/cost";
128
+ import type { PersistedUsageAttempt } from "../../usage/log";
129
+ import { isAllowedRequestOrigin, jsonResponse, providerManagementConfigError, publicProviderBaseUrl, safeConfigDTO, corsHeaders } from "../auth-cors";
130
+ import { applySystemEnvToggle } from "../system-env";
131
+ import {
132
+ ClientPathError,
133
+ EXPORT_CLIENTS,
134
+ EXPORT_CLIENT_IDS,
135
+ OPENCODE_PROVIDER_ID,
136
+ buildClientConfigText,
137
+ isExportClientId,
138
+ opencodeProxyBaseUrl,
139
+ } from "../../clients/config-export";
140
+ import type {
141
+ ExportClientId,
142
+ ExportModel,
143
+ OpencodeGeneratedConfig,
144
+ PiGeneratedConfig,
145
+ } from "../../clients/config-export";
146
+
147
+ import { isPlainRecord, parseDebugLogQuery, tokPerSecondResult, unavailableCostReason, costResult, requestLogDto, stripRegistryOnlyStaticHeaders, fetchAllModels } from "./shared";
148
+ import type { MetricUnavailableReason, TokPerSecondResult, CostEstimateReason, CostResult, MetricSource } from "./shared";
149
+ import type { ManagementContext } from "./context";
150
+ import { listManagementModelRows, loadExportModels } from "./model-rows";
151
+ import { readManagementJsonBody, rethrowManagementBodyTooLarge } from "./body";
152
+
153
+ /**
154
+ * Counts read back off the SERIALIZED document rather than recomputed from the input rows.
155
+ * `modelsWithoutLimits` drives a GUI line claiming "these models ship without limits", so it
156
+ * has to describe the bytes the user actually receives — a parallel reimplementation of the
157
+ * core's "authoritative context window" rule would be free to drift from it silently.
158
+ */
159
+ function summarizeExportedModels(client: ExportClientId, document: unknown): { modelCount: number; modelsWithoutLimits: number } {
160
+ // Each client counts its own document shape. The previous branch assumed
161
+ // "anything that is not OpenCode must be Pi", which silently misread the
162
+ // moment a third client existed.
163
+ return EXPORT_CLIENTS[client].summarize(document);
164
+ }
165
+
166
+ export async function handleModelRoutes(ctx: ManagementContext): Promise<Response | null> {
167
+ const { req, url, config, deps, convergeCodexCatalog, syncClaudeAgentDefsBestEffort } = ctx;
168
+ // A handler persists the exact config object passed in. Production defaults to
169
+ // the real store; tests that pass an in-memory fixture inject a no-op/spy. Do not
170
+ // bypass this seam with a dynamic config import — doing so replaced a user's
171
+ // ~/.opencodex/config.json with the `existing-uuid` test fixture.
172
+ const persistConfig = deps.saveConfigPreservingClaudeCode ?? saveConfigPreservingClaudeCode;
173
+
174
+ if (url.pathname === "/api/catalog" && req.method === "GET") {
175
+ const { readCatalog, readCodexCatalogPath } = await import("../../codex/catalog");
176
+ const catalog = readCatalog(readCodexCatalogPath());
177
+ if (!catalog) return jsonResponse({ error: "catalog not found" }, 404, req, config);
178
+ const headers: Record<string, string> = {
179
+ "Content-Type": "application/json",
180
+ ...corsHeaders(req, config),
181
+ };
182
+ const { loadPersistedCodexRuntime } = await import("../../codex/runtime");
183
+ const version = loadPersistedCodexRuntime()?.selectedVersion;
184
+ if (version) headers["x-opencodex-codex-version"] = version;
185
+ return new Response(JSON.stringify(catalog), { status: 200, headers });
186
+ }
187
+
188
+ if (url.pathname === "/api/models" && req.method === "GET") {
189
+ return jsonResponse(await listManagementModelRows(config));
190
+ }
191
+
192
+ /**
193
+ * Client config document for OpenCode / Pi, built from the SAME function `ocx export`
194
+ * calls, so the bytes a user downloads here and the bytes they pipe from the CLI cannot
195
+ * disagree. Read-only: this route never writes the user's client config.
196
+ */
197
+ if (url.pathname === "/api/client-config" && req.method === "GET") {
198
+ const requested = url.searchParams.get("client")?.trim() ?? "";
199
+ if (!isExportClientId(requested)) {
200
+ return jsonResponse(
201
+ { error: `client must be one of: ${EXPORT_CLIENT_IDS.join(", ")}` },
202
+ 400,
203
+ req,
204
+ config,
205
+ );
206
+ }
207
+ const spec = EXPORT_CLIENTS[requested];
208
+ // Resolved before the catalog load on purpose. A refused override is a
209
+ // property of the request, not of the catalog: validating it afterwards
210
+ // let a busy or failing catalog answer 503 first, so a user with a
211
+ // relative override never saw the message that says how to fix it — and
212
+ // the route did the enumeration work anyway for input it was going to
213
+ // reject.
214
+ let destination: string;
215
+ try {
216
+ destination = spec.destination(process.env);
217
+ } catch (error) {
218
+ // A client's own environment override can name a path the resolver
219
+ // refuses — a relative value, which this process and the client would
220
+ // resolve against different working directories. That is a
221
+ // user-correctable configuration error, not a server fault, so it leaves
222
+ // this boundary as a bounded 400 instead of escaping handleManagementAPI
223
+ // as a generic 500 and stripping the message that says how to fix it.
224
+ // `integrations/state.ts` and `integrations/writer.ts` already catch the
225
+ // same error on their paths; this route was the one that did not.
226
+ if (!(error instanceof ClientPathError)) throw error;
227
+ return jsonResponse({ error: error.message }, 400, req, config);
228
+ }
229
+ let models: ExportModel[];
230
+ try {
231
+ // The ONE loader every export surface uses. It carries the visibility
232
+ // filter with it, so this route and the integration routes cannot end up
233
+ // telling a client about different models.
234
+ models = await loadExportModels(config);
235
+ } catch (error) {
236
+ // A partial or empty `models` block reads as a valid config while offering nothing,
237
+ // so a catalog failure is surfaced as unavailable rather than serialized. The
238
+ // catalog-busy error keeps its own 503 from handleManagementAPI.
239
+ if (error instanceof CatalogGatherBusyError) throw error;
240
+ return jsonResponse(
241
+ { error: `model catalog unavailable: ${error instanceof Error ? error.message : String(error)}` },
242
+ 503,
243
+ req,
244
+ config,
245
+ );
246
+ }
247
+ const built = buildClientConfigText(requested, {
248
+ baseUrl: opencodeProxyBaseUrl(Number(url.port) || config.port, config.hostname),
249
+ models,
250
+ config,
251
+ });
252
+ const document = built.document;
253
+ return jsonResponse({
254
+ client: spec.id,
255
+ filename: spec.filename,
256
+ destination,
257
+ apiKeyEnv: spec.apiKeyEnv,
258
+ exportHint: spec.exportHint,
259
+ // The client's own format and the exact bytes for it. The GUI previously
260
+ // re-serialized `config` as JSON, which is wrong for four of the six
261
+ // clients; `mediaType` also drives the download blob.
262
+ format: built.format,
263
+ mediaType: built.mediaType,
264
+ text: built.text,
265
+ ...summarizeExportedModels(requested, document),
266
+ config: document,
267
+ }, 200, req, config);
268
+ }
269
+
270
+ // Enable/disable models: which routed models Codex sees. PUT hides them from the catalog +
271
+ // /v1/models and invalidates Codex's 5-min models cache so it applies on the next turn.
272
+ if (url.pathname === "/api/disabled-models" && req.method === "PUT") {
273
+ let body: { models?: unknown };
274
+ try { body = await readManagementJsonBody(req); } catch (error) { rethrowManagementBodyTooLarge(error); return jsonResponse({ error: "invalid JSON body" }, 400); }
275
+ const disabled = Array.isArray(body.models) ? body.models.filter((m): m is string => typeof m === "string") : [];
276
+ config.disabledModels = disabled;
277
+ persistConfig(config);
278
+ const catalogRefresh = await convergeCodexCatalog();
279
+ return jsonResponse({ ok: true, disabled, catalogRefresh });
280
+ }
281
+
282
+ // One user-facing visibility switch spans two persisted filters: a provider allowlist and the
283
+ // shared blocklist. Keep the update atomic so an interrupted request cannot expose a half-applied
284
+ // state. Native rows only use the blocklist; routed/custom rows also join a non-empty allowlist.
285
+ if (url.pathname === "/api/model-visibility" && req.method === "PUT") {
286
+ let parsedBody: unknown;
287
+ try { parsedBody = await readManagementJsonBody(req); } catch (error) { rethrowManagementBodyTooLarge(error); return jsonResponse({ error: "invalid JSON body" }, 400); }
288
+ if (!isPlainRecord(parsedBody)) return jsonResponse({ error: "invalid model visibility request" }, 400);
289
+ const body = parsedBody;
290
+ const scope = body.scope === "models" || body.scope === "provider" ? body.scope : null;
291
+ const provider = typeof body.provider === "string" ? body.provider.trim() : "";
292
+ if (!scope || !provider || !isValidProviderName(provider) || typeof body.enabled !== "boolean" || !Array.isArray(body.targets)) {
293
+ return jsonResponse({ error: "invalid model visibility request" }, 400);
294
+ }
295
+
296
+ const providerConfig = hasOwnProvider(config.providers, provider) ? config.providers[provider] : undefined;
297
+ const isVirtualComboNamespace = provider === COMBO_NAMESPACE && !preservesPhysicalComboProvider(config);
298
+ if (!providerConfig && provider !== "openai" && !isVirtualComboNamespace) {
299
+ return jsonResponse({ error: "unknown model visibility provider" }, 400);
300
+ }
301
+ const accountNativeQualified = shouldIncludeAccountBoundNativeOpenAi(config)
302
+ ? [...accountBoundNativeOpenAiSlugsBySelector(config).entries()].flatMap(([selector, slugs]) =>
303
+ slugs.filter(slug => !nativeModelRows(config).some(row => row.slug === slug)).map(slug => `${selector}/${slug}`))
304
+ : [];
305
+ const supportedNative = new Set([
306
+ ...nativeModelRows(config).map(row => row.slug),
307
+ ...accountNativeQualified,
308
+ ]);
309
+ const targets: Array<{ id: string; native: boolean }> = [];
310
+ const seen = new Set<string>();
311
+ for (const value of body.targets) {
312
+ if (!isPlainRecord(value) || typeof value.id !== "string" || (value.native !== undefined && typeof value.native !== "boolean")) {
313
+ return jsonResponse({ error: "invalid model visibility target" }, 400);
314
+ }
315
+ const id = value.id.trim();
316
+ const native = value.native === true;
317
+ if (!id || (provider === "openai") !== native || (native && !supportedNative.has(id))) {
318
+ return jsonResponse({ error: "invalid model visibility target" }, 400);
319
+ }
320
+ const key = `${native ? "native" : "routed"}:${id}`;
321
+ if (!seen.has(key)) {
322
+ seen.add(key);
323
+ targets.push({ id, native });
324
+ }
325
+ }
326
+ if (targets.length === 0) return jsonResponse({ error: "model visibility targets required" }, 400);
327
+
328
+ const knownComboSelectors = new Set(
329
+ Object.entries(config.combos ?? {}).flatMap(([id, combo]) => (
330
+ comboDisabledModelSelectors(id, combo)
331
+ )),
332
+ );
333
+ const targetComboSelectors = new Map<string, Set<string>>();
334
+ if (isVirtualComboNamespace) {
335
+ for (const target of targets) {
336
+ const combo = config.combos && Object.hasOwn(config.combos, target.id) ? config.combos[target.id] : undefined;
337
+ if (!combo) return jsonResponse({ error: "invalid model visibility target" }, 400);
338
+ targetComboSelectors.set(target.id, new Set(comboDisabledModelSelectors(target.id, combo)));
339
+ }
340
+ }
341
+ const matchesTarget = (stored: string, target: { id: string; native: boolean }) => target.native
342
+ ? stored === target.id
343
+ : isVirtualComboNamespace
344
+ ? targetComboSelectors.get(target.id)!.has(stored)
345
+ : slugEquals(stored, provider, target.id);
346
+
347
+ let disabled = [...new Set(config.disabledModels ?? [])];
348
+ if (body.enabled) {
349
+ if (scope === "provider") {
350
+ if (providerConfig && !isVirtualComboNamespace) delete providerConfig.selectedModels;
351
+ if (isVirtualComboNamespace) {
352
+ disabled = disabled.filter(stored => !knownComboSelectors.has(stored));
353
+ } else {
354
+ const nativeIds = provider === "openai"
355
+ ? disabledNativeSlugs({ disabledModels: disabled })
356
+ : new Set<string>();
357
+ const accountNativeIds = provider === "openai" ? new Set(accountNativeQualified) : new Set<string>();
358
+ const nativeAliasSlugs = provider === "openai"
359
+ ? configuredNativeAliasSlugs(config)
360
+ : new Set<string>();
361
+ disabled = disabled.filter(stored => (
362
+ knownComboSelectors.has(stored)
363
+ || nativeAliasSlugs.has(stored)
364
+ || (!stored.startsWith(`${provider}/`) && !nativeIds.has(stored) && !accountNativeIds.has(stored))
365
+ ));
366
+ }
367
+ } else {
368
+ if (!isVirtualComboNamespace && providerConfig?.selectedModels && providerConfig.selectedModels.length > 0) {
369
+ const additions = targets.filter(target => !target.native).map(target => target.id);
370
+ providerConfig.selectedModels = [...new Set([...providerConfig.selectedModels, ...additions])];
371
+ }
372
+ disabled = disabled.filter(stored => !targets.some(target => matchesTarget(stored, target)));
373
+ }
374
+ } else {
375
+ for (const target of targets) {
376
+ const canonical = target.native
377
+ ? target.id
378
+ : isVirtualComboNamespace
379
+ ? comboModelId(target.id)
380
+ : routedSlug(provider, target.id);
381
+ const alreadyDisabled = disabled.some(stored => matchesTarget(stored, target));
382
+ if (!alreadyDisabled) disabled.push(canonical);
383
+ }
384
+ }
385
+
386
+ config.disabledModels = disabled;
387
+ persistConfig(config);
388
+ const catalogRefresh = await convergeCodexCatalog();
389
+ return jsonResponse({ ok: true, scope, provider, enabled: body.enabled, disabled, catalogRefresh });
390
+ }
391
+
392
+ if (url.pathname === "/api/custom-models" && req.method === "GET") {
393
+ return jsonResponse(config.customModels ?? []);
394
+ }
395
+
396
+ if (url.pathname === "/api/custom-models" && req.method === "POST") {
397
+ let body: { provider?: unknown; modelId?: unknown; displayName?: unknown; contextWindow?: unknown; inputModalities?: unknown; reasoningEfforts?: unknown; defaultReasoningEffort?: unknown };
398
+ try { body = await readManagementJsonBody(req); } catch (error) { rethrowManagementBodyTooLarge(error); return jsonResponse({ error: "invalid JSON body" }, 400); }
399
+ const provider = typeof body.provider === "string" ? body.provider.trim() : "";
400
+ const modelId = typeof body.modelId === "string" ? body.modelId.trim() : "";
401
+ if (!provider || !modelId) return jsonResponse({ error: "provider and modelId are required" }, 400);
402
+ if (!isValidProviderName(provider)) return jsonResponse({ error: "invalid provider name" }, 400);
403
+ if (!hasOwnProvider(config.providers, provider)) return jsonResponse({ error: "provider not configured" }, 404);
404
+ const displayName = typeof body.displayName === "string" && body.displayName.trim() ? body.displayName.trim() : undefined;
405
+ if (displayName?.includes("/")) return jsonResponse({ error: "displayName must not contain /" }, 400);
406
+ const contextWindow = typeof body.contextWindow === "number" && body.contextWindow > 0 ? Math.floor(body.contextWindow) : undefined;
407
+ const modalities = readInputModalities(body.inputModalities);
408
+ if (modalities.error) return jsonResponse({ error: modalities.error }, 400);
409
+ const inputModalities = modalities.values;
410
+ const reasoning = readReasoningEfforts(body.reasoningEfforts);
411
+ if (reasoning.error) return jsonResponse({ error: reasoning.error }, 400);
412
+ const defaultEffort = readDefaultReasoningEffort(body.defaultReasoningEffort, reasoning.values);
413
+ if (defaultEffort.error) return jsonResponse({ error: defaultEffort.error }, 400);
414
+ const existing = config.customModels ?? [];
415
+ const newSlug = routedSlug(provider, modelId);
416
+ if (existing.some(cm => routedSlug(cm.provider, cm.modelId) === newSlug)) {
417
+ return jsonResponse({ error: "duplicate model" }, 409);
418
+ }
419
+ const known = knownModelIdsForProvider(provider, config.providers[provider], config);
420
+ if (encodedModelIdCollides(modelId, known)) {
421
+ return jsonResponse({ error: "ambiguous model id" }, 409);
422
+ }
423
+ const entry: OcxCustomModel = {
424
+ id: randomUUID(),
425
+ provider,
426
+ modelId,
427
+ ...(displayName ? { displayName } : {}),
428
+ ...(contextWindow ? { contextWindow } : {}),
429
+ ...(inputModalities && inputModalities.length > 0 ? { inputModalities } : {}),
430
+ ...(reasoning.values !== undefined ? { reasoningEfforts: reasoning.values } : {}),
431
+ ...(defaultEffort.value ? { defaultReasoningEffort: defaultEffort.value } : {}),
432
+ addedAt: new Date().toISOString(),
433
+ };
434
+ config.customModels = [...existing, entry];
435
+ persistConfig(config);
436
+ const catalogRefresh = await convergeCodexCatalog();
437
+ return jsonResponse({ ...entry, catalogRefresh }, 201);
438
+ }
439
+
440
+ const customPutMatch = url.pathname.match(/^\/api\/custom-models\/([^/]+)$/);
441
+ if (customPutMatch && req.method === "PUT") {
442
+ let id: string;
443
+ try { id = decodeURIComponent(customPutMatch[1]); } catch { return jsonResponse({ error: "invalid id encoding" }, 400); }
444
+ let body: { displayName?: unknown; contextWindow?: unknown; inputModalities?: unknown; modelId?: unknown; reasoningEfforts?: unknown; defaultReasoningEffort?: unknown };
445
+ try { body = await readManagementJsonBody(req); } catch (error) { rethrowManagementBodyTooLarge(error); return jsonResponse({ error: "invalid JSON body" }, 400); }
446
+ const list = config.customModels ?? [];
447
+ const idx = list.findIndex(cm => cm.id === id);
448
+ if (idx === -1) return jsonResponse({ error: "not found" }, 404);
449
+ const cm = { ...list[idx] };
450
+ if (typeof body.modelId === "string" && body.modelId.trim()) {
451
+ cm.modelId = body.modelId.trim();
452
+ }
453
+ if (body.displayName !== undefined) {
454
+ const dn = typeof body.displayName === "string" ? body.displayName.trim() : "";
455
+ if (dn.includes("/")) return jsonResponse({ error: "displayName must not contain /" }, 400);
456
+ cm.displayName = dn || undefined;
457
+ }
458
+ if (body.contextWindow !== undefined) {
459
+ cm.contextWindow = typeof body.contextWindow === "number" && body.contextWindow > 0 ? Math.floor(body.contextWindow) : undefined;
460
+ }
461
+ if (body.inputModalities !== undefined) {
462
+ const edited = readInputModalities(body.inputModalities);
463
+ if (edited.error) return jsonResponse({ error: edited.error }, 400);
464
+ cm.inputModalities = edited.values && edited.values.length > 0 ? edited.values : undefined;
465
+ }
466
+ // `null` clears the stored ladder back to "inherit from the provider row"; `[]` stays
467
+ // stored as an explicit "no reasoning" override. The default effort rides along and is
468
+ // validated against the ladder the row ends up with.
469
+ if (body.reasoningEfforts !== undefined) {
470
+ if (body.reasoningEfforts === null) {
471
+ cm.reasoningEfforts = undefined;
472
+ } else {
473
+ const edited = readReasoningEfforts(body.reasoningEfforts);
474
+ if (edited.error) return jsonResponse({ error: edited.error }, 400);
475
+ cm.reasoningEfforts = edited.values;
476
+ }
477
+ }
478
+ if (body.defaultReasoningEffort !== undefined) {
479
+ const edited = readDefaultReasoningEffort(body.defaultReasoningEffort, cm.reasoningEfforts);
480
+ if (edited.error) return jsonResponse({ error: edited.error }, 400);
481
+ cm.defaultReasoningEffort = edited.value;
482
+ }
483
+ // Mirror of the POST invariant: a default only survives as a member of the final ladder.
484
+ // Without this, a ladder shrink/clear on a row that was created with a default leaves a
485
+ // stale default that re-applies itself onto the inherited ladder in the generated catalog
486
+ // (the GUI toggle-off path sends only reasoningEfforts, never the default).
487
+ if (cm.defaultReasoningEffort !== undefined) {
488
+ const ladder = cm.reasoningEfforts;
489
+ if (!ladder || ladder.length === 0 || !ladder.includes(cm.defaultReasoningEffort)) {
490
+ cm.defaultReasoningEffort = undefined;
491
+ }
492
+ }
493
+ const updatedSlug = routedSlug(cm.provider, cm.modelId);
494
+ if (list.some((other, i) => i !== idx && routedSlug(other.provider, other.modelId) === updatedSlug)) {
495
+ return jsonResponse({ error: "duplicate model" }, 409);
496
+ }
497
+ const known = knownModelIdsForProvider(cm.provider, config.providers[cm.provider], {
498
+ customModels: list.filter((_, i) => i !== idx),
499
+ });
500
+ if (encodedModelIdCollides(cm.modelId, known)) {
501
+ return jsonResponse({ error: "ambiguous model id" }, 409);
502
+ }
503
+ list[idx] = cm;
504
+ config.customModels = list;
505
+ persistConfig(config);
506
+ const catalogRefresh = await convergeCodexCatalog();
507
+ return jsonResponse({ ...cm, catalogRefresh });
508
+ }
509
+
510
+ const customDelMatch = url.pathname.match(/^\/api\/custom-models\/([^/]+)$/);
511
+ if (customDelMatch && req.method === "DELETE") {
512
+ let id: string;
513
+ try { id = decodeURIComponent(customDelMatch[1]); } catch { return jsonResponse({ error: "invalid id encoding" }, 400); }
514
+ const list = config.customModels ?? [];
515
+ const idx = list.findIndex(cm => cm.id === id);
516
+ if (idx === -1) return jsonResponse({ error: "not found" }, 404);
517
+ list.splice(idx, 1);
518
+ config.customModels = list.length > 0 ? list : undefined;
519
+ persistConfig(config);
520
+ const catalogRefresh = await convergeCodexCatalog();
521
+ return jsonResponse({ ok: true, catalogRefresh });
522
+ }
523
+
524
+ // Per-provider catalog allowlist (issue #52): when a provider has a non-empty selectedModels list,
525
+ // only those ids ship to Codex's catalog / /v1/models. GET returns the CURRENT selection plus the
526
+ // FULL available set per provider (unfiltered — the picker needs everything to choose from).
527
+ if (url.pathname === "/api/selected-models" && req.method === "GET") {
528
+ const models = await fetchAllModels(config);
529
+ const available: Record<string, string[]> = {};
530
+ for (const m of models) (available[m.provider] ??= []).push(m.id);
531
+ const selected: Record<string, string[]> = {};
532
+ // Live-catalog provenance. The GUI cannot infer this by subtracting known custom ids: an id
533
+ // that is both custom and discovered would make a real live catalog look custom-only.
534
+ const liveModelCounts: Record<string, number> = {};
535
+ for (const [name, prov] of Object.entries(config.providers)) {
536
+ if (Array.isArray(prov.selectedModels) && prov.selectedModels.length > 0) selected[name] = [...prov.selectedModels];
537
+ const liveCount = getProviderLiveModelCount(name);
538
+ if (liveCount !== undefined) liveModelCounts[name] = liveCount;
539
+ }
540
+ return jsonResponse({ selected, available, liveModelCounts });
541
+ }
542
+ if (url.pathname === "/api/selected-models" && req.method === "PUT") {
543
+ let body: { provider?: unknown; models?: unknown };
544
+ try { body = await readManagementJsonBody(req); } catch (error) { rethrowManagementBodyTooLarge(error); return jsonResponse({ error: "invalid JSON body" }, 400); }
545
+ const provider = typeof body.provider === "string" ? body.provider : "";
546
+ if (!provider || !hasOwnProvider(config.providers, provider)) {
547
+ return jsonResponse({ error: "unknown provider" }, provider ? 404 : 400);
548
+ }
549
+ const models = Array.isArray(body.models)
550
+ ? [...new Set(body.models.filter((m): m is string => typeof m === "string"))]
551
+ : [];
552
+ // Empty list clears the allowlist (provider reverts to exposing all models).
553
+ if (models.length > 0) config.providers[provider].selectedModels = models;
554
+ else delete config.providers[provider].selectedModels;
555
+ persistConfig(config);
556
+ const catalogRefresh = await convergeCodexCatalog();
557
+ return jsonResponse({ ok: true, provider, selected: models, catalogRefresh });
558
+ }
559
+ return null;
560
+ }