@iislee/opencodex 2.11.0 → 2.35.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (733) hide show
  1. package/AGENTS_INSTALL.md +109 -0
  2. package/README.md +117 -22
  3. package/bin/ocx.mjs +245 -88
  4. package/bin/package-main.mjs +1 -1
  5. package/gui/dist/assets/index-Be1sDpWw.css +1 -0
  6. package/gui/dist/assets/index-D4OKgUV4.js +114 -0
  7. package/gui/dist/index.html +2 -2
  8. package/gui/dist/provider-icons/alibaba-color.svg +1 -1
  9. package/gui/dist/provider-icons/antigravity-color.svg +1 -1
  10. package/gui/dist/provider-icons/claude-color.svg +1 -1
  11. package/gui/dist/provider-icons/cline-color.svg +16 -0
  12. package/gui/dist/provider-icons/cloudflare-ai-gateway-color.svg +1 -1
  13. package/gui/dist/provider-icons/commandcode-color.svg +1 -0
  14. package/gui/dist/provider-icons/copilot-color.svg +1 -1
  15. package/gui/dist/provider-icons/cursor-color.svg +1 -1
  16. package/gui/dist/provider-icons/deepseek-color.svg +1 -1
  17. package/gui/dist/provider-icons/firepass-color.svg +1 -1
  18. package/gui/dist/provider-icons/fireworks-color.svg +1 -1
  19. package/gui/dist/provider-icons/gemini-color.svg +1 -1
  20. package/gui/dist/provider-icons/github-copilot-color.svg +1 -1
  21. package/gui/dist/provider-icons/gitlab-duo-color.svg +1 -1
  22. package/gui/dist/provider-icons/grok.svg +1 -1
  23. package/gui/dist/provider-icons/groq-color.svg +1 -1
  24. package/gui/dist/provider-icons/huggingface-color.svg +1 -1
  25. package/gui/dist/provider-icons/kimi-color.svg +1 -1
  26. package/gui/dist/provider-icons/kiro-color.svg +2 -2
  27. package/gui/dist/provider-icons/lm-studio-color.svg +1 -1
  28. package/gui/dist/provider-icons/mistral-color.svg +1 -1
  29. package/gui/dist/provider-icons/moonshot-color.svg +1 -1
  30. package/gui/dist/provider-icons/nvidia-color.svg +1 -1
  31. package/gui/dist/provider-icons/ollama-color.svg +1 -1
  32. package/gui/dist/provider-icons/openai.svg +1 -1
  33. package/gui/dist/provider-icons/opencode.svg +2 -1
  34. package/gui/dist/provider-icons/openrouter-color.svg +1 -1
  35. package/gui/dist/provider-icons/pi.svg +2 -2
  36. package/gui/dist/provider-icons/qianfan-color.svg +1 -1
  37. package/gui/dist/provider-icons/qwen-portal-color.svg +1 -1
  38. package/gui/dist/provider-icons/vercel-ai-gateway-color.svg +1 -1
  39. package/gui/dist/provider-icons/vllm-color.svg +1 -1
  40. package/gui/dist/provider-icons/xiaomi-color.svg +1 -1
  41. package/package.json +21 -10
  42. package/src/adapters/anthropic-output-schema.ts +137 -0
  43. package/src/adapters/anthropic.ts +475 -62
  44. package/src/adapters/base.ts +82 -7
  45. package/src/adapters/client-fingerprint.ts +18 -12
  46. package/src/adapters/cline-pass-deepseek-v4-tool-replay.ts +69 -0
  47. package/src/adapters/command-code.ts +637 -0
  48. package/src/adapters/cursor/call-id.ts +44 -0
  49. package/src/adapters/cursor/catalog.ts +541 -0
  50. package/src/adapters/cursor/checkpoint-store.ts +308 -0
  51. package/src/adapters/cursor/cursor-errors.ts +144 -6
  52. package/src/adapters/cursor/discovery.ts +122 -14
  53. package/src/adapters/cursor/effort-map.ts +106 -4
  54. package/src/adapters/cursor/envelope-echo.ts +290 -0
  55. package/src/adapters/cursor/framing.ts +39 -0
  56. package/src/adapters/cursor/h2-pool.ts +123 -0
  57. package/src/adapters/cursor/http1-bidi.ts +361 -0
  58. package/src/adapters/cursor/images.ts +704 -0
  59. package/src/adapters/cursor/live-models.ts +180 -59
  60. package/src/adapters/cursor/live-transport.ts +623 -170
  61. package/src/adapters/cursor/message-mapper.ts +4 -1
  62. package/src/adapters/cursor/native-exec-common.ts +23 -2
  63. package/src/adapters/cursor/native-exec-desktop.ts +23 -0
  64. package/src/adapters/cursor/native-exec-fs.ts +10 -7
  65. package/src/adapters/cursor/native-exec-network.ts +1 -1
  66. package/src/adapters/cursor/native-exec-shell.ts +5 -3
  67. package/src/adapters/cursor/native-exec.ts +121 -14
  68. package/src/adapters/cursor/protobuf-events.ts +829 -11
  69. package/src/adapters/cursor/protobuf-request.ts +447 -75
  70. package/src/adapters/cursor/request-builder.ts +271 -35
  71. package/src/adapters/cursor/tool-definitions.ts +252 -12
  72. package/src/adapters/cursor/tool-result-normalize.ts +115 -0
  73. package/src/adapters/cursor/transport.ts +22 -0
  74. package/src/adapters/cursor/types.ts +48 -1
  75. package/src/adapters/cursor.ts +336 -39
  76. package/src/adapters/exec-tool-result-normalize.ts +99 -0
  77. package/src/adapters/google-antigravity-replay.ts +713 -41
  78. package/src/adapters/google-antigravity-wire.ts +48 -10
  79. package/src/adapters/google-errors.ts +44 -12
  80. package/src/adapters/google-http.ts +38 -10
  81. package/src/adapters/google-tool-schema.ts +80 -15
  82. package/src/adapters/google-truncation.ts +11 -0
  83. package/src/adapters/google.ts +742 -86
  84. package/src/adapters/identity.ts +39 -6
  85. package/src/adapters/image.ts +1 -1
  86. package/src/adapters/kiro-constants.ts +27 -0
  87. package/src/adapters/kiro-errors.ts +11 -0
  88. package/src/adapters/kiro-events.ts +19 -1
  89. package/src/adapters/kiro-thinking.ts +18 -2
  90. package/src/adapters/kiro-tools.ts +65 -17
  91. package/src/adapters/kiro.ts +270 -73
  92. package/src/adapters/mimo-free.ts +17 -0
  93. package/src/adapters/openai-chat-url.ts +11 -0
  94. package/src/adapters/openai-chat.ts +1377 -344
  95. package/src/adapters/openai-responses-url.ts +16 -0
  96. package/src/adapters/openai-responses.ts +1164 -64
  97. package/src/adapters/registry.ts +175 -0
  98. package/src/adapters/responses-tool-schema.ts +67 -0
  99. package/src/adapters/run-turn-queue.ts +36 -1
  100. package/src/adapters/tool-call-id.ts +119 -0
  101. package/src/adapters/tool-catalog-nudge.ts +105 -21
  102. package/src/adapters/xai-tool-schema.ts +436 -0
  103. package/src/adapters/xai-web-search.ts +186 -0
  104. package/src/bridge.ts +512 -92
  105. package/src/chat/inbound.ts +57 -20
  106. package/src/chat/outbound.ts +97 -33
  107. package/src/claude/agents-inject.ts +40 -10
  108. package/src/claude/context-windows.ts +37 -14
  109. package/src/claude/desktop-3p.ts +243 -9
  110. package/src/claude/gateway-cache.ts +41 -4
  111. package/src/claude/inbound.ts +72 -3
  112. package/src/claude/model-info.ts +38 -15
  113. package/src/claude/outbound.ts +88 -21
  114. package/src/cli/access.ts +46 -3
  115. package/src/cli/account-api.ts +150 -19
  116. package/src/cli/account-auth.ts +33 -6
  117. package/src/cli/account-catalog-refresh.ts +14 -0
  118. package/src/cli/account-extended.ts +648 -28
  119. package/src/cli/account-main.ts +317 -0
  120. package/src/cli/account.ts +97 -19
  121. package/src/cli/agent.ts +53 -2
  122. package/src/cli/alias.ts +66 -0
  123. package/src/cli/capabilities-command.ts +94 -0
  124. package/src/cli/capabilities.ts +496 -0
  125. package/src/cli/claude-agent-startup-sync.ts +73 -0
  126. package/src/cli/claude-desktop.ts +60 -15
  127. package/src/cli/claude.ts +129 -35
  128. package/src/cli/codex-log-guard-doctor.ts +103 -0
  129. package/src/cli/codex-shim-autorestore.ts +2 -0
  130. package/src/cli/codex-shim-readiness.ts +76 -0
  131. package/src/cli/combo.ts +8 -0
  132. package/src/cli/config-command.ts +74 -10
  133. package/src/cli/dispatch.ts +821 -0
  134. package/src/cli/doctor.ts +440 -56
  135. package/src/cli/ensure-desired-integrations.ts +152 -0
  136. package/src/cli/export-command.ts +46 -20
  137. package/src/cli/help.ts +34 -274
  138. package/src/cli/index.ts +417 -528
  139. package/src/cli/init.ts +5 -17
  140. package/src/cli/inspect.ts +230 -0
  141. package/src/cli/integrations.ts +120 -2
  142. package/src/cli/lab.ts +607 -0
  143. package/src/cli/launcher-context.ts +77 -0
  144. package/src/cli/minimax.ts +497 -0
  145. package/src/cli/models-runtime.ts +130 -2
  146. package/src/cli/models.ts +118 -16
  147. package/src/cli/observe.ts +123 -11
  148. package/src/cli/opencode.ts +4 -2
  149. package/src/cli/provider-runtime.ts +34 -3
  150. package/src/cli/provider.ts +25 -3
  151. package/src/cli/ready.ts +301 -0
  152. package/src/cli/registry.ts +476 -0
  153. package/src/cli/root.ts +86 -0
  154. package/src/cli/route-policy.ts +92 -0
  155. package/src/cli/runtime-api.ts +57 -10
  156. package/src/cli/star-prompt.ts +71 -15
  157. package/src/cli/status.ts +51 -4
  158. package/src/cli/storage.ts +234 -0
  159. package/src/cli/system-command.ts +16 -0
  160. package/src/cli/system-restart-client.ts +146 -0
  161. package/src/cli/tray-proxy.ts +153 -6
  162. package/src/cli/usage-report.ts +184 -0
  163. package/src/cli/v2.ts +137 -18
  164. package/src/cli/version-skew.ts +46 -0
  165. package/src/cli.ts +1 -1
  166. package/src/clients/config-export.ts +1442 -23
  167. package/src/clients/effective-status.ts +30 -2
  168. package/src/clients/probes/cline.ts +81 -0
  169. package/src/codex/account-label.ts +35 -1
  170. package/src/codex/account-lifecycle.ts +130 -13
  171. package/src/codex/account-namespaces.ts +49 -3
  172. package/src/codex/account-pause.ts +2 -1
  173. package/src/codex/account-priority.ts +84 -0
  174. package/src/codex/account-store.ts +29 -2
  175. package/src/codex/account-usability.ts +25 -2
  176. package/src/codex/admission.ts +256 -0
  177. package/src/codex/affinity-debug.ts +162 -0
  178. package/src/codex/app-server-processes.ts +570 -107
  179. package/src/codex/app-server-restart-service.ts +232 -0
  180. package/src/codex/auth-api.ts +893 -246
  181. package/src/codex/auth-collision.ts +5 -3
  182. package/src/codex/auth-context.ts +348 -32
  183. package/src/codex/autostart-health.ts +24 -1
  184. package/src/codex/catalog/account-models.ts +67 -0
  185. package/src/codex/catalog/aggregation.ts +86 -10
  186. package/src/codex/catalog/bundled.ts +331 -33
  187. package/src/codex/catalog/effort.ts +138 -32
  188. package/src/codex/catalog/filesystem-evidence.ts +302 -0
  189. package/src/codex/catalog/kinds.ts +2 -0
  190. package/src/codex/catalog/metadata.ts +555 -45
  191. package/src/codex/catalog/native-models.ts +75 -0
  192. package/src/codex/catalog/parsing.ts +269 -30
  193. package/src/codex/catalog/provider-fetch.ts +1626 -144
  194. package/src/codex/catalog/sync.ts +1576 -187
  195. package/src/codex/catalog-admission.ts +199 -0
  196. package/src/codex/catalog-refresh-status.ts +105 -0
  197. package/src/codex/catalog-write-serialization.ts +242 -0
  198. package/src/codex/catalog.ts +6 -3
  199. package/src/codex/codex-write-lock.ts +386 -0
  200. package/src/codex/convergence-types.ts +614 -0
  201. package/src/codex/convergence.ts +676 -0
  202. package/src/codex/coordinator-doctor.ts +332 -0
  203. package/src/codex/custom-model-catalog-migration.ts +176 -0
  204. package/src/codex/desired-state.ts +230 -0
  205. package/src/codex/desktop-app-restart.ts +355 -0
  206. package/src/codex/features.ts +636 -39
  207. package/src/codex/generation.ts +202 -0
  208. package/src/codex/history-job.ts +436 -0
  209. package/src/codex/history-lock.ts +242 -0
  210. package/src/codex/history-manifest.ts +112 -0
  211. package/src/codex/history-migration-guardian.ts +30 -24
  212. package/src/codex/history-provider.ts +1016 -235
  213. package/src/codex/history-transition.ts +105 -0
  214. package/src/codex/history-worker.ts +223 -0
  215. package/src/codex/inject-coordination.ts +373 -0
  216. package/src/codex/inject.ts +1114 -152
  217. package/src/codex/injected-marker.ts +37 -3
  218. package/src/codex/integration-record.ts +266 -0
  219. package/src/codex/internal/catalog-writer.ts +203 -0
  220. package/src/codex/internal/history-writer.ts +81 -0
  221. package/src/codex/journal.ts +66 -4
  222. package/src/codex/log-guard/inspect.ts +524 -0
  223. package/src/codex/log-guard/lock.ts +150 -0
  224. package/src/codex/log-guard/maintenance.ts +403 -0
  225. package/src/codex/log-guard/path-safety.ts +88 -0
  226. package/src/codex/log-guard/policy.ts +44 -0
  227. package/src/codex/log-guard/processes.ts +205 -0
  228. package/src/codex/log-guard/protection.ts +489 -0
  229. package/src/codex/log-guard/sqlite-errors.ts +9 -0
  230. package/src/codex/main-account-cache.ts +24 -0
  231. package/src/codex/main-account.ts +29 -1
  232. package/src/codex/management-convergence.ts +167 -0
  233. package/src/codex/model-cache.ts +56 -10
  234. package/src/codex/model-entitlements.ts +360 -0
  235. package/src/codex/native-main-admission.ts +47 -0
  236. package/src/codex/native-main-auth-temp.ts +187 -0
  237. package/src/codex/native-main-claim.ts +178 -0
  238. package/src/codex/native-main-lock-file.ts +162 -0
  239. package/src/codex/native-main-owner.ts +329 -0
  240. package/src/codex/native-profile-api.ts +247 -0
  241. package/src/codex/native-profile-manager.ts +1531 -0
  242. package/src/codex/native-profile-processes.ts +121 -0
  243. package/src/codex/native-profile-recovery.ts +99 -0
  244. package/src/codex/native-profile-stage-store.ts +387 -0
  245. package/src/codex/native-profile-startup.ts +622 -0
  246. package/src/codex/native-profile-store.ts +855 -0
  247. package/src/codex/native-profile-types.ts +120 -0
  248. package/src/codex/native-residue.ts +675 -0
  249. package/src/codex/paths.ts +80 -1
  250. package/src/codex/plan-from-token.ts +140 -0
  251. package/src/codex/plan.ts +40 -0
  252. package/src/codex/plugins-doctor.ts +1 -1
  253. package/src/codex/pool-rotation.ts +74 -4
  254. package/src/codex/project-config-warnings.ts +20 -6
  255. package/src/codex/prompt-journal.ts +352 -0
  256. package/src/codex/prompt-layers.ts +1313 -0
  257. package/src/codex/prompt-lock.ts +143 -0
  258. package/src/codex/prompt-text-probe.ts +238 -0
  259. package/src/codex/quota-rejection.ts +298 -0
  260. package/src/codex/quota.ts +264 -21
  261. package/src/codex/refresh.ts +11 -2
  262. package/src/codex/reset-credit-recovery.ts +1044 -0
  263. package/src/codex/routing.ts +514 -94
  264. package/src/codex/runtime.ts +159 -38
  265. package/src/codex/shim.ts +1065 -31
  266. package/src/codex/subagent-model-fallback.ts +437 -43
  267. package/src/codex/sync.ts +191 -2
  268. package/src/codex/transition-state.ts +720 -0
  269. package/src/codex/upstream-host-health.ts +368 -0
  270. package/src/codex/user-identity.ts +557 -0
  271. package/src/codex/warmup.ts +187 -81
  272. package/src/codex/write-coordination.ts +114 -0
  273. package/src/combos/failover.ts +47 -0
  274. package/src/combos/index.ts +4 -0
  275. package/src/combos/request.ts +32 -0
  276. package/src/combos/types.ts +91 -9
  277. package/src/compatibility/index.ts +26 -0
  278. package/src/compatibility/manifest.ts +253 -0
  279. package/src/compatibility/openai-responses.ts +121 -0
  280. package/src/config/atomic-write.ts +219 -0
  281. package/src/config/paths.ts +40 -0
  282. package/src/config/process-state.ts +309 -0
  283. package/src/config/provider-name.ts +24 -0
  284. package/src/config/provider-validation.ts +177 -0
  285. package/src/config/rebase-provenance.ts +68 -0
  286. package/src/config.ts +1713 -709
  287. package/src/generated/compatibility-version.json +3324 -0
  288. package/src/generated/{jawcode-model-metadata.ts → model-metadata.ts} +19 -17
  289. package/src/grok/inject.ts +16 -5
  290. package/src/grok/inspect.ts +45 -0
  291. package/src/grok/sync.ts +2 -2
  292. package/src/images/loop.ts +157 -33
  293. package/src/images/plan.ts +24 -13
  294. package/src/integrations/config-io.ts +269 -0
  295. package/src/integrations/journal.ts +315 -0
  296. package/src/integrations/merge.ts +135 -0
  297. package/src/integrations/mutation-flight.ts +71 -0
  298. package/src/integrations/native/ownership-preflight.ts +202 -0
  299. package/src/integrations/omp-yaml-source.ts +358 -0
  300. package/src/integrations/owned-refresh.ts +74 -0
  301. package/src/integrations/ownership-policy.ts +160 -0
  302. package/src/integrations/ownership.ts +155 -0
  303. package/src/integrations/registry.ts +166 -0
  304. package/src/integrations/serialize.ts +314 -0
  305. package/src/integrations/state.ts +433 -0
  306. package/src/integrations/store.ts +103 -0
  307. package/src/integrations/writer-lock.ts +98 -0
  308. package/src/integrations/writer.ts +715 -0
  309. package/src/lab/artifacts/sanitize.ts +586 -0
  310. package/src/lab/artifacts/secure-fs.ts +475 -0
  311. package/src/lab/artifacts/store.ts +310 -0
  312. package/src/lab/automation/budgets.ts +78 -0
  313. package/src/lab/automation/config-persistence.ts +256 -0
  314. package/src/lab/automation/constants.ts +39 -0
  315. package/src/lab/automation/cooldown.ts +103 -0
  316. package/src/lab/automation/dispatch.ts +211 -0
  317. package/src/lab/automation/index.ts +13 -0
  318. package/src/lab/automation/orchestrator.ts +499 -0
  319. package/src/lab/automation/persistence.ts +512 -0
  320. package/src/lab/automation/planner.ts +371 -0
  321. package/src/lab/automation/policy.ts +136 -0
  322. package/src/lab/automation/queue.ts +191 -0
  323. package/src/lab/automation/recovery.ts +24 -0
  324. package/src/lab/automation/route-context.ts +21 -0
  325. package/src/lab/automation/run-key.ts +44 -0
  326. package/src/lab/automation/runs-query.ts +34 -0
  327. package/src/lab/automation/types.ts +160 -0
  328. package/src/lab/conformance/assertion.ts +325 -0
  329. package/src/lab/conformance/digest.ts +22 -0
  330. package/src/lab/conformance/executor.ts +741 -0
  331. package/src/lab/conformance/fixture-provider.ts +27 -0
  332. package/src/lab/conformance/fixtures/live-v1-cases.json +175 -0
  333. package/src/lab/conformance/fixtures/protocol-v1-cases.json +461 -0
  334. package/src/lab/conformance/harness-budget.ts +47 -0
  335. package/src/lab/conformance/index.ts +5 -0
  336. package/src/lab/conformance/jcs.ts +64 -0
  337. package/src/lab/conformance/json-pointer.ts +39 -0
  338. package/src/lab/conformance/manifest.ts +180 -0
  339. package/src/lab/conformance/mcp-stub.ts +179 -0
  340. package/src/lab/conformance/negative-controls.ts +164 -0
  341. package/src/lab/conformance/observation.ts +355 -0
  342. package/src/lab/conformance/runner.ts +68 -0
  343. package/src/lab/conformance/sse-normalize.ts +59 -0
  344. package/src/lab/conformance/suite-manifest.ts +78 -0
  345. package/src/lab/conformance/types.ts +214 -0
  346. package/src/lab/constants.ts +126 -0
  347. package/src/lab/digest.ts +64 -0
  348. package/src/lab/events/errors.ts +9 -0
  349. package/src/lab/events/limits.ts +117 -0
  350. package/src/lab/events/types.ts +229 -0
  351. package/src/lab/events/validate.ts +781 -0
  352. package/src/lab/fabric/constants.ts +40 -0
  353. package/src/lab/fabric/executor.ts +492 -0
  354. package/src/lab/fabric/index.ts +80 -0
  355. package/src/lab/fabric/manifest.ts +222 -0
  356. package/src/lab/fabric/observe.ts +489 -0
  357. package/src/lab/fabric/patch.ts +79 -0
  358. package/src/lab/fabric/producer-child.ts +139 -0
  359. package/src/lab/fabric/producer-isolate.ts +276 -0
  360. package/src/lab/fabric/producer-protocol.ts +61 -0
  361. package/src/lab/fabric/scratch.ts +439 -0
  362. package/src/lab/fabric/subject.ts +106 -0
  363. package/src/lab/fabric/types.ts +134 -0
  364. package/src/lab/fabric/verifier.ts +98 -0
  365. package/src/lab/index.ts +54 -0
  366. package/src/lab/ledger/artifact-refs.ts +127 -0
  367. package/src/lab/ledger/invalidation.ts +136 -0
  368. package/src/lab/ledger/purge.ts +310 -0
  369. package/src/lab/ledger/store.ts +532 -0
  370. package/src/lab/live/credential-lease.ts +53 -0
  371. package/src/lab/live/destination.ts +155 -0
  372. package/src/lab/live/executor.ts +336 -0
  373. package/src/lab/live/inert-tools.ts +56 -0
  374. package/src/lab/live/manifest.ts +85 -0
  375. package/src/lab/live/mcp-loopback.ts +57 -0
  376. package/src/lab/live/runner.ts +19 -0
  377. package/src/lab/live/sandbox.ts +61 -0
  378. package/src/lab/live/suite-manifest.ts +41 -0
  379. package/src/lab/live/transport.ts +118 -0
  380. package/src/lab/live/types.ts +197 -0
  381. package/src/lab/observe/from-conformance.ts +301 -0
  382. package/src/lab/observe/from-live.ts +117 -0
  383. package/src/lab/paths.ts +153 -0
  384. package/src/lab/projection/rebuild.ts +495 -0
  385. package/src/lab/projection/schema.ts +135 -0
  386. package/src/lab/projection/verdicts.ts +474 -0
  387. package/src/lab/projection/verification.ts +412 -0
  388. package/src/lab/public/bundle.ts +217 -0
  389. package/src/lab/public/community-authority.ts +175 -0
  390. package/src/lab/public/community-files.ts +29 -0
  391. package/src/lab/public/community.ts +479 -0
  392. package/src/lab/public/file-safety.ts +155 -0
  393. package/src/lab/public/ids.ts +26 -0
  394. package/src/lab/public/index.ts +16 -0
  395. package/src/lab/public/mutation-lock.ts +424 -0
  396. package/src/lab/public/operator.ts +353 -0
  397. package/src/lab/public/origin-purge.ts +79 -0
  398. package/src/lab/public/origin.ts +203 -0
  399. package/src/lab/public/privacy.ts +143 -0
  400. package/src/lab/public/private-file.ts +261 -0
  401. package/src/lab/public/project.ts +124 -0
  402. package/src/lab/public/purge-test-fault.ts +21 -0
  403. package/src/lab/public/purge.ts +223 -0
  404. package/src/lab/public/registry.ts +44 -0
  405. package/src/lab/public/revocation.ts +252 -0
  406. package/src/lab/public/signature.ts +243 -0
  407. package/src/lab/public/storage.ts +105 -0
  408. package/src/lab/public/strict-json.ts +206 -0
  409. package/src/lab/public/time.ts +26 -0
  410. package/src/lab/public/types.ts +172 -0
  411. package/src/lab/public/validate.ts +391 -0
  412. package/src/lab/query/catalog.ts +101 -0
  413. package/src/lab/query/connection.ts +107 -0
  414. package/src/lab/query/constants.ts +4 -0
  415. package/src/lab/query/cursor.ts +132 -0
  416. package/src/lab/query/dto-map.ts +277 -0
  417. package/src/lab/query/errors.ts +22 -0
  418. package/src/lab/query/freshness.ts +53 -0
  419. package/src/lab/query/index.ts +45 -0
  420. package/src/lab/query/latest-observation.ts +59 -0
  421. package/src/lab/query/passive-production.ts +159 -0
  422. package/src/lab/query/queries.ts +444 -0
  423. package/src/lab/query/types.ts +266 -0
  424. package/src/lab/subject/behavior-fingerprint.ts +77 -0
  425. package/src/lab/subject/installation-salt.ts +112 -0
  426. package/src/lab/subject/protocol-subject.ts +80 -0
  427. package/src/lab/subject/route-subject.ts +74 -0
  428. package/src/lib/admin-secrets.ts +24 -0
  429. package/src/lib/app-owned-memory-stores.ts +22 -0
  430. package/src/lib/bounded-body.ts +167 -11
  431. package/src/lib/bun-runtime.ts +125 -12
  432. package/src/lib/bun-stream-caps.ts +13 -9
  433. package/src/lib/codex-restart-contract.ts +120 -0
  434. package/src/lib/config-ownership.ts +26 -2
  435. package/src/lib/destination-policy.ts +65 -1
  436. package/src/lib/errors.ts +80 -5
  437. package/src/lib/fabric-task-execution-authority.ts +7 -0
  438. package/src/lib/fabric-task-host.ts +29 -0
  439. package/src/lib/lab-activation.ts +223 -0
  440. package/src/lib/lab-live-execution-authority.ts +13 -0
  441. package/src/lib/lab-live-host.ts +30 -0
  442. package/src/lib/lab-live-pinned-sender.ts +56 -0
  443. package/src/lib/lab-live-route-production.ts +130 -0
  444. package/src/lib/lab-passive-linker-registration.ts +26 -0
  445. package/src/lib/local-management-attestation.ts +51 -0
  446. package/src/lib/local-management-capability.ts +100 -0
  447. package/src/lib/local-provider-reload-contract.ts +100 -0
  448. package/src/lib/optional-shutdown-hooks.ts +57 -0
  449. package/src/lib/package-tree-integrity.ts +101 -0
  450. package/src/lib/pinned-http.ts +145 -26
  451. package/src/lib/process-control.ts +6 -2
  452. package/src/lib/provider-outbound.ts +49 -9
  453. package/src/lib/redact.ts +419 -3
  454. package/src/lib/self-launch-argv.ts +15 -0
  455. package/src/lib/server-resource-ownership.ts +71 -0
  456. package/src/lib/service-secrets.ts +15 -0
  457. package/src/lib/shadow-call.ts +35 -4
  458. package/src/lib/sse-decoder.ts +41 -0
  459. package/src/lib/state-store-registrations.ts +10 -2
  460. package/src/lib/system-restart-contract.ts +73 -0
  461. package/src/lib/token-estimate.ts +19 -2
  462. package/src/lib/tool-argument-integers.ts +253 -0
  463. package/src/lib/translator-budget.ts +44 -0
  464. package/src/lib/upstream-http-version.ts +57 -0
  465. package/src/lib/upstream-reachability.ts +95 -0
  466. package/src/lib/upstream-retry.ts +156 -3
  467. package/src/lib/windows-atomic-replace.ts +155 -0
  468. package/src/lib/windows-elevation.ts +70 -2
  469. package/src/lib/windows-secret-acl.ts +409 -69
  470. package/src/lib/windows-service-wrappers.ts +72 -0
  471. package/src/lib/windows-text.ts +106 -0
  472. package/src/lib/windows-user-principal.ts +341 -0
  473. package/src/lib/winsw.ts +33 -5
  474. package/src/oauth/account-import/google-antigravity-adapter.ts +74 -0
  475. package/src/oauth/account-import/index.ts +15 -0
  476. package/src/oauth/account-import/parser.ts +83 -0
  477. package/src/oauth/account-import/registry.ts +18 -0
  478. package/src/oauth/account-import/service.ts +75 -0
  479. package/src/oauth/account-import/types.ts +91 -0
  480. package/src/oauth/anthropic.ts +12 -1
  481. package/src/oauth/callback-server.ts +30 -4
  482. package/src/oauth/chatgpt.ts +12 -1
  483. package/src/oauth/cline.ts +203 -0
  484. package/src/oauth/command-code.ts +239 -0
  485. package/src/oauth/cursor.ts +46 -5
  486. package/src/oauth/generic-account-failover.ts +231 -0
  487. package/src/oauth/google-antigravity.ts +35 -3
  488. package/src/oauth/health.ts +20 -12
  489. package/src/oauth/index.ts +462 -71
  490. package/src/oauth/key-providers.ts +25 -0
  491. package/src/oauth/kimi.ts +25 -3
  492. package/src/oauth/kiro.ts +95 -6
  493. package/src/oauth/local-token-detect.ts +13 -2
  494. package/src/oauth/log.ts +3 -1
  495. package/src/oauth/login-cli.ts +88 -28
  496. package/src/oauth/nous.ts +798 -0
  497. package/src/oauth/open-browser-choice.ts +26 -0
  498. package/src/oauth/store.ts +133 -26
  499. package/src/oauth/token-guardian.ts +9 -3
  500. package/src/oauth/types.ts +15 -0
  501. package/src/pi/models.ts +2 -2
  502. package/src/providers/alibaba-region-migration.ts +1 -1
  503. package/src/providers/antigravity-models.ts +521 -31
  504. package/src/providers/auto-compact-budget.ts +65 -0
  505. package/src/providers/base-url-choices.ts +10 -0
  506. package/src/providers/codex-capacity.ts +292 -0
  507. package/src/providers/command-code-efforts.ts +176 -0
  508. package/src/providers/context-cap.ts +26 -8
  509. package/src/providers/cursor-pool.ts +72 -0
  510. package/src/providers/default-aliases.ts +65 -0
  511. package/src/providers/derive.ts +281 -6
  512. package/src/providers/fastwire.ts +507 -0
  513. package/src/providers/free-directory.ts +10 -7
  514. package/src/providers/google-vertex-location.ts +14 -0
  515. package/src/providers/key-failover.ts +71 -3
  516. package/src/providers/label.ts +35 -2
  517. package/src/providers/model-discovery-limits.ts +16 -0
  518. package/src/providers/model-discovery.ts +115 -22
  519. package/src/providers/model-presets.ts +119 -0
  520. package/src/providers/model-rename-migration.ts +255 -0
  521. package/src/providers/model-rename-startup.ts +28 -0
  522. package/src/providers/new-model-policy.ts +146 -0
  523. package/src/providers/openai-sidecar.ts +72 -4
  524. package/src/providers/openai-tier-startup.ts +31 -2
  525. package/src/providers/openai-tiers.ts +119 -4
  526. package/src/providers/openai-virtual-models.ts +1 -0
  527. package/src/providers/opencode-zen-rate-limit.ts +102 -0
  528. package/src/providers/provider-id-rewrite.ts +30 -0
  529. package/src/providers/quota.ts +1379 -40
  530. package/src/providers/registry.ts +1545 -112
  531. package/src/providers/request-pacing.ts +310 -0
  532. package/src/providers/service-tier.ts +277 -0
  533. package/src/providers/slug-codec.ts +94 -6
  534. package/src/providers/static-model-discovery.ts +86 -0
  535. package/src/providers/xai-responses-opt-in.ts +15 -0
  536. package/src/providers/xai-transport.ts +32 -4
  537. package/src/reasoning-effort.ts +68 -3
  538. package/src/responses/apply-patch-envelope.ts +63 -0
  539. package/src/responses/code-mode-helper-compat.ts +50 -0
  540. package/src/responses/compaction.ts +26 -1
  541. package/src/responses/custom-tool-compat.ts +384 -0
  542. package/src/responses/hosted-tool-policy.ts +9 -0
  543. package/src/responses/namespace-tool-compat.ts +435 -0
  544. package/src/responses/parser.ts +225 -38
  545. package/src/responses/provider-continuation.ts +98 -0
  546. package/src/responses/provider-opaque-metadata.ts +73 -0
  547. package/src/responses/reasoning-envelope.ts +9 -1
  548. package/src/responses/reasoning-replay-cache.ts +504 -0
  549. package/src/responses/schema.ts +12 -2
  550. package/src/responses/spill-store.ts +75 -10
  551. package/src/responses/state.ts +659 -31
  552. package/src/responses/thought-signature-replay.ts +364 -0
  553. package/src/responses/tool-search-compat.ts +301 -0
  554. package/src/responses/truncated-stop-reason.ts +60 -0
  555. package/src/responses/turn-termination.ts +107 -0
  556. package/src/router.ts +405 -31
  557. package/src/routing/analytics.ts +379 -0
  558. package/src/routing/capability.ts +244 -0
  559. package/src/routing/compatibility/assemble.ts +73 -0
  560. package/src/routing/compatibility/behavior.ts +278 -0
  561. package/src/routing/compatibility/catalog.ts +99 -0
  562. package/src/routing/compatibility/endpoint.ts +52 -0
  563. package/src/routing/compatibility/lab-evidence-provider.ts +130 -0
  564. package/src/routing/compatibility/policy.ts +181 -0
  565. package/src/routing/compatibility/provider-slot.ts +56 -0
  566. package/src/routing/compatibility/reader.ts +110 -0
  567. package/src/routing/compatibility/subject.ts +191 -0
  568. package/src/routing/compatibility/types.ts +64 -0
  569. package/src/routing/compatibility/version.ts +104 -0
  570. package/src/routing/cost.ts +77 -0
  571. package/src/routing/evaluator.ts +495 -0
  572. package/src/routing/health.ts +412 -0
  573. package/src/routing/history/cursor.ts +43 -0
  574. package/src/routing/history/indexer.ts +605 -0
  575. package/src/routing/history/schema.ts +72 -0
  576. package/src/routing/profile-namespace.ts +15 -0
  577. package/src/routing/profile.ts +547 -0
  578. package/src/routing/quota.ts +155 -0
  579. package/src/routing/request-evidence.ts +45 -0
  580. package/src/routing/trace.ts +776 -0
  581. package/src/server/adapter-resolve.ts +2 -29
  582. package/src/server/auth-cors.ts +289 -48
  583. package/src/server/background-lifecycle.ts +182 -0
  584. package/src/server/chat-completions.ts +154 -72
  585. package/src/server/chat-native-sse.ts +331 -0
  586. package/src/server/chat-native.ts +445 -0
  587. package/src/server/claude-messages.ts +160 -44
  588. package/src/server/direct-local-http.ts +347 -0
  589. package/src/server/effort-policy.ts +18 -0
  590. package/src/server/github-copilot-responses-repair.ts +338 -0
  591. package/src/server/gui-static.ts +39 -10
  592. package/src/server/images.ts +94 -12
  593. package/src/server/index.ts +1021 -190
  594. package/src/server/lifecycle.ts +336 -14
  595. package/src/server/live.ts +136 -17
  596. package/src/server/local-management-read-client.ts +90 -0
  597. package/src/server/local-provider-reload-client.ts +137 -0
  598. package/src/server/management/agent-settings-routes.ts +433 -116
  599. package/src/server/management/api-key-usage.ts +31 -5
  600. package/src/server/management/body.ts +6 -0
  601. package/src/server/management/codex-prompt-routes.ts +570 -0
  602. package/src/server/management/combo-routes.ts +64 -25
  603. package/src/server/management/config-routes.ts +511 -51
  604. package/src/server/management/context.ts +89 -2
  605. package/src/server/management/integration-routes.ts +498 -0
  606. package/src/server/management/lab-automation-routes.ts +206 -0
  607. package/src/server/management/lab-routes.ts +563 -0
  608. package/src/server/management/logs-usage-routes.ts +127 -52
  609. package/src/server/management/model-routes.ts +461 -133
  610. package/src/server/management/model-rows.ts +167 -0
  611. package/src/server/management/native-integration-routes.ts +770 -0
  612. package/src/server/management/oauth-account-routes.ts +90 -8
  613. package/src/server/management/provider-capability-config.ts +48 -0
  614. package/src/server/management/provider-routes.ts +836 -159
  615. package/src/server/management/request-history-routes.ts +191 -0
  616. package/src/server/management/route-registry.ts +332 -0
  617. package/src/server/management/routing-analytics-routes.ts +74 -0
  618. package/src/server/management/routing-profile-routes.ts +380 -0
  619. package/src/server/management/shared.ts +27 -11
  620. package/src/server/management/sidebar-routes.ts +47 -31
  621. package/src/server/management/storage-log-guard-routes.ts +186 -0
  622. package/src/server/management/sync-response.ts +69 -0
  623. package/src/server/management/system-restart.ts +289 -33
  624. package/src/server/management/system-routes.ts +77 -0
  625. package/src/server/management/usage-summary-cache.ts +9 -1
  626. package/src/server/management/vision-sidecar-options.ts +167 -0
  627. package/src/server/management/web-search-sidecar-options.ts +120 -0
  628. package/src/server/management-api.ts +126 -23
  629. package/src/server/management-auth.ts +220 -5
  630. package/src/server/passive-route-linker.ts +66 -0
  631. package/src/server/port-reclaim.ts +1 -1
  632. package/src/server/ports.ts +41 -1
  633. package/src/server/proxy-liveness.ts +161 -10
  634. package/src/server/readiness.ts +99 -0
  635. package/src/server/relay-eager.ts +221 -54
  636. package/src/server/relay.ts +479 -87
  637. package/src/server/request-decompress.ts +113 -6
  638. package/src/server/request-log-conversation.ts +51 -0
  639. package/src/server/request-log.ts +298 -31
  640. package/src/server/responses/agent-task-recovery-cache.ts +143 -0
  641. package/src/server/responses/agent-task-recovery.ts +465 -0
  642. package/src/server/responses/codex-auth-error.ts +55 -0
  643. package/src/server/responses/collaboration.ts +239 -36
  644. package/src/server/responses/combo-stream-preflight.ts +178 -0
  645. package/src/server/responses/compact.ts +444 -72
  646. package/src/server/responses/core.ts +3579 -436
  647. package/src/server/responses/empty-completion-guard.ts +333 -0
  648. package/src/server/responses/encrypted-payload.ts +62 -39
  649. package/src/server/responses/fetch-helpers.ts +89 -99
  650. package/src/server/responses/input-admission.ts +187 -0
  651. package/src/server/responses/pacing-overload.ts +13 -0
  652. package/src/server/responses/passthrough-error.ts +33 -9
  653. package/src/server/responses/policy-fallback.ts +178 -0
  654. package/src/server/responses/responses-field-backfill.ts +343 -0
  655. package/src/server/responses/terminal-guard.ts +26 -5
  656. package/src/server/responses/upstream-error.ts +5 -0
  657. package/src/server/responses/ws-upstream.ts +381 -0
  658. package/src/server/responses-custom-tool-repair.ts +363 -0
  659. package/src/server/responses-item-id-repair.ts +54 -6
  660. package/src/server/responses-json-events.ts +90 -0
  661. package/src/server/responses-model-rewrite.ts +29 -0
  662. package/src/server/responses-reasoning-summary-rewrite.ts +178 -0
  663. package/src/server/responses-snapshot-repair.ts +621 -0
  664. package/src/server/responses-terminal-repair.ts +363 -0
  665. package/src/server/responses-tool-search-repair.ts +267 -0
  666. package/src/server/responses-undeclared-tool-guard.ts +376 -0
  667. package/src/server/responses.ts +18 -2
  668. package/src/server/search.ts +78 -13
  669. package/src/server/sse-frame-buffer.ts +319 -0
  670. package/src/server/sse-payload-rewrite.ts +110 -22
  671. package/src/server/startup-action-control.ts +8 -1
  672. package/src/server/startup-health-cache.ts +19 -1
  673. package/src/server/system-env.ts +80 -9
  674. package/src/server/ws-bridge.ts +53 -40
  675. package/src/service-manager-probe.ts +892 -0
  676. package/src/service.ts +1153 -94
  677. package/src/sidecar/auth.ts +92 -0
  678. package/src/sidecar/candidates.ts +83 -0
  679. package/src/storage/cleanup.ts +2 -2
  680. package/src/storage/policy-job.ts +14 -4
  681. package/src/storage/policy.ts +88 -23
  682. package/src/storage/scanner.ts +1 -1
  683. package/src/storage/worker-lifecycle.ts +14 -14
  684. package/src/tray/windows-tray.ps1 +83 -9
  685. package/src/tray/windows.ts +43 -16
  686. package/src/types/accounts.ts +37 -0
  687. package/src/types/config.ts +911 -0
  688. package/src/types/provider.ts +614 -0
  689. package/src/types/request.ts +397 -0
  690. package/src/types/tools.ts +236 -0
  691. package/src/types/wire.ts +80 -0
  692. package/src/types.ts +107 -1237
  693. package/src/update/index.ts +37 -23
  694. package/src/update/job.ts +445 -68
  695. package/src/update/notify.ts +12 -6
  696. package/src/update/npm-cache-preflight.d.mts +47 -0
  697. package/src/update/npm-cache-preflight.mjs +201 -0
  698. package/src/update/transactional-install.d.mts +22 -0
  699. package/src/update/transactional-install.mjs +266 -0
  700. package/src/usage/cost.ts +0 -0
  701. package/src/usage/expected-prices.ts +287 -16
  702. package/src/usage/log.ts +666 -50
  703. package/src/usage/summary.ts +388 -17
  704. package/src/usage/user-cost-overlay-reconciler.ts +313 -0
  705. package/src/usage/user-cost-overlays.ts +314 -0
  706. package/src/vision/anthropic-describe.ts +10 -6
  707. package/src/vision/backends.ts +97 -0
  708. package/src/vision/describe.ts +27 -16
  709. package/src/vision/eligibility.ts +250 -0
  710. package/src/vision/index.ts +238 -24
  711. package/src/vision/reasoning.ts +55 -0
  712. package/src/vision/routed-describe.ts +175 -0
  713. package/src/vision/timeout-bounds.ts +9 -0
  714. package/src/web-search/anthropic-executor.ts +13 -7
  715. package/src/web-search/backends.ts +108 -0
  716. package/src/web-search/exa-executor.ts +119 -0
  717. package/src/web-search/executor.ts +21 -6
  718. package/src/web-search/gemini-executor.ts +141 -0
  719. package/src/web-search/index.ts +150 -15
  720. package/src/web-search/loop.ts +284 -54
  721. package/src/web-search/parse.ts +125 -30
  722. package/src/web-search/sources.ts +60 -0
  723. package/src/web-search/xai-executor.ts +219 -0
  724. package/gui/dist/assets/index-DTpMHS4F.js +0 -67
  725. package/gui/dist/assets/index-ZNVDE3C7.css +0 -1
  726. package/gui/dist/provider-icons/antigravity.svg +0 -1
  727. package/gui/dist/provider-icons/claude.svg +0 -1
  728. package/gui/dist/provider-icons/copilot.svg +0 -1
  729. package/gui/dist/provider-icons/cursor.svg +0 -2
  730. package/gui/dist/provider-icons/gemini.svg +0 -1
  731. package/gui/dist/provider-icons/grok-color.svg +0 -1
  732. package/gui/dist/provider-icons/kiro.svg +0 -14
  733. package/src/cli/internal-dispatch.ts +0 -20
@@ -1,5 +1,5 @@
1
1
  import { createHash, randomUUID } from "node:crypto";
2
- import { copyFileSync, existsSync, mkdirSync, readFileSync } from "node:fs";
2
+ import { copyFileSync, existsSync, mkdirSync, readFileSync, unlinkSync } from "node:fs";
3
3
  import { homedir } from "node:os";
4
4
  import { join } from "node:path";
5
5
  import { atomicWriteFile } from "../config";
@@ -10,7 +10,7 @@ import {
10
10
  renderDesktopProfile,
11
11
  type DesktopProfileModel,
12
12
  } from "./desktop-profile";
13
- import { nativeOpenAiContextWindow } from "../codex/catalog";
13
+ import { nativeOpenAiContextWindow, type NativeContextLimitsInput } from "../codex/catalog";
14
14
  import { assertDesktop3pModelsValid } from "./desktop-3p-guard";
15
15
 
16
16
  export interface Desktop3pModelEntry {
@@ -103,6 +103,45 @@ interface Desktop3pMetadata {
103
103
  [key: string]: unknown;
104
104
  }
105
105
 
106
+ export type Desktop3pLibraryKind =
107
+ | "not_installed"
108
+ | "standard"
109
+ | "gateway_ours"
110
+ | "gateway_drifted"
111
+ | "foreign"
112
+ | "no_owned_state"
113
+ | "broken"
114
+ | "unsafe";
115
+
116
+ export interface Desktop3pLibraryInspection {
117
+ kind: Desktop3pLibraryKind;
118
+ libraryPath: string;
119
+ selectedProfilePath: string | null;
120
+ appliedId: string | null;
121
+ /** Paths of opencodex-owned rows that are not selected by Desktop. */
122
+ residualPaths: string[];
123
+ /** Bounded reason code; never includes metadata or profile contents. */
124
+ reason?: "metadata_unreadable" | "unsafe_applied_id" | "invalid_owned_profile";
125
+ fingerprint?: string;
126
+ /**
127
+ * Whether Desktop's applied selection is our owned entry, by ID match alone.
128
+ * `null` = undeterminable (no metadata, unreadable metadata, or no appliedId);
129
+ * a readable appliedId with no owned entry is a KNOWN false, not unknown.
130
+ * Deliberately independent of profile-file health: the status contract
131
+ * predates this inspector and callers render tri-state.
132
+ */
133
+ ownedProfileActive: boolean | null;
134
+ }
135
+
136
+ export interface Desktop3pRemovalResult {
137
+ ok: boolean;
138
+ changed: boolean;
139
+ kind: "removed" | "noop" | "cleanup_incomplete" | "unsafe" | "write_failed";
140
+ libraryPath: string;
141
+ residualPaths?: string[];
142
+ reason?: string;
143
+ }
144
+
106
145
  let desktop3pRegistry = new Map<string, string>();
107
146
  let desktop3pAliasesByRoute = new Map<string, string>();
108
147
 
@@ -152,6 +191,7 @@ function collectDesktop3pModels(
152
191
  nativeSlugs: string[],
153
192
  routedModels: Array<Desktop3pRoutedModel>,
154
193
  profile?: OcxClaudeDesktopProfile,
194
+ nativeContextCap?: NativeContextLimitsInput,
155
195
  ): { models: Desktop3pModelEntry[]; registry: Map<string, string> } {
156
196
  const registry = new Map<string, string>();
157
197
  const models: Desktop3pModelEntry[] = [];
@@ -160,7 +200,7 @@ function collectDesktop3pModels(
160
200
  // Desktop DTO uses, so a native 1M/372k model resolves identically in the written
161
201
  // config and on the dashboard.
162
202
  ...nativeSlugs.map(id => {
163
- const contextWindow = nativeOpenAiContextWindow(id);
203
+ const contextWindow = nativeOpenAiContextWindow(id, nativeContextCap);
164
204
  return { provider: "native", id, ...(contextWindow !== undefined ? { contextWindow } : {}) };
165
205
  }),
166
206
  ...routedModels,
@@ -253,8 +293,9 @@ export function buildDesktop3pRegistry(
253
293
  nativeSlugs: string[],
254
294
  routedModels: Array<Desktop3pRoutedModel>,
255
295
  profile?: OcxClaudeDesktopProfile,
296
+ nativeContextCap?: NativeContextLimitsInput,
256
297
  ): Map<string, string> {
257
- const { registry } = collectDesktop3pModels(nativeSlugs, routedModels, profile);
298
+ const { registry } = collectDesktop3pModels(nativeSlugs, routedModels, profile, nativeContextCap);
258
299
  desktop3pRegistry = registry;
259
300
  return registry;
260
301
  }
@@ -264,8 +305,9 @@ export function generateDesktop3pModels(
264
305
  nativeSlugs: string[],
265
306
  routedModels: Array<Desktop3pRoutedModel>,
266
307
  profile?: OcxClaudeDesktopProfile,
308
+ nativeContextCap?: NativeContextLimitsInput,
267
309
  ): Desktop3pModelEntry[] {
268
- const { models, registry } = collectDesktop3pModels(nativeSlugs, routedModels, profile);
310
+ const { models, registry } = collectDesktop3pModels(nativeSlugs, routedModels, profile, nativeContextCap);
269
311
  desktop3pRegistry = registry;
270
312
  return models;
271
313
  }
@@ -295,6 +337,7 @@ export function generateDesktop3pConfig(
295
337
  apiKey = "ocx",
296
338
  mode: Desktop3pConfigMode = "static",
297
339
  profile?: OcxClaudeDesktopProfile,
340
+ nativeContextCap?: NativeContextLimitsInput,
298
341
  ): object {
299
342
  const base = {
300
343
  inferenceProvider: "gateway",
@@ -304,14 +347,14 @@ export function generateDesktop3pConfig(
304
347
  };
305
348
  if (mode === "discovery") {
306
349
  // Build/refresh the decode registry even though no static list is emitted.
307
- buildDesktop3pRegistry(nativeSlugs, routedModels, profile);
350
+ buildDesktop3pRegistry(nativeSlugs, routedModels, profile, nativeContextCap);
308
351
  return { ...base, modelDiscoveryEnabled: true };
309
352
  }
310
353
  return {
311
354
  ...base,
312
355
  modelDiscoveryEnabled: mode === "hybrid",
313
356
  inferenceModels: (() => {
314
- const models = generateDesktop3pModels(nativeSlugs, routedModels, profile);
357
+ const models = generateDesktop3pModels(nativeSlugs, routedModels, profile, nativeContextCap);
315
358
  // Fail loud at the write boundary rather than ship a config Desktop rejects:
316
359
  // the output counterpart of the request-path guards.
317
360
  assertDesktop3pModelsValid(models);
@@ -327,6 +370,195 @@ function parseMetadata(path: string): Desktop3pMetadata {
327
370
  return { ...parsed, entries: parsed.entries };
328
371
  }
329
372
 
373
+ const SAFE_DESKTOP_PROFILE_ID = /^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/;
374
+
375
+ function isRecord(value: unknown): value is Record<string, unknown> {
376
+ return typeof value === "object" && value !== null && !Array.isArray(value);
377
+ }
378
+
379
+ function isOwnedDesktopEntry(entry: Desktop3pMetadataEntry | undefined): boolean {
380
+ return entry?.name === "opencodex" || entry?.name === "opencodex-standard";
381
+ }
382
+
383
+ /** A gateway row is removable; the selected standard row must always remain. */
384
+ function isOwnedDesktopGatewayEntry(entry: Desktop3pMetadataEntry | undefined): boolean {
385
+ return entry?.name === "opencodex";
386
+ }
387
+
388
+ function profilePath(libraryPath: string, id: string): string {
389
+ return join(libraryPath, `${id}.json`);
390
+ }
391
+
392
+ /**
393
+ * Read Desktop's selected config without changing its library.
394
+ *
395
+ * This is intentionally separate from the eager writer below: status probes must
396
+ * never manufacture a config-library directory on a machine without Desktop.
397
+ */
398
+ export function inspectDesktop3pConfigLibrary(
399
+ options: Desktop3pConfigLibraryOptions & { appliedFingerprint?: string | null } = {},
400
+ ): Desktop3pLibraryInspection {
401
+ const libraryPath = resolveDesktop3pConfigLibraryPath(options);
402
+ if (!existsSync(libraryPath)) {
403
+ return { kind: "not_installed", libraryPath, selectedProfilePath: null, appliedId: null, residualPaths: [], ownedProfileActive: null };
404
+ }
405
+
406
+ const metadataPath = join(libraryPath, "_meta.json");
407
+ if (!existsSync(metadataPath)) {
408
+ return { kind: "no_owned_state", libraryPath, selectedProfilePath: null, appliedId: null, residualPaths: [], ownedProfileActive: null };
409
+ }
410
+
411
+ let metadata: Desktop3pMetadata;
412
+ try {
413
+ metadata = parseMetadata(metadataPath);
414
+ } catch {
415
+ return {
416
+ kind: "unsafe", libraryPath, selectedProfilePath: null, appliedId: null, residualPaths: [], reason: "metadata_unreadable", ownedProfileActive: null,
417
+ };
418
+ }
419
+ const appliedId = typeof metadata.appliedId === "string" ? metadata.appliedId : null;
420
+ if (appliedId === null) {
421
+ return { kind: "no_owned_state", libraryPath, selectedProfilePath: null, appliedId: null, residualPaths: [], ownedProfileActive: null };
422
+ }
423
+ const selected = metadata.entries.find(entry => entry?.id === appliedId);
424
+ // A readable appliedId with no owned entry is a KNOWN false, not unknown.
425
+ const ownedProfileActive = isOwnedDesktopEntry(selected);
426
+ if (!SAFE_DESKTOP_PROFILE_ID.test(appliedId)) {
427
+ return {
428
+ kind: "unsafe", libraryPath, selectedProfilePath: null, appliedId, residualPaths: [], reason: "unsafe_applied_id", ownedProfileActive,
429
+ };
430
+ }
431
+
432
+ const selectedProfilePath = profilePath(libraryPath, appliedId);
433
+ const residualPaths = metadata.entries
434
+ .filter(entry => isOwnedDesktopGatewayEntry(entry) && entry.id !== appliedId && SAFE_DESKTOP_PROFILE_ID.test(entry.id))
435
+ .flatMap(entry => [profilePath(libraryPath, entry.id), `${profilePath(libraryPath, entry.id)}.bak`])
436
+ .filter(existsSync);
437
+ if (!existsSync(selectedProfilePath)) {
438
+ return { kind: "broken", libraryPath, selectedProfilePath, appliedId, residualPaths, ownedProfileActive };
439
+ }
440
+
441
+ let profile: Record<string, unknown>;
442
+ let fingerprint: string;
443
+ try {
444
+ const source = readFileSync(selectedProfilePath, "utf8");
445
+ const parsed = JSON.parse(source) as unknown;
446
+ if (!isRecord(parsed)) return { kind: "broken", libraryPath, selectedProfilePath, appliedId, residualPaths, ownedProfileActive };
447
+ profile = parsed;
448
+ fingerprint = createHash("sha256").update(source).digest("hex").slice(0, 16);
449
+ } catch {
450
+ return { kind: "broken", libraryPath, selectedProfilePath, appliedId, residualPaths, ownedProfileActive };
451
+ }
452
+ if (!isOwnedDesktopEntry(selected)) {
453
+ return { kind: "foreign", libraryPath, selectedProfilePath, appliedId, residualPaths, fingerprint, ownedProfileActive };
454
+ }
455
+ if (profile.inferenceProvider === undefined) {
456
+ return { kind: "standard", libraryPath, selectedProfilePath, appliedId, residualPaths, fingerprint, ownedProfileActive };
457
+ }
458
+ const validGateway = profile.inferenceProvider === "gateway"
459
+ && profile.inferenceCredentialKind === "static"
460
+ && typeof profile.inferenceGatewayBaseUrl === "string"
461
+ && typeof profile.inferenceGatewayApiKey === "string";
462
+ if (!validGateway) {
463
+ return {
464
+ kind: "unsafe", libraryPath, selectedProfilePath, appliedId, residualPaths, fingerprint, reason: "invalid_owned_profile", ownedProfileActive,
465
+ };
466
+ }
467
+ return {
468
+ kind: options.appliedFingerprint && options.appliedFingerprint === fingerprint ? "gateway_ours" : "gateway_drifted",
469
+ libraryPath, selectedProfilePath, appliedId, residualPaths, fingerprint, ownedProfileActive,
470
+ };
471
+ }
472
+
473
+ /**
474
+ * Select a credential-free standard profile before deleting an owned gateway.
475
+ * The old metadata row remains as a retry locator only until both its profile
476
+ * and backup are absent; successful cleanup removes it in the same operation.
477
+ *
478
+ * `gateway_drifted` is still an owned opencodex gateway (name + valid shape); the
479
+ * fingerprint only says on-disk bytes differ from the last saved marker. Refusing
480
+ * OFF for drift left users unable to disable after a lost `appliedFingerprint`
481
+ * (or any other benign mismatch), while the Integrations card still showed the
482
+ * leftover profile as applied/stale.
483
+ */
484
+ export function removeDesktop3pStandardPivot(
485
+ options: Desktop3pConfigLibraryOptions & {
486
+ appliedFingerprint?: string | null;
487
+ unlink?: (path: string) => void;
488
+ } = {},
489
+ ): Desktop3pRemovalResult {
490
+ const inspected = inspectDesktop3pConfigLibrary(options);
491
+ if (inspected.kind === "not_installed" || inspected.kind === "no_owned_state") {
492
+ return { ok: true, changed: false, kind: "noop", libraryPath: inspected.libraryPath };
493
+ }
494
+ if (inspected.kind === "broken" || inspected.kind === "unsafe") {
495
+ return { ok: false, changed: false, kind: "unsafe", libraryPath: inspected.libraryPath, reason: inspected.reason };
496
+ }
497
+ if (!inspected.appliedId || !SAFE_DESKTOP_PROFILE_ID.test(inspected.appliedId)) {
498
+ return { ok: false, changed: false, kind: "unsafe", libraryPath: inspected.libraryPath, reason: "unsafe_applied_id" };
499
+ }
500
+
501
+ const metadataPath = join(inspected.libraryPath, "_meta.json");
502
+ try {
503
+ const metadata = parseMetadata(metadataPath);
504
+ const selectedId = inspected.appliedId;
505
+ // When Desktop is actively using our gateway (current or drifted), pivot only
506
+ // that selected row first. Any second owned row is residue for a later
507
+ // standard-mode retry; this preserves the selected-row preference after an
508
+ // interrupted cleanup.
509
+ const selectedOwnedGatewayActive =
510
+ inspected.kind === "gateway_ours" || inspected.kind === "gateway_drifted";
511
+ const targetIds = selectedOwnedGatewayActive
512
+ ? [selectedId]
513
+ : metadata.entries
514
+ .filter(isOwnedDesktopGatewayEntry)
515
+ .map(entry => entry.id)
516
+ .filter(id => SAFE_DESKTOP_PROFILE_ID.test(id));
517
+ if (targetIds.length === 0) return { ok: true, changed: false, kind: "noop", libraryPath: inspected.libraryPath };
518
+
519
+ let metadataAfterPivot = metadata;
520
+ if (selectedOwnedGatewayActive) {
521
+ const standardId = randomUUID();
522
+ const standardPath = profilePath(inspected.libraryPath, standardId);
523
+ atomicWriteFile(standardPath, "{}\n");
524
+ const standardEntry: Desktop3pMetadataEntry = { id: standardId, name: "opencodex-standard" };
525
+ metadataAfterPivot = { ...metadata, appliedId: standardId, entries: [...metadata.entries, standardEntry] };
526
+ atomicWriteFile(metadataPath, JSON.stringify(metadataAfterPivot, null, 2) + "\n");
527
+ }
528
+
529
+ const residualPaths: string[] = [];
530
+ for (const id of targetIds) {
531
+ for (const candidate of [profilePath(inspected.libraryPath, id), `${profilePath(inspected.libraryPath, id)}.bak`]) {
532
+ try {
533
+ if (existsSync(candidate)) (options.unlink ?? unlinkSync)(candidate);
534
+ } catch {
535
+ // Only the path is allowed to leave this credential-bearing cleanup boundary.
536
+ }
537
+ if (existsSync(candidate)) residualPaths.push(candidate);
538
+ }
539
+ }
540
+ const ownedResiduePaths = metadataAfterPivot.entries
541
+ .filter(entry => isOwnedDesktopGatewayEntry(entry) && !targetIds.includes(entry.id) && SAFE_DESKTOP_PROFILE_ID.test(entry.id))
542
+ .flatMap(entry => [profilePath(inspected.libraryPath, entry.id), `${profilePath(inspected.libraryPath, entry.id)}.bak`])
543
+ .filter(existsSync);
544
+ if (residualPaths.length > 0 || ownedResiduePaths.length > 0) {
545
+ return {
546
+ ok: false, changed: true, kind: "cleanup_incomplete", libraryPath: inspected.libraryPath,
547
+ residualPaths: [...new Set([...residualPaths, ...ownedResiduePaths])],
548
+ };
549
+ }
550
+ // Do not leave a metadata row pointing at a deleted profile. For a foreign
551
+ // selection this only removes proven opencodex residues; appliedId is kept.
552
+ atomicWriteFile(
553
+ metadataPath,
554
+ JSON.stringify({ ...metadataAfterPivot, entries: metadataAfterPivot.entries.filter(entry => !targetIds.includes(entry.id)) }, null, 2) + "\n",
555
+ );
556
+ return { ok: true, changed: true, kind: "removed", libraryPath: inspected.libraryPath };
557
+ } catch {
558
+ return { ok: false, changed: false, kind: "write_failed", libraryPath: inspected.libraryPath };
559
+ }
560
+ }
561
+
330
562
  /** Write and apply the opencodex config in Claude Desktop 3P's config library. */
331
563
  export function writeDesktop3pConfig(
332
564
  port: number,
@@ -335,6 +567,7 @@ export function writeDesktop3pConfig(
335
567
  apiKey?: string,
336
568
  mode: Desktop3pConfigMode = "static",
337
569
  profile?: OcxClaudeDesktopProfile,
570
+ nativeContextCap?: NativeContextLimitsInput,
338
571
  ): { written: boolean; path: string; reason?: string; fingerprint?: string } {
339
572
  const libraryPath = resolveDesktop3pConfigLibraryPath();
340
573
  const metadataPath = join(libraryPath, "_meta.json");
@@ -343,7 +576,8 @@ export function writeDesktop3pConfig(
343
576
  try {
344
577
  mkdirSync(libraryPath, { recursive: true, mode: 0o700 });
345
578
  const metadata = parseMetadata(metadataPath);
346
- const existing = metadata.entries.find(entry => entry?.name === "opencodex" && typeof entry.id === "string");
579
+ const selected = metadata.entries.find(entry => entry?.id === metadata.appliedId && isOwnedDesktopGatewayEntry(entry));
580
+ const existing = selected ?? metadata.entries.find(entry => isOwnedDesktopGatewayEntry(entry) && typeof entry.id === "string");
347
581
  const id = existing?.id ?? randomUUID();
348
582
  configPath = join(libraryPath, `${id}.json`);
349
583
  const entry: Desktop3pMetadataEntry = existing ? { ...existing, id, name: "opencodex" } : { id, name: "opencodex" };
@@ -351,7 +585,7 @@ export function writeDesktop3pConfig(
351
585
  ? metadata.entries.map(current => current === existing ? entry : current)
352
586
  : [...metadata.entries, entry];
353
587
 
354
- const configJson = JSON.stringify(generateDesktop3pConfig(port, nativeSlugs, routedModels, apiKey, mode, profile), null, 2) + "\n";
588
+ const configJson = JSON.stringify(generateDesktop3pConfig(port, nativeSlugs, routedModels, apiKey, mode, profile, nativeContextCap), null, 2) + "\n";
355
589
  const fingerprint = createHash("sha256").update(configJson).digest("hex").slice(0, 16);
356
590
  const { backupPath } = atomicReplaceDesktopConfig(configPath, configJson);
357
591
  try {
@@ -13,12 +13,21 @@
13
13
  import { mkdirSync, writeFileSync } from "node:fs";
14
14
  import { homedir } from "node:os";
15
15
  import { join } from "node:path";
16
+ import { loadServiceTokenFromFile, serviceApiTokenFilePath } from "../lib/service-secrets";
17
+ import type { OcxConfig } from "../types";
16
18
 
17
19
  export interface GatewayModelRow {
18
20
  id: string;
19
21
  display_name?: string;
20
22
  }
21
23
 
24
+ export interface GatewayModelCacheRefreshOptions {
25
+ timeoutMs?: number;
26
+ configDir?: string;
27
+ admissionConfig?: Pick<OcxConfig, "apiKeys">;
28
+ env?: NodeJS.ProcessEnv;
29
+ }
30
+
22
31
  /** Claude Code config dir (CLAUDE_CONFIG_DIR override honored, like the CLI). */
23
32
  export function claudeConfigDir(): string {
24
33
  const custom = process.env.CLAUDE_CONFIG_DIR;
@@ -45,14 +54,42 @@ export function writeGatewayModelCache(baseUrl: string, models: readonly Gateway
45
54
  }
46
55
  }
47
56
 
57
+ /**
58
+ * Hardened service-token file, the same precedence `ocx opencode` uses. A service
59
+ * install writes the admission token to disk rather than the interactive environment,
60
+ * so an interactive `ocx claude` with neither env token nor configured key would
61
+ * otherwise still get a 401 and keep a stale picker list.
62
+ */
63
+ function serviceFileToken(env: NodeJS.ProcessEnv): string | null {
64
+ const lookup = env.OCX_API_TOKEN_FILE?.trim()
65
+ ? env
66
+ : { ...env, OCX_API_TOKEN_FILE: serviceApiTokenFilePath() };
67
+ return loadServiceTokenFromFile(lookup as Record<string, string | undefined>);
68
+ }
69
+
48
70
  /** Fetch the anthropic-flavor /v1/models from the local proxy and write the cache. */
49
- export async function refreshGatewayModelCacheFromProxy(port: number, timeoutMs = 3_000, configDir?: string): Promise<string | null> {
71
+ export async function refreshGatewayModelCacheFromProxy(
72
+ port: number,
73
+ options: GatewayModelCacheRefreshOptions = {},
74
+ ): Promise<string | null> {
50
75
  try {
76
+ const headers = new Headers({ "anthropic-version": "2023-06-01" });
77
+ // A wildcard/non-loopback listener requires data-plane admission even for a
78
+ // request sent to its local 127.0.0.1 address. Reuse the same dedicated
79
+ // credential domain as /v1/models admission; never place it in Authorization,
80
+ // which can belong to an upstream provider on other data-plane surfaces.
81
+ const envToken = (options.env ?? process.env).OPENCODEX_API_AUTH_TOKEN?.trim();
82
+ const configuredToken = options.admissionConfig?.apiKeys
83
+ ?.find(entry => entry.key.trim().length > 0)
84
+ ?.key.trim();
85
+ const admissionToken = envToken || serviceFileToken(options.env ?? process.env) || configuredToken;
86
+ if (admissionToken) headers.set("x-opencodex-api-key", admissionToken);
87
+
51
88
  // ?ids=cli pins the readable claude-ocx id family deterministically (audit 051
52
89
  // #5): the cache prewrite must not depend on UA sniffing.
53
90
  const res = await fetch(`http://127.0.0.1:${port}/v1/models?limit=1000&ids=cli`, {
54
- headers: { "anthropic-version": "2023-06-01" },
55
- signal: AbortSignal.timeout(timeoutMs),
91
+ headers,
92
+ signal: AbortSignal.timeout(options.timeoutMs ?? 3_000),
56
93
  });
57
94
  if (!res.ok) return null;
58
95
  const body = await res.json() as { data?: unknown };
@@ -63,7 +100,7 @@ export async function refreshGatewayModelCacheFromProxy(port: number, timeoutMs
63
100
  id: m.id as string,
64
101
  display_name: typeof m.display_name === "string" ? m.display_name : undefined,
65
102
  }));
66
- return writeGatewayModelCache(`http://127.0.0.1:${port}`, models, configDir);
103
+ return writeGatewayModelCache(`http://127.0.0.1:${port}`, models, options.configDir);
67
104
  } catch {
68
105
  return null;
69
106
  }
@@ -10,9 +10,11 @@
10
10
  * - top_k is accepted and silently dropped (no Responses equivalent, CCR parity).
11
11
  */
12
12
  import type { OcxClaudeCodeConfig } from "../types";
13
+ import { isAnthropicOutputSchema } from "../adapters/anthropic-output-schema";
13
14
  import { resolveAlias } from "./alias";
14
15
  import { stripOneMillionMarker } from "./context-windows";
15
16
  import { resolveDesktop3pAlias } from "./desktop-3p";
17
+ import { isClaudeWebSearchToolName } from "./outbound";
16
18
  import { createHash } from "node:crypto";
17
19
 
18
20
  export class AnthropicRequestError extends Error {}
@@ -23,7 +25,37 @@ function isRec(v: unknown): v is Rec {
23
25
  return !!v && typeof v === "object" && !Array.isArray(v);
24
26
  }
25
27
 
26
- /** Alias first, then modelMap: exact id, then date-suffix-stripped (`-\d{8}$`), else passthrough. */
28
+ function isClaudeClassifierModel(model: string): boolean {
29
+ const stripped = model.replace(/-\d{8}$/, "");
30
+ return /^claude-opus-[45]/.test(stripped);
31
+ }
32
+
33
+ /**
34
+ * Explicitly configured classifier route for Claude Code Auto Mode safety checks (#1697).
35
+ *
36
+ * Only OPERATOR-DECLARED targets are used: `classifierModel`, then the ordered
37
+ * `classifierFallbacks`. Both are qualified `provider/model` strings the operator chose, so
38
+ * routing them crosses no boundary the operator did not ask for.
39
+ *
40
+ * Deliberately NOT here: inferring a provider from `claudeCode.model`. That value is the
41
+ * injected/default config slot, not the provider the live session actually selected, so it goes
42
+ * stale the moment the user changes the model picker -- and acting on it would silently move a
43
+ * classifier turn onto a provider with its own privacy and billing consequences. Live session
44
+ * affinity needs the request/session state this function does not have; it is tracked as
45
+ * follow-up work rather than approximated from static config.
46
+ */
47
+ function configuredClassifierRoute(cc?: OcxClaudeCodeConfig): string | undefined {
48
+ const explicit = typeof cc?.classifierModel === "string" ? cc.classifierModel.trim() : "";
49
+ if (explicit.length > 0) return explicit;
50
+ if (Array.isArray(cc?.classifierFallbacks)) {
51
+ for (const candidate of cc.classifierFallbacks) {
52
+ if (typeof candidate === "string" && candidate.trim().length > 0) return candidate.trim();
53
+ }
54
+ }
55
+ return undefined;
56
+ }
57
+
58
+ /** Alias first, then modelMap: exact id, then date-suffix-stripped (`-\d{8}$`), then classifier affinity/config, else passthrough. */
27
59
  export function resolveInboundModel(model: string, cc?: OcxClaudeCodeConfig): string {
28
60
  // Defensive: Desktop/CLI strip the [1m] context-variant marker client-side, but a
29
61
  // leaking build must not break alias decode (devlog 138 — the 1M signal is the
@@ -45,6 +77,14 @@ export function resolveInboundModel(model: string, cc?: OcxClaudeCodeConfig): st
45
77
  const stripped = model.replace(/-\d{8}$/, "");
46
78
  const dateless = map[stripped];
47
79
  if (typeof dateless === "string" && dateless.length > 0) return dateless;
80
+
81
+ // Claude Code Auto Mode classifier routing (#1697). Bare classifier checks such as
82
+ // `claude-opus-5` carry no provider, so without this they fall through to defaultProvider --
83
+ // which may not speak Anthropic at all. Only an operator-declared target is used.
84
+ if (isClaudeClassifierModel(model)) {
85
+ const configured = configuredClassifierRoute(cc);
86
+ if (configured) return configured;
87
+ }
48
88
  return model;
49
89
  }
50
90
 
@@ -68,6 +108,17 @@ export function effortFromOutputConfig(outputConfig: unknown): string | undefine
68
108
  return typeof effort === "string" && OUTPUT_CONFIG_EFFORTS.has(effort) ? effort : undefined;
69
109
  }
70
110
 
111
+ function formatFromOutputConfig(outputConfig: unknown): Rec | undefined {
112
+ if (!isRec(outputConfig) || !isRec(outputConfig.format)) return undefined;
113
+ const format = outputConfig.format;
114
+ if (
115
+ format.type !== "json_schema"
116
+ || !isRec(format.schema)
117
+ || !isAnthropicOutputSchema(format.schema)
118
+ ) return undefined;
119
+ return { type: "json_schema", name: "response", schema: format.schema };
120
+ }
121
+
71
122
  function systemToInstructions(system: unknown): string | undefined {
72
123
  if (typeof system === "string") return system.length > 0 ? system : undefined;
73
124
  if (Array.isArray(system)) {
@@ -106,6 +157,10 @@ function toolResultOutput(block: Rec): string | Rec[] {
106
157
  } else if (item.type === "image") {
107
158
  const img = imageBlockToInputImage(item);
108
159
  if (img) out.push(img);
160
+ } else if (item.type === "document") {
161
+ // Same marker as the user-message document case below: the model should see the
162
+ // attachment happened instead of an empty tool output.
163
+ out.push({ type: "input_text", text: `[document${typeof item.title === "string" ? `: ${item.title}` : ""}]` });
109
164
  }
110
165
  }
111
166
  if (isError) out.unshift({ type: "input_text", text: "[tool error]" });
@@ -371,7 +426,12 @@ function toolChoiceToResponses(choice: unknown, body: Rec): void {
371
426
  if (typeof choice.name !== "string" || choice.name.length === 0) {
372
427
  throw new AnthropicRequestError("tool_choice.tool requires a name");
373
428
  }
374
- body.tool_choice = { type: "function", name: choice.name };
429
+ // Anthropic represents hosted WebSearch as a named tool choice, while
430
+ // Responses requires the choice type to match the hosted declaration.
431
+ // Preserve forced-tool intent rather than weakening it to `auto`.
432
+ body.tool_choice = isClaudeWebSearchToolName(choice.name)
433
+ ? { type: "web_search" }
434
+ : { type: "function", name: choice.name };
375
435
  break;
376
436
  default: break;
377
437
  }
@@ -457,6 +517,8 @@ export function anthropicToResponsesTranslation(raw: unknown, cc?: OcxClaudeCode
457
517
  if (Array.isArray(raw.stop_sequences) && raw.stop_sequences.length > 0) {
458
518
  body.stop = raw.stop_sequences.filter((s): s is string => typeof s === "string");
459
519
  }
520
+ const outputConfigFormat = formatFromOutputConfig(raw.output_config);
521
+ if (outputConfigFormat) body.text = { format: outputConfigFormat };
460
522
  let cacheKeySource: ClaudeCacheKeySource = null;
461
523
  if (isRec(raw.metadata) && typeof raw.metadata.user_id === "string") {
462
524
  body.user = raw.metadata.user_id;
@@ -494,7 +556,14 @@ export function anthropicToResponsesTranslation(raw: unknown, cc?: OcxClaudeCode
494
556
  const thinking = raw.thinking;
495
557
  const outputConfigEffort = effortFromOutputConfig(raw.output_config);
496
558
  const thinkingDisabled = isRec(thinking) && thinking.type === "disabled";
497
- if (!thinkingDisabled && (isRec(thinking) || outputConfigEffort !== undefined)) {
559
+ if (thinkingDisabled) {
560
+ // An explicit "disabled" is an instruction, not an absence. Dropping it made this
561
+ // indistinguishable from a request that never mentioned thinking — and for models that
562
+ // think by default, omission means thinking is ON, sharing the caller's max_tokens (#545).
563
+ // `none` is the effort disable sentinel. It is not a valid OpenAI summary
564
+ // value, so do not attach the similarly named internal catalog sentinel.
565
+ body.reasoning = { effort: "none" };
566
+ } else if (isRec(thinking) || outputConfigEffort !== undefined) {
498
567
  const reasoning: Rec = { summary: "auto" };
499
568
  if (outputConfigEffort !== undefined) {
500
569
  // Adaptive wire: /effort arrives as output_config.effort (devlog 080).
@@ -15,10 +15,10 @@
15
15
  * - created_at is a fixed constant; max_input_tokens is authoritative-or-null;
16
16
  * max_tokens is always null (no authoritative output limit exists proxy-side).
17
17
  */
18
- import { catalogModelEfforts, nativeEffortClamp, nativeOpenAiContextWindow, type CatalogModel } from "../codex/catalog";
18
+ import { catalogModelEfforts, nativeEffortClamp, nativeOpenAiContextWindow, nativeOpenAiMaxInputTokens, type CatalogModel, type NativeContextLimitsInput } from "../codex/catalog";
19
19
  import { claudeCodeAlias, claudeCodeNativeAlias } from "./alias";
20
20
  import { desktop3pAlias } from "./desktop-3p";
21
- import { AUTO_CONTEXT_OFF, shouldMarkOneMillion, type AutoContextMode } from "./context-windows";
21
+ import { AUTO_CONTEXT_OFF, type AutoContextMode } from "./context-windows";
22
22
 
23
23
  const MODEL_INFO_CREATED_AT = "2026-01-01T00:00:00Z";
24
24
  const ANTHROPIC_EFFORT_RUNGS = new Set(["low", "medium", "high", "xhigh", "max"]);
@@ -108,32 +108,47 @@ export function buildAnthropicModelInfos(
108
108
  auto: AutoContextMode = AUTO_CONTEXT_OFF,
109
109
  idStyle: AnthropicIdStyle = "desktop3p",
110
110
  aliasForRoute: (provider: string, modelId: string) => string = desktop3pAlias,
111
+ nativeContextCap?: NativeContextLimitsInput,
111
112
  ): AnthropicModelInfo[] {
112
113
  const out: AnthropicModelInfo[] = [];
113
114
  const seen = new Set<string>();
114
115
  // [1m] picker variant (devlog 260712 B1): Claude Code accounts exactly 1M for ids
115
116
  // carrying the marker (2.1.207 binary: /\[1m\]/i → 1e6, compaction preserved), so
116
- // models with an authoritative >=1M window get a second selectable row. In
117
- // auto-context mode (devlog 020) the predicate widens to windows > 200k that can
118
- // host the compact window display stays honest (real window, not "1M"). Guards
119
- // (audit R1#11): same dedupe set, never double-suffix.
120
- const push1mVariant = (base: AnthropicModelInfo, contextWindow: number | undefined, mode: AutoContextMode = auto) => {
121
- if (!shouldMarkOneMillion(contextWindow, mode)) return;
117
+ // ONLY models with an authoritative >=1M window get a second selectable row
118
+ // the auto-context widening that let a 372K route carry the marker (and be
119
+ // over-filled) is the #854 defect and does not come back. Guards (audit R1#11):
120
+ // same dedupe set, never double-suffix.
121
+ const push1mVariant = (base: AnthropicModelInfo, contextWindow: number | undefined, maxInputTokens?: number) => {
122
+ // The [1m] marker makes Claude Code account 1e6 tokens for the row, so it
123
+ // may only name models whose AUTHORITATIVE effective window is >= 1M —
124
+ // never the auto-context widening, which would mark a 372K route and have
125
+ // Claude Code over-fill it (the #854 defect).
126
+ if (contextWindow === undefined || contextWindow < ONE_MILLION) return;
122
127
  if (base.id.includes("[1m]")) return;
123
128
  const id = `${base.id}[1m]`;
124
129
  if (seen.has(id)) return;
125
130
  seen.add(id);
126
- const window = contextWindow as number;
127
- const label = window >= ONE_MILLION ? "1M" : `${Math.round(window / 1_000)}k`;
128
- out.push({ ...base, id, display_name: `${base.display_name} · ${label}`, max_input_tokens: Math.min(window, ONE_MILLION) });
131
+ // The marker fixes Claude Code's accounting at 1e6, but a model may accept less input
132
+ // than that a routed GPT-5.6 row runs a 1,050,000 window while refusing past 922,000
133
+ // (measured see devlog/_plan/260817_native_gpt56_1m_context/001_measurement_evidence.md).
134
+ // Advertising the flat 1e6 there would invite mid-session context_length_exceeded, so the
135
+ // variant reports whichever of the two is smaller.
136
+ const advertised = typeof maxInputTokens === "number" && maxInputTokens > 0
137
+ ? Math.min(ONE_MILLION, maxInputTokens)
138
+ : ONE_MILLION;
139
+ out.push({ ...base, id, display_name: `${base.display_name} · 1M`, max_input_tokens: advertised });
129
140
  };
130
141
  for (const slug of nativeSlugs) {
131
142
  const id = idStyle === "readable" ? claudeCodeNativeAlias(slug) : aliasForRoute("native", slug);
132
143
  if (seen.has(id)) continue;
133
144
  seen.add(id);
134
- const info = modelInfo(id, `${slug} (native)`, nativeEffectiveLadder(slug), true);
145
+ const nativeWindow = nativeOpenAiContextWindow(slug, nativeContextCap);
146
+ const nativeMaxInput = nativeOpenAiMaxInputTokens(slug, nativeContextCap);
147
+ // max_input_tokens is an INPUT limit, so it follows the measured input ceiling rather
148
+ // than the total window whenever the model publishes one.
149
+ const info = modelInfo(id, `${slug} (native)`, nativeEffectiveLadder(slug), true, nativeMaxInput ?? nativeWindow);
135
150
  out.push(info);
136
- push1mVariant(info, nativeOpenAiContextWindow(slug));
151
+ push1mVariant(info, nativeWindow, nativeMaxInput);
137
152
  }
138
153
  for (const m of routedModels) {
139
154
  const id = idStyle === "readable" ? claudeCodeAlias(m.provider, m.id) : aliasForRoute(m.provider, m.id);
@@ -141,11 +156,19 @@ export function buildAnthropicModelInfos(
141
156
  seen.add(id);
142
157
  const ladder = Array.isArray(m.reasoningEfforts) ? m.reasoningEfforts : [];
143
158
  const imageInput = Array.isArray(m.inputModalities) ? m.inputModalities.includes("image") : false;
144
- const info = modelInfo(id, `${m.id} (${m.provider})`, ladder, imageInput, m.contextWindow);
159
+ // max_input_tokens is an input limit, so a row that publishes a lower input ceiling than
160
+ // its window (native GPT-5.6 forwarded through a provider: 922k under 1.05M) reports the
161
+ // ceiling. Rows without one keep reporting the window, as before.
162
+ const routedMaxInput = typeof m.maxInputTokens === "number" && m.maxInputTokens > 0
163
+ ? (typeof m.contextWindow === "number" && m.contextWindow > 0
164
+ ? Math.min(m.maxInputTokens, m.contextWindow)
165
+ : m.maxInputTokens)
166
+ : undefined;
167
+ const info = modelInfo(id, `${m.id} (${m.provider})`, ladder, imageInput, routedMaxInput ?? m.contextWindow);
145
168
  out.push(info);
146
169
  // Anthropic passthrough guard (audit 021 #3): never auto-widen canonical claude
147
170
  // routes — only a genuine >=1M window earns the variant row there.
148
- push1mVariant(info, m.contextWindow, m.provider === "anthropic" ? AUTO_CONTEXT_OFF : auto);
171
+ push1mVariant(info, m.contextWindow, routedMaxInput);
149
172
  }
150
173
  return out;
151
174
  }