@bitkyc08/opencodex 2.83.0 → 2.86.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 (405) hide show
  1. package/README.md +0 -4
  2. package/bin/ocx.mjs +61 -3
  3. package/gui/dist/assets/App-BjlttVLy.js +58 -0
  4. package/gui/dist/assets/Tray-DyR9yBee.js +1 -0
  5. package/gui/dist/assets/{index-BAbcj51J.css → index-BB6uK6tU.css} +1 -1
  6. package/gui/dist/assets/index-BMssP4k3.js +94 -0
  7. package/gui/dist/assets/quota-summary-D8M1bc_R.js +1 -0
  8. package/gui/dist/index.html +2 -2
  9. package/package.json +2 -3
  10. package/src/adapters/anthropic/beta-allowlist.ts +87 -8
  11. package/src/adapters/anthropic/client-identity.ts +1 -1
  12. package/src/adapters/anthropic/passthrough.ts +33 -4
  13. package/src/adapters/azure.ts +19 -1
  14. package/src/adapters/client-fingerprint.ts +24 -0
  15. package/src/adapters/coding-agent/protocol.ts +15 -8
  16. package/src/adapters/coding-agent/turn.ts +22 -9
  17. package/src/adapters/cursor/arg-codec.ts +17 -5
  18. package/src/adapters/cursor/catalog.ts +8 -0
  19. package/src/adapters/cursor/effort-map.ts +3 -0
  20. package/src/adapters/cursor/freeform-args.ts +75 -0
  21. package/src/adapters/cursor/protobuf-events.ts +36 -23
  22. package/src/adapters/google-antigravity-wire.ts +22 -1
  23. package/src/adapters/google.ts +2 -2
  24. package/src/adapters/ollama-native.ts +29 -1
  25. package/src/adapters/openai-chat/messages.ts +8 -4
  26. package/src/adapters/openai-chat/response-events.ts +59 -1
  27. package/src/adapters/openai-chat.ts +8 -5
  28. package/src/adapters/openai-responses/empty-assistant.ts +32 -0
  29. package/src/adapters/openai-responses/passthrough.ts +3 -0
  30. package/src/adapters/openai-responses/request-strips.ts +10 -2
  31. package/src/adapters/qoder/adapter.ts +56 -15
  32. package/src/advisor/consult.ts +220 -0
  33. package/src/advisor/context.ts +226 -0
  34. package/src/advisor/disclosure.ts +20 -0
  35. package/src/advisor/runtime.ts +305 -0
  36. package/src/advisor/settings.ts +135 -0
  37. package/src/advisor/state.ts +369 -0
  38. package/src/advisor/synthetic-tool.ts +33 -0
  39. package/src/bridge/errors.ts +24 -0
  40. package/src/bridge.ts +1 -1
  41. package/src/chat/inbound.ts +30 -6
  42. package/src/chat/outbound.ts +5 -0
  43. package/src/claude/context-windows.ts +54 -1
  44. package/src/claude/desktop-3p-library.ts +43 -3
  45. package/src/claude/desktop-3p.ts +22 -8
  46. package/src/claude/desktop-picker-profile.ts +11 -4
  47. package/src/claude/desktop-profile.ts +10 -1
  48. package/src/claude/desktop-remote-store-io.ts +5 -3
  49. package/src/claude/inbound.ts +121 -50
  50. package/src/claude/intercept/connect-proxy.ts +10 -3
  51. package/src/claude/intercept/local-ca.ts +8 -1
  52. package/src/claude/intercept/picker-ca-persistence.ts +3 -1
  53. package/src/claude/intercept/picker-listener.ts +49 -1
  54. package/src/claude/intercept/settings-no-proxy.ts +146 -0
  55. package/src/claude/intercept/settings.ts +101 -9
  56. package/src/claude/model-default-settings.ts +154 -0
  57. package/src/claude/model-info.ts +7 -7
  58. package/src/claude/outbound.ts +13 -6
  59. package/src/cli/account-api.ts +65 -0
  60. package/src/cli/account-auth.ts +26 -2
  61. package/src/cli/account.ts +41 -5
  62. package/src/cli/advisor.ts +151 -0
  63. package/src/cli/auxiliary-listener-preflight.ts +37 -0
  64. package/src/cli/capabilities-access-remote.ts +8 -0
  65. package/src/cli/capabilities-agents-routing.ts +18 -0
  66. package/src/cli/capabilities-base.ts +14 -2
  67. package/src/cli/catalog-command-result.ts +2 -1
  68. package/src/cli/chatgpt-command.ts +35 -14
  69. package/src/cli/claude.ts +60 -11
  70. package/src/cli/config-command.ts +8 -2
  71. package/src/cli/debug.ts +8 -0
  72. package/src/cli/dispatch.ts +21 -4
  73. package/src/cli/doctor.ts +9 -0
  74. package/src/cli/ensure-desktop-cold-start.ts +329 -0
  75. package/src/cli/export-command.ts +15 -2
  76. package/src/cli/help.ts +2 -1
  77. package/src/cli/hub-disable.ts +99 -0
  78. package/src/cli/hub.ts +4 -2
  79. package/src/cli/index.ts +26 -8
  80. package/src/cli/integration-plan-dto.ts +2 -0
  81. package/src/cli/link-child.ts +104 -0
  82. package/src/cli/link-enrollment.ts +96 -0
  83. package/src/cli/link.ts +39 -24
  84. package/src/cli/observe-stream.ts +6 -1
  85. package/src/cli/observe.ts +10 -2
  86. package/src/cli/provider.ts +12 -0
  87. package/src/cli/registry.ts +20 -7
  88. package/src/cli/restart-scope.ts +3 -1
  89. package/src/cli/runtime-api.ts +27 -2
  90. package/src/cli/status.ts +104 -20
  91. package/src/client/connect.ts +39 -4
  92. package/src/client/hub-client.ts +14 -7
  93. package/src/client/hub-state.ts +25 -14
  94. package/src/client/link-home.ts +248 -0
  95. package/src/client/link-management-relay.ts +61 -0
  96. package/src/client/link-relay.ts +6 -1
  97. package/src/client/link-status.ts +14 -0
  98. package/src/client/machine-api.ts +14 -7
  99. package/src/client/machine-listener.ts +29 -4
  100. package/src/clients/config-export/contracts.ts +1 -0
  101. package/src/clients/config-export/model-metadata.ts +3 -2
  102. package/src/clients/config-export/qoder.ts +93 -0
  103. package/src/clients/config-export.ts +59 -16
  104. package/src/clients/omo-role-models.ts +6 -3
  105. package/src/codex/agent-role-models.ts +26 -8
  106. package/src/codex/auth-api/account-list.ts +17 -2
  107. package/src/codex/auth-api/account-selection.ts +17 -7
  108. package/src/codex/auth-api/pool-mode-gate.ts +5 -2
  109. package/src/codex/auth-api/routes.ts +5 -1
  110. package/src/codex/auth-context.ts +79 -5
  111. package/src/codex/autostart-health.ts +7 -1
  112. package/src/codex/catalog/build-entries.ts +11 -2
  113. package/src/codex/catalog/bundled.ts +5 -1
  114. package/src/codex/catalog/configured-tiers.ts +50 -0
  115. package/src/codex/catalog/effort.ts +6 -3
  116. package/src/codex/catalog/metadata.ts +4 -2
  117. package/src/codex/catalog/model-hints.ts +21 -3
  118. package/src/codex/catalog/native-models.ts +16 -2
  119. package/src/codex/catalog/native-ultrafast.ts +106 -0
  120. package/src/codex/catalog/parsing.ts +5 -0
  121. package/src/codex/catalog/provider-models.ts +4 -0
  122. package/src/codex/catalog/retained-sync.ts +2 -0
  123. package/src/codex/catalog/routed-gather.ts +22 -4
  124. package/src/codex/catalog/vendor-context.ts +82 -0
  125. package/src/codex/config-write-lock.ts +40 -2
  126. package/src/codex/convergence.ts +2 -0
  127. package/src/codex/desktop-app/types.ts +2 -0
  128. package/src/codex/desktop-app/windows.ts +29 -16
  129. package/src/codex/desktop-app-restart.ts +4 -2
  130. package/src/codex/desktop-compatibility/certificate-service.ts +99 -0
  131. package/src/codex/desktop-compatibility/certificate-store.ts +193 -0
  132. package/src/codex/desktop-compatibility/connection-store.ts +87 -0
  133. package/src/codex/desktop-compatibility/installed-build.ts +55 -0
  134. package/src/codex/desktop-compatibility/json-body.ts +29 -0
  135. package/src/codex/desktop-compatibility/native-identity.ts +62 -0
  136. package/src/codex/desktop-compatibility/relay-listener.ts +93 -0
  137. package/src/codex/desktop-compatibility/routing-binding.ts +10 -0
  138. package/src/codex/desktop-compatibility/routing-preflight.ts +69 -0
  139. package/src/codex/desktop-compatibility/runtime-ownership.ts +28 -0
  140. package/src/codex/desktop-compatibility/runtime.ts +173 -0
  141. package/src/codex/desktop-compatibility/service.ts +30 -0
  142. package/src/codex/desktop-compatibility/startup-settings.ts +50 -0
  143. package/src/codex/desktop-compatibility/usage-activation.ts +86 -0
  144. package/src/codex/desktop-compatibility/usage-controlled-fetch.ts +59 -0
  145. package/src/codex/desktop-compatibility/usage-controller.ts +107 -0
  146. package/src/codex/desktop-compatibility/usage-policy.ts +59 -0
  147. package/src/codex/desktop-compatibility/usage-refresh.ts +33 -0
  148. package/src/codex/desktop-compatibility/usage-sse-controller.ts +86 -0
  149. package/src/codex/desktop-compatibility/windows-activation-source.ts +44 -0
  150. package/src/codex/desktop-compatibility/windows-certificate-trust.ts +81 -0
  151. package/src/codex/desktop-compatibility/windows-key-protection.ts +59 -0
  152. package/src/codex/desktop-compatibility/windows-package-command.ts +86 -0
  153. package/src/codex/desktop-compatibility/windows-package-launch.ts +28 -0
  154. package/src/codex/history-provider.ts +1 -1
  155. package/src/codex/inject/config-write-section.ts +4 -1
  156. package/src/codex/inject/remove.ts +18 -6
  157. package/src/codex/journal.ts +16 -1
  158. package/src/codex/main-account.ts +99 -15
  159. package/src/codex/native-main-refresh-mode.ts +217 -0
  160. package/src/codex/native-profile-startup.ts +4 -1
  161. package/src/codex/prompt-journal.ts +20 -2
  162. package/src/codex/prompt-layers/encoding.ts +49 -0
  163. package/src/codex/prompt-layers/import-source.ts +30 -0
  164. package/src/codex/prompt-layers/toml-edit.ts +269 -37
  165. package/src/codex/prompt-layers/toml-read.ts +52 -4
  166. package/src/codex/prompt-layers.ts +383 -20
  167. package/src/codex/prompt-lock-claim.ts +13 -3
  168. package/src/codex/prompt-lock-evidence.ts +3 -3
  169. package/src/codex/prompt-lock-owner.ts +14 -6
  170. package/src/codex/prompt-text-probe.ts +34 -5
  171. package/src/codex/quota.ts +13 -6
  172. package/src/codex/role-auto-assign.ts +2 -5
  173. package/src/codex/routing/active-account.ts +21 -0
  174. package/src/codex/routing/health-store.ts +1 -1
  175. package/src/codex/routing/pool-recovery.ts +79 -0
  176. package/src/codex/routing.ts +1 -1
  177. package/src/codex/runtime.ts +196 -8
  178. package/src/codex/shim-diagnostics.ts +6 -0
  179. package/src/codex/shim-overlay.ts +15 -6
  180. package/src/codex/shim.ts +1 -1
  181. package/src/combos/failover.ts +3 -0
  182. package/src/config/api-key-reconcile.ts +34 -0
  183. package/src/config/diagnostics.ts +18 -1
  184. package/src/config/feature-flags.ts +5 -0
  185. package/src/config/live-reconcile.ts +56 -29
  186. package/src/config/live-topology.ts +29 -0
  187. package/src/config/load-degrade.ts +8 -2
  188. package/src/config/paths.ts +13 -3
  189. package/src/config/salvage.ts +3 -1
  190. package/src/config/schema/config-schema.ts +19 -0
  191. package/src/config/schema/desktop-compatibility.ts +9 -0
  192. package/src/config/schema/leaf-validators.ts +1 -0
  193. package/src/generated/compatibility-version.json +713 -285
  194. package/src/generated/model-metadata.ts +1 -1
  195. package/src/grok/grpc-web.ts +23 -1
  196. package/src/grok/reset-coupon-ledger.ts +75 -5
  197. package/src/grok/reset-coupons.ts +4 -2
  198. package/src/integrations/catalog-refresh.ts +1 -1
  199. package/src/integrations/config-io.ts +20 -2
  200. package/src/integrations/journal.ts +15 -1
  201. package/src/integrations/mutation-plan.ts +14 -7
  202. package/src/integrations/registry.ts +52 -11
  203. package/src/integrations/state.ts +19 -7
  204. package/src/integrations/store.ts +3 -0
  205. package/src/integrations/target.ts +42 -5
  206. package/src/integrations/writer.ts +24 -7
  207. package/src/lib/advisor-activation.ts +16 -0
  208. package/src/lib/app-owned-memory-stores.ts +4 -0
  209. package/src/lib/app-owned-memory.ts +11 -0
  210. package/src/lib/background-work.ts +65 -0
  211. package/src/lib/bounded-file-read.ts +58 -0
  212. package/src/lib/bun-path-runtime.d.mts +10 -1
  213. package/src/lib/bun-path-runtime.mjs +62 -3
  214. package/src/lib/desktop-proxy-route.ts +30 -0
  215. package/src/lib/desktop-upstream-tunnel.ts +107 -0
  216. package/src/lib/errors.ts +7 -0
  217. package/src/lib/json-byte-size.ts +31 -0
  218. package/src/lib/local-internal-call-capability.ts +55 -0
  219. package/src/lib/optional-shutdown-hooks.ts +35 -0
  220. package/src/lib/outbound-credential-registry.ts +210 -0
  221. package/src/lib/package-tree-integrity.ts +2 -2
  222. package/src/lib/passive-read.ts +35 -0
  223. package/src/lib/shadow-call.ts +44 -5
  224. package/src/lib/socks5-fetch.ts +48 -71
  225. package/src/lib/socks5-handshake.ts +112 -0
  226. package/src/lib/translator-budget.ts +7 -1
  227. package/src/link/codex-settings.ts +155 -0
  228. package/src/link/management-routes.ts +89 -0
  229. package/src/link/remote-probe.ts +38 -0
  230. package/src/link/ssh-argv.ts +26 -5
  231. package/src/link/ssh-runner.ts +58 -27
  232. package/src/link/status-projection.ts +22 -4
  233. package/src/link/supervisor.ts +144 -49
  234. package/src/link/tunnel-state.ts +11 -4
  235. package/src/oauth/anthropic-continuity.ts +4 -3
  236. package/src/oauth/anthropic-identity.ts +13 -2
  237. package/src/oauth/anthropic-model-quota.ts +13 -1
  238. package/src/oauth/anthropic-routing.ts +230 -23
  239. package/src/oauth/anthropic.ts +18 -4
  240. package/src/oauth/generic-account-failover.ts +29 -5
  241. package/src/oauth/google-antigravity.ts +24 -7
  242. package/src/oauth/index.ts +28 -17
  243. package/src/oauth/kiro-account-identity.ts +106 -0
  244. package/src/oauth/kiro-device-login.ts +2 -1
  245. package/src/oauth/kiro.ts +55 -6
  246. package/src/oauth/plan.ts +29 -0
  247. package/src/oauth/store.ts +40 -3
  248. package/src/oauth/types.ts +2 -0
  249. package/src/plugins/loader.ts +11 -0
  250. package/src/protocols/codecs/chat-ir.ts +467 -0
  251. package/src/protocols/codecs/chat.ts +10 -1
  252. package/src/protocols/codecs/messages-ir.ts +500 -0
  253. package/src/protocols/features.ts +10 -2
  254. package/src/protocols/path.ts +11 -1
  255. package/src/protocols/plan-snapshot.ts +39 -16
  256. package/src/protocols/plan.ts +9 -1
  257. package/src/protocols/settings.ts +8 -0
  258. package/src/providers/anthropic-billing-ownership.ts +74 -0
  259. package/src/providers/anthropic-billing-recovery.ts +31 -0
  260. package/src/providers/anthropic-billing-refusal.ts +55 -0
  261. package/src/providers/anthropic-reset-admission.ts +62 -0
  262. package/src/providers/anthropic-reset-grant-ledger.ts +28 -6
  263. package/src/providers/antigravity-account-roster.ts +102 -0
  264. package/src/providers/antigravity-models.ts +85 -7
  265. package/src/providers/api-key-resolve.ts +17 -2
  266. package/src/providers/key-failover.ts +52 -5
  267. package/src/providers/model-rename-fields.ts +2 -0
  268. package/src/providers/model-rename-migration.ts +5 -3
  269. package/src/providers/model-rename-startup.ts +5 -3
  270. package/src/providers/quota/account-cache.ts +11 -0
  271. package/src/providers/quota/anthropic-account-quota.ts +4 -2
  272. package/src/providers/quota/report-cache.ts +14 -0
  273. package/src/providers/quota/vendor-probes-oauth.ts +3 -3
  274. package/src/providers/quota.ts +4 -1
  275. package/src/providers/registry/entries-core.ts +21 -20
  276. package/src/providers/registry/entries-extended.ts +19 -1
  277. package/src/providers/registry/model-seeds.ts +17 -0
  278. package/src/providers/request-pacing.ts +9 -0
  279. package/src/providers/stale-no-vision-seed-migration.ts +72 -0
  280. package/src/responses/compaction.ts +14 -0
  281. package/src/responses/parser.ts +37 -20
  282. package/src/responses/state.ts +32 -29
  283. package/src/routing/history/build-file.ts +92 -0
  284. package/src/routing/history/indexer.ts +248 -216
  285. package/src/routing/history/ingest.ts +238 -0
  286. package/src/routing/history/rebuild-job.ts +111 -0
  287. package/src/routing/history/rebuild-protocol.ts +15 -0
  288. package/src/routing/history/rebuild-worker.ts +34 -0
  289. package/src/server/admission-model-scope.ts +4 -1
  290. package/src/server/antigravity-direct-eligibility.ts +208 -0
  291. package/src/server/auth-cors.ts +46 -0
  292. package/src/server/chat-completions.ts +75 -9
  293. package/src/server/claude-messages.ts +110 -20
  294. package/src/server/configured-credentials.ts +194 -0
  295. package/src/server/index/desktop-compatibility-startup.ts +56 -0
  296. package/src/server/index/link-management.ts +58 -0
  297. package/src/server/index/optional-listeners.ts +5 -1
  298. package/src/server/index/serve-options.ts +15 -2
  299. package/src/server/index/startup-warnings.ts +9 -0
  300. package/src/server/index/websocket-handler.ts +9 -0
  301. package/src/server/index.ts +13 -10
  302. package/src/server/inference/client-encoder-delivery.ts +6 -5
  303. package/src/server/lifecycle.ts +2 -1
  304. package/src/server/login-shell-path.ts +146 -0
  305. package/src/server/management/advisor-routes.ts +191 -0
  306. package/src/server/management/agent-settings-routes.ts +50 -14
  307. package/src/server/management/anthropic-reset-grant-routes.ts +17 -1
  308. package/src/server/management/codex-agent-role-routes.ts +19 -6
  309. package/src/server/management/codex-prompt-routes.ts +136 -5
  310. package/src/server/management/codex-role-auto-assign.ts +1 -1
  311. package/src/server/management/companion-routes.ts +4 -0
  312. package/src/server/management/config-routes.ts +2 -2
  313. package/src/server/management/context.ts +4 -0
  314. package/src/server/management/desktop-compatibility-routes.ts +46 -0
  315. package/src/server/management/desktop-compatibility-runtime-routes.ts +46 -0
  316. package/src/server/management/desktop-compatibility-settings-routes.ts +27 -0
  317. package/src/server/management/grok-coupon-routes.ts +78 -27
  318. package/src/server/management/link-enrollment.ts +192 -0
  319. package/src/server/management/link-route-state.ts +185 -0
  320. package/src/server/management/link-routes.ts +82 -402
  321. package/src/server/management/model-routes.ts +2 -2
  322. package/src/server/management/model-rows.ts +3 -1
  323. package/src/server/management/protocol-settings-patch.ts +1 -0
  324. package/src/server/management/provider-overwrite-carry.ts +10 -0
  325. package/src/server/management/provider-routes.ts +1 -0
  326. package/src/server/management/route-registry.ts +16 -3
  327. package/src/server/management/shared.ts +3 -0
  328. package/src/server/management/sibling-guard.ts +1 -0
  329. package/src/server/management-api.ts +9 -1
  330. package/src/server/messages-antigravity-direct.ts +195 -0
  331. package/src/server/messages-native.ts +73 -43
  332. package/src/server/relay-eager.ts +2 -0
  333. package/src/server/request-log-filter.ts +6 -1
  334. package/src/server/request-log-terminal-status.ts +63 -6
  335. package/src/server/request-log.ts +7 -0
  336. package/src/server/responses/adapter-continuation.ts +35 -18
  337. package/src/server/responses/adapter-delivery.ts +24 -2
  338. package/src/server/responses/adapter-dispatch.ts +261 -81
  339. package/src/server/responses/advisor-plan-slot.ts +27 -0
  340. package/src/server/responses/advisor-slot.ts +336 -0
  341. package/src/server/responses/code-mode-collab-surface.ts +313 -0
  342. package/src/server/responses/codex-ws-exchange.ts +147 -6
  343. package/src/server/responses/codex-ws-wire.ts +203 -0
  344. package/src/server/responses/collaboration.ts +36 -7
  345. package/src/server/responses/compact.ts +126 -48
  346. package/src/server/responses/context-overflow.ts +13 -1
  347. package/src/server/responses/core-codex-account.ts +105 -1
  348. package/src/server/responses/core-combo-failure.ts +24 -1
  349. package/src/server/responses/core-combo.ts +28 -2
  350. package/src/server/responses/core-errors.ts +29 -1
  351. package/src/server/responses/core-lifetime.ts +2 -0
  352. package/src/server/responses/core-normalize.ts +11 -0
  353. package/src/server/responses/core-options.ts +48 -0
  354. package/src/server/responses/core-replay.ts +7 -4
  355. package/src/server/responses/core.ts +1 -1
  356. package/src/server/responses/deferred-collab-surface.ts +158 -0
  357. package/src/server/responses/deferred-send-reporter.ts +25 -0
  358. package/src/server/responses/fetch-helpers.ts +12 -1
  359. package/src/server/responses/passthrough-delivery.ts +20 -11
  360. package/src/server/responses/passthrough-dispatch.ts +29 -7
  361. package/src/server/responses/request-prepare.ts +74 -9
  362. package/src/server/responses/request-transport.ts +22 -8
  363. package/src/server/responses/run-turn-execution.ts +70 -8
  364. package/src/server/responses/sidecar-execution.ts +89 -5
  365. package/src/server/responses/terminal-guard.ts +2 -1
  366. package/src/server/responses/ws-upstream.ts +9 -1
  367. package/src/server/responses-undeclared-tool-guard.ts +30 -9
  368. package/src/server/startup-action-control.ts +38 -4
  369. package/src/server/startup-health-cache.ts +5 -0
  370. package/src/server/system-env-shell.ts +7 -2
  371. package/src/server/system-env.ts +11 -1
  372. package/src/service/cli.ts +5 -1
  373. package/src/service/desktop-startup.ts +161 -34
  374. package/src/service/desktop-supervision.d.mts +27 -1
  375. package/src/service/desktop-supervision.mjs +111 -5
  376. package/src/service/health-sentinel-launch.ts +176 -0
  377. package/src/service/health-sentinel-main.ts +466 -0
  378. package/src/service/health-sentinel-policy.ts +241 -0
  379. package/src/service/health-sentinel-probe.ts +71 -0
  380. package/src/service/health-sentinel-windows.ts +87 -0
  381. package/src/service/linux-linger.ts +20 -0
  382. package/src/service/logging.ts +72 -0
  383. package/src/tray/windows-tray.ps1 +43 -24
  384. package/src/types/config.ts +68 -6
  385. package/src/types/provider.ts +7 -0
  386. package/src/types/request.ts +15 -0
  387. package/src/types/tools.ts +2 -0
  388. package/src/update/async-check.ts +1 -1
  389. package/src/update/badge.ts +2 -2
  390. package/src/update/index.ts +88 -15
  391. package/src/update/job-supervised-restart.ts +107 -0
  392. package/src/update/job.ts +32 -32
  393. package/src/update/notify.ts +1 -1
  394. package/src/update/refresh-scheduler.ts +2 -2
  395. package/src/update/supervised-recovery.d.mts +27 -0
  396. package/src/update/supervised-recovery.mjs +157 -0
  397. package/src/usage/cache-diagnostic.ts +2 -0
  398. package/src/usage/expected-prices.ts +16 -0
  399. package/src/usage/log.ts +19 -1
  400. package/src/web-search/gemini-executor.ts +2 -2
  401. package/assets/sponsors/packycode.png +0 -0
  402. package/gui/dist/assets/App-Ba8uG696.js +0 -54
  403. package/gui/dist/assets/Tray-IImbNPRy.js +0 -1
  404. package/gui/dist/assets/index-BHRZdJuv.js +0 -94
  405. package/gui/dist/assets/quota-summary-DiJO6sYY.js +0 -1
@@ -47,6 +47,18 @@ import { googleVertexLocationConfigError } from "../providers/google-vertex-loca
47
47
  import { xaiResponsesOptInState } from "../providers/xai-responses-opt-in";
48
48
 
49
49
  let _corsOrigin = "http://localhost:10100";
50
+ // Listener identity is process-local. Never infer this trust from forwarded headers: the
51
+ // request handler marks a Request only after resolving the actual Bun Server instance.
52
+ const trustedHubManagementRequests = new WeakSet<Request>();
53
+
54
+ export function markTrustedHubManagementRequest(req: Request): void {
55
+ trustedHubManagementRequests.add(req);
56
+ }
57
+
58
+ export function inheritTrustedHubManagementRequest(source: Request, target: Request): void {
59
+ if (trustedHubManagementRequests.has(source)) trustedHubManagementRequests.add(target);
60
+ }
61
+
50
62
  export function setCorsOrigin(port: number): void { _corsOrigin = `http://localhost:${port}`; }
51
63
  /** The proxy's own listening port. No admission check uses it: both loopback predicates key on hostname alone. */
52
64
  export function configuredPort(): string {
@@ -144,6 +156,24 @@ export function managementRequestOrigin(req: Request, config: OcxConfig): string
144
156
  return null;
145
157
  }
146
158
  }
159
+ if (trustedHubManagementRequests.has(req)) {
160
+ if (config.runtimeRole !== "hub") return null;
161
+ const canonical = canonicalHubManagementOrigin(config.hub?.managementPublicOrigin);
162
+ if (!canonical) return null;
163
+ try {
164
+ // The dedicated listener may receive HTTP from a local TLS terminator, so its URL
165
+ // scheme is not authoritative. Host must still name exactly the configured public
166
+ // authority; forwarded host/proto headers are deliberately ignored.
167
+ const requestAuthority = new URL(`https://${host}`);
168
+ if (requestAuthority.username || requestAuthority.password || requestAuthority.pathname !== "/"
169
+ || requestAuthority.search || requestAuthority.hash || requestAuthority.host !== new URL(canonical).host) {
170
+ return null;
171
+ }
172
+ return canonical;
173
+ } catch {
174
+ return null;
175
+ }
176
+ }
147
177
  if (!isApiAuthRequired(config)) return null;
148
178
  if (config.runtimeRole === "hub" && config.hub?.managementPublicOrigin) {
149
179
  try {
@@ -437,6 +467,18 @@ export function resolveDataPlaneAdmissionSecret(
437
467
  return null;
438
468
  }
439
469
 
470
+ function canonicalHubManagementOrigin(value: unknown): string | null {
471
+ if (typeof value !== "string") return null;
472
+ try {
473
+ const parsed = new URL(value);
474
+ if (parsed.protocol !== "https:" || parsed.username || parsed.password || parsed.pathname !== "/"
475
+ || parsed.search || parsed.hash) return null;
476
+ return parsed.origin;
477
+ } catch {
478
+ return null;
479
+ }
480
+ }
481
+
440
482
  /** The principal an authenticated admission belongs to, or undefined for loopback. */
441
483
  export function contextPrincipalIdOf(admission: DataPlaneAdmission | undefined): string | undefined {
442
484
  return admission && "contextPrincipalId" in admission ? admission.contextPrincipalId : undefined;
@@ -883,6 +925,8 @@ export function providerManagementConfigError(
883
925
  if (raw.xaiResponsesXSearch !== undefined && typeof raw.xaiResponsesXSearch !== "boolean") {
884
926
  return `provider ${name} xaiResponsesXSearch must be a boolean`;
885
927
  }
928
+ const minMaxOutputError = positiveIntegerConfigError(raw.minMaxOutputTokens, "minMaxOutputTokens");
929
+ if (minMaxOutputError) return `provider ${name} ${minMaxOutputError}`;
886
930
  const defaultMaxOutputError = positiveIntegerConfigError(raw.defaultMaxOutputTokens, "defaultMaxOutputTokens");
887
931
  if (defaultMaxOutputError) return `provider ${name} ${defaultMaxOutputError}`;
888
932
  const maxOutputError = positiveIntegerRecordConfigError(raw.modelMaxOutputTokens, "modelMaxOutputTokens");
@@ -1043,6 +1087,7 @@ const PROVIDER_CONFIG_FIELD_POLICY = {
1043
1087
  newModelPolicy: "editor",
1044
1088
  modelPreset: "editor",
1045
1089
  contextWindow: "editor",
1090
+ inferContextWindow: "editor",
1046
1091
  modelContextWindows: "editor",
1047
1092
  modelContextTiers: "editor",
1048
1093
  modelInputModalities: "editor",
@@ -1050,6 +1095,7 @@ const PROVIDER_CONFIG_FIELD_POLICY = {
1050
1095
  modelMaxInputTokens: "runtime",
1051
1096
  modelAutoCompactTokenLimits: "editor",
1052
1097
  defaultMaxOutputTokens: "editor",
1098
+ minMaxOutputTokens: "editor",
1053
1099
  modelMaxOutputTokens: "editor",
1054
1100
  modelCosts: "editor",
1055
1101
  headers: "redacted",
@@ -10,7 +10,9 @@ import {
10
10
  assertChatCompletionsRoutingBody,
11
11
  ChatCompletionsRequestError,
12
12
  } from "../chat/inbound";
13
- import { chatToResponsesBody } from "../protocols/codecs/chat";
13
+ import { chatSettingsBody, chatToResponsesBody } from "../protocols/codecs/chat";
14
+ import { chatConversationToIr, chatIrRouteEvidence, withDirectChatConversation, type ChatIrConversation } from "../protocols/codecs/chat-ir";
15
+ import { antigravityDirectDeclineReason } from "./antigravity-direct-eligibility";
14
16
  import { normalizeChatImageParts } from "../chat/image-parts";
15
17
  import {
16
18
  chatCompletionsErrorResponse,
@@ -19,7 +21,8 @@ import {
19
21
  responsesJsonToChatCompletion,
20
22
  responsesSseToChatCompletionsSse,
21
23
  } from "../chat/outbound";
22
- import { classifyError, cyberPolicyErrorType, CYBER_POLICY_ERROR_CODE, isCyberPolicyCode } from "../lib/errors";
24
+ import { ADVISOR_INTERNAL_CAPABILITY_HEADER, isInternalCallCapability } from "../lib/local-internal-call-capability";
25
+ import { classifyError, cyberPolicyErrorType, CYBER_POLICY_ERROR_CODE, isCyberPolicyCode, UNDECLARED_TOOL_CALL_ERROR_CODE } from "../lib/errors";
23
26
  import { redactSecretString } from "../lib/redact";
24
27
  import { resolveClientRetryAfter } from "../lib/retry-after";
25
28
  import {
@@ -44,6 +47,7 @@ import type { OcxConfig } from "../types";
44
47
  import { readJsonRequestBody, resolveInboundBodyLimitBytes } from "./request-decompress";
45
48
  import {
46
49
  addFinalRequestLog,
50
+ cacheDiagnosticsObserving,
47
51
  httpStatusForRequestLogTerminal,
48
52
  recordFirstOutput,
49
53
  type RequestLogContext,
@@ -272,6 +276,12 @@ async function handleChatCompletionsWithBudget(
272
276
  const protocolSettings = resolveProtocolSettings(config);
273
277
  const nativeChatCombos = protocolSettings.rollout.nativeChatCombos
274
278
  && settledRoute?.combo !== undefined && !effortRow;
279
+ // Antigravity direct lane (off by default): the conversation is decoded straight into the IR
280
+ // and the answer is encoded straight into Chat, with no internal Responses body either way.
281
+ const antigravityDecline = settledRoute && !chatNativeRoute
282
+ ? antigravityDirectDeclineReason(settledRoute, chatBody, config, { cacheDiagnostics: cacheDiagnosticsObserving() })
283
+ : undefined;
284
+ const directRoute = settledRoute && !chatNativeRoute && antigravityDecline === undefined ? settledRoute : null;
275
285
  const envelope = protocolSettings.unrepresentable === "reject" || nativeChatCombos
276
286
  ? createProtocolEnvelope({ inbound: "chat", body: chatBody, translatorBudget })
277
287
  : undefined;
@@ -281,7 +291,7 @@ async function handleChatCompletionsWithBudget(
281
291
  inbound: "chat",
282
292
  requestPath: chatNativeRoute
283
293
  ? requestPathForLane("chat", "native", "chat")
284
- : requestPathForLane("chat", "bridge", upstreamWireForAdapter(settledRoute.provider.adapter)),
294
+ : requestPathForLane("chat", directRoute ? "direct" : "bridge", upstreamWireForAdapter(settledRoute.provider.adapter)),
285
295
  features: envelope.features(),
286
296
  policy: "reject",
287
297
  });
@@ -294,8 +304,15 @@ async function handleChatCompletionsWithBudget(
294
304
  }
295
305
  markProtocolEntry(logCtx, {
296
306
  inbound: "chat",
297
- lane: chatNativeRoute ? "native" : "bridge",
298
- reasonCodes: !chatNativeRoute && nativeDecline ? [nativeDecline] : [],
307
+ lane: chatNativeRoute ? "native" : directRoute ? "direct" : "bridge",
308
+ reasonCodes: [
309
+ ...(!chatNativeRoute && !directRoute && nativeDecline ? [nativeDecline] : []),
310
+ // Only a decline that says something about this route: with the switch off or a
311
+ // non-Antigravity target the path reason already explains the bridge.
312
+ ...(antigravityDecline && antigravityDecline !== "rollout-disabled" && antigravityDecline !== "not-migrated"
313
+ ? [antigravityDecline]
314
+ : []),
315
+ ],
299
316
  features: envelope ? () => envelope.features() : () => featuresFromChatBody(chatBody),
300
317
  });
301
318
  recordProtocolShadowPlan(logCtx, config, { inbound: "chat", model: requestedModel });
@@ -314,10 +331,18 @@ async function handleChatCompletionsWithBudget(
314
331
  }
315
332
 
316
333
  let internalBody: Rec;
334
+ let directConversation: ChatIrConversation | undefined;
317
335
  try {
318
336
  // Validate the full Chat boundary after routing. Native Chat keeps `chatBody` as
319
337
  // its wire source; this Responses projection is used only by the fallback path.
320
- internalBody = chatToResponsesBody(chatBody);
338
+ // The direct lane validates the same boundary while decoding the conversation into the IR,
339
+ // and sends the core only the request settings.
340
+ if (directRoute) {
341
+ directConversation = chatConversationToIr(chatBody, chatBody.model as string);
342
+ internalBody = chatSettingsBody(chatBody);
343
+ } else {
344
+ internalBody = chatToResponsesBody(chatBody);
345
+ }
321
346
  if (effortRow) {
322
347
  internalBody.reasoning = {
323
348
  ...(isRec(internalBody.reasoning) ? internalBody.reasoning : {}),
@@ -370,6 +395,17 @@ async function handleChatCompletionsWithBudget(
370
395
  }
371
396
 
372
397
  const visionDescribeTerminal = req.headers.get("x-opencodex-vision-describe") === "1";
398
+ // Terminal advisor marker: the advisor sidecar's own loopback consultation re-enters through
399
+ // this surface. The bridge rebuilds headers from the FORWARD_HEADERS allowlist, which would
400
+ // drop the raw marker header — so the fact is detected here and carried as an option flag
401
+ // (same structure as the vision-describe fence above, depth cap 1).
402
+ //
403
+ // The header value is NOT evidence by itself: any external caller can send it, and the peer
404
+ // address proves nothing (Docker/WSL/tunnels/port-forwarding end on loopback). Internal
405
+ // authority comes from a process-owned capability minted at random per process and compared in
406
+ // constant time, so a forged header — or a token captured from an older process — is treated
407
+ // as an ordinary external request.
408
+ const advisorInternal = isInternalCallCapability(req.headers.get(ADVISOR_INTERNAL_CAPABILITY_HEADER));
373
409
  // Concrete helper targets must fail before optional stored-main credential enrichment.
374
410
  // Unresolved combos are checked after their concrete child route is selected in Responses.
375
411
  if (settledRoute && !settledRoute.combo && isCanonicalOpenAiForwardProvider(settledRoute.provider)
@@ -455,6 +491,10 @@ async function handleChatCompletionsWithBudget(
455
491
  abortSignal: req.signal,
456
492
  // Body is Responses-shaped by now, but the client spoke Chat Completions.
457
493
  inboundWire: "chat",
494
+ // A Chat caller may send an explicit empty `tools` array — a deny-all declaration the
495
+ // translation drops along with the whole catalog. Carried separately so the
496
+ // undeclared-tool guard can still enforce it; absent/partial catalogs stay deferred.
497
+ clientDeclaredEmptyToolCatalog: Array.isArray(chatBody.tools) && chatBody.tools.length === 0,
458
498
  ...((settledRoute?.combo || settledRoute?.routeKind === "policy") && droidDefaultEffort
459
499
  ? { droidDefaultEffort }
460
500
  : {}),
@@ -468,13 +508,35 @@ async function handleChatCompletionsWithBudget(
468
508
  // headers from the FORWARD_HEADERS allowlist, which would drop the raw
469
509
  // header — so the fact is detected here and carried as an option flag.
470
510
  ...(visionDescribeTerminal ? { visionDescribeTerminal: true } : {}),
511
+ ...(advisorInternal ? { advisorInternal: true } : {}),
471
512
  translatorBudget,
472
513
  ...(logIds ? { onFirstOutput: () => recordFirstOutput(logCtx, logIds.start) } : {}),
473
514
  onNativePassthroughTerminal: status => finalizeNativeLog(httpStatusForRequestLogTerminal(status, logCtx), { terminalStatus: status, closeReason: "terminal" }),
474
515
  onNativePassthroughCancel: () => finalizeNativeLog(499, { closeReason: "client_cancel" }),
475
- ...(directEncodersApply(config, settledRoute)
476
- ? { clientEncoder: { protocol: "chat" as const, stream, model: requestedModel } }
477
- : {}),
516
+ // The direct lane answers through the direct encoder whatever `directEncoders` says: the
517
+ // point of the lane is that neither direction passes through the internal Responses body.
518
+ ...(directRoute && directConversation
519
+ ? {
520
+ clientEncoder: { protocol: "chat" as const, stream, model: requestedModel, requestLane: "direct" as const },
521
+ directRequest: {
522
+ protocol: "chat" as const,
523
+ providerName: directRoute.providerName,
524
+ modelId: directRoute.modelId,
525
+ evidence: chatIrRouteEvidence(directConversation),
526
+ decode: base => withDirectChatConversation(base, chatBody, directConversation!),
527
+ bridgeConversation: () => {
528
+ const bridge = chatToResponsesBody(chatBody);
529
+ return {
530
+ input: bridge.input,
531
+ ...(bridge.instructions !== undefined ? { instructions: bridge.instructions } : {}),
532
+ ...(bridge.tools !== undefined ? { tools: bridge.tools } : {}),
533
+ };
534
+ },
535
+ },
536
+ }
537
+ : directEncodersApply(config, settledRoute)
538
+ ? { clientEncoder: { protocol: "chat" as const, stream, model: requestedModel } }
539
+ : {}),
478
540
  });
479
541
  // Already in the Chat wire: no conversion. A direct-encoder body (PF-09) reports its own log
480
542
  // facts to the deferred request log. A native combo child (PF-07) carries none: its row is
@@ -543,6 +605,10 @@ async function handleChatCompletionsWithBudget(
543
605
  // Structured model_not_found must win over classifyError's generic remaps.
544
606
  classified.code = "model_not_found";
545
607
  classified.type = "invalid_request_error";
608
+ } else if (upstreamCode === UNDECLARED_TOOL_CALL_ERROR_CODE) {
609
+ // The guard's deterministic refusal keeps its wire identity — same code the
610
+ // streamed `response.failed` frame and the Anthropic surface carry.
611
+ classified.code = UNDECLARED_TOOL_CALL_ERROR_CODE;
546
612
  } else if (upstreamCode !== undefined && upstreamCode !== null && classified.code == null) {
547
613
  classified.code = upstreamCode;
548
614
  }
@@ -21,6 +21,13 @@ import { normalizeAnthropicImages } from "../adapters/anthropic-image-normalize"
21
21
  import { createToolCallIdAllocator } from "../adapters/tool-call-id";
22
22
  import { openAIChatSerializesThinking } from "../adapters/openai-chat/messages";
23
23
  import { messagesToResponsesTranslation } from "../protocols/codecs/messages";
24
+ import {
25
+ messagesDirectPlanHolds,
26
+ messagesDirectRequestOption,
27
+ messagesDirectTraceDecline,
28
+ planMessagesAntigravityDirect,
29
+ type MessagesDirectPlan,
30
+ } from "./messages-antigravity-direct";
24
31
  import { AnthropicRequestError, DesktopModelMappingUnavailableError, extractOcxEffortDirective, extractOcxRouteDirective, nativeAnthropicProjection, resolveInboundModel, type ClaudeCacheKeySource } from "../claude/inbound";
25
32
  import { nativeReasoningTag, type NativeReasoningOwner } from "../responses/reasoning-replay-cache";
26
33
  import { isKnownDesktop3pModelId, resolveDesktop3pAlias } from "../claude/desktop-3p";
@@ -40,6 +47,7 @@ import {
40
47
  REPLAY_REFUSED_STATUS,
41
48
  UPSTREAM_RESET_REPLAY_REFUSED_CODE,
42
49
  } from "../lib/upstream-retry";
50
+ import { UNDECLARED_TOOL_CALL_ERROR_CODE } from "../lib/errors";
43
51
  import { resolveClientRetryAfter } from "../lib/retry-after";
44
52
  import { anthropicRateLimitHeaders } from "./anthropic-rate-limit-headers";
45
53
  import { upstreamMessagesRequestIdHeaders } from "./messages-response-headers";
@@ -850,6 +858,21 @@ export async function handleClaudeMessages(
850
858
  }
851
859
  }
852
860
 
861
+ /** The client answer for a Messages body the translation (or the direct decode) refused. */
862
+ function translationFailure(err: unknown, logCtx: RequestLogContext, logIds: { requestId: string; start: number } | undefined): Response {
863
+ const overflow = isTranslatorBudgetExceededError(err);
864
+ const unavailable = err instanceof DesktopModelMappingUnavailableError;
865
+ const status = overflow ? 413 : unavailable ? 503 : err instanceof AnthropicRequestError ? 400 : 500;
866
+ if (logIds) addFinalRequestLog(logIds.requestId, logIds.start, logCtx, status, { closeReason: "non_stream" });
867
+ if (unavailable) return desktopMappingUnavailableResponse(err);
868
+ return anthropicErrorResponse(
869
+ status,
870
+ overflow ? "request translation buffer exceeded the safe limit" : err instanceof Error ? err.message : String(err),
871
+ overflow ? "request_too_large" : undefined,
872
+ overflow ? "translation_buffer_limit" : undefined,
873
+ );
874
+ }
875
+
853
876
  /**
854
877
  * Translate a Claude Messages request, route it through the Responses pipeline,
855
878
  * and translate the reply back. Runs under a translator budget owned by the
@@ -885,6 +908,11 @@ async function handleClaudeMessagesWithBudget(
885
908
  let effortRow: ParsedEffortRowId | null = null;
886
909
  let fastRow: ParsedFastRowId | null = null;
887
910
  let requestedModel = "";
911
+ // Antigravity direct lane (off by default): planned before translation, kept only if routing
912
+ // settles on the route the plan was made for.
913
+ let directPlan: MessagesDirectPlan | undefined;
914
+ /** The retained charge for the settings-only body, handed back if the plan is left. */
915
+ let directSettingsBodyBytes = 0;
888
916
  // Built only under the reject policy; the legacy default leaves this request untouched.
889
917
  let envelope: ProtocolEnvelope | undefined;
890
918
  let messagesFeatures: () => Iterable<ProtocolFeature> = () => [];
@@ -1034,9 +1062,16 @@ async function handleClaudeMessagesWithBudget(
1034
1062
  };
1035
1063
  delete anthropicBody.thinking;
1036
1064
  }
1037
- let translation;
1065
+ // On the direct lane the core receives only the request settings; the conversation is
1066
+ // already decoded into the IR. Without a plan the translation below runs as before.
1067
+ directPlan = planMessagesAntigravityDirect(anthropicBody, cc, config, translatorBudget);
1068
+ let translation = directPlan && {
1069
+ body: directPlan.settingsBody,
1070
+ cacheKeySource: directPlan.cacheKeySource,
1071
+ nativeReasoningReplay: directPlan.conversation.nativeReasoningReplay,
1072
+ };
1038
1073
  try {
1039
- translation = messagesToResponsesTranslation(anthropicBody, cc, translatorBudget);
1074
+ translation ??= messagesToResponsesTranslation(anthropicBody, cc, translatorBudget);
1040
1075
  } catch (err) {
1041
1076
  // A native Messages route can discard an undecodable proxy envelope. Keep the
1042
1077
  // original error for any route that ultimately translates to Responses.
@@ -1052,21 +1087,13 @@ async function handleClaudeMessagesWithBudget(
1052
1087
  // fields only, so the caller intent is applied to the TRANSLATED body rather than the
1053
1088
  // inbound one.
1054
1089
  if (fastRow) internalBody.service_tier = "priority";
1055
- translatorBudget.chargeRetained(jsonUtf8Bytes(internalBody), { kind: "request_copies" });
1090
+ const internalBodyBytes = jsonUtf8Bytes(internalBody);
1091
+ translatorBudget.chargeRetained(internalBodyBytes, { kind: "request_copies" });
1092
+ if (directPlan) directSettingsBodyBytes = internalBodyBytes;
1056
1093
  cacheKeySource = translation.cacheKeySource;
1057
1094
  nativeReasoningReplay = translation.nativeReasoningReplay;
1058
1095
  } catch (err) {
1059
- const overflow = isTranslatorBudgetExceededError(err);
1060
- const unavailable = err instanceof DesktopModelMappingUnavailableError;
1061
- const status = overflow ? 413 : unavailable ? 503 : err instanceof AnthropicRequestError ? 400 : 500;
1062
- if (logIds) addFinalRequestLog(logIds.requestId, logIds.start, logCtx, status, { closeReason: "non_stream" });
1063
- if (unavailable) return desktopMappingUnavailableResponse(err);
1064
- return anthropicErrorResponse(
1065
- status,
1066
- overflow ? "request translation buffer exceeded the safe limit" : err instanceof Error ? err.message : String(err),
1067
- overflow ? "request_too_large" : undefined,
1068
- overflow ? "translation_buffer_limit" : undefined,
1069
- );
1096
+ return translationFailure(err, logCtx, logIds);
1070
1097
  }
1071
1098
 
1072
1099
  if (!requestedModel) requestedModel = (anthropicBody as Rec).model as string;
@@ -1119,8 +1146,30 @@ async function handleClaudeMessagesWithBudget(
1119
1146
  }
1120
1147
  return requestTokenFloor;
1121
1148
  };
1149
+ /** Leave the direct plan: the core gets the full bridge translation, as without the switch. */
1150
+ const leaveDirectPlan = (): unknown => {
1151
+ // Hand back everything the plan retained: its decode and the settings-only body it replaces.
1152
+ directPlan?.releaseDecodeCharges();
1153
+ directPlan = undefined;
1154
+ nativeReasoningReplay = undefined;
1155
+ translatorBudget.releaseRetained(directSettingsBodyBytes, { kind: "request_copies" });
1156
+ directSettingsBodyBytes = 0;
1157
+ try {
1158
+ const translation = messagesToResponsesTranslation(anthropicBody, cc, translatorBudget);
1159
+ internalBody = translation.body;
1160
+ if (fastRow) internalBody.service_tier = "priority";
1161
+ internalBody.stream = true;
1162
+ translatorBudget.chargeRetained(jsonUtf8Bytes(internalBody), { kind: "request_copies" });
1163
+ cacheKeySource = translation.cacheKeySource;
1164
+ nativeReasoningReplay = translation.nativeReasoningReplay;
1165
+ return undefined;
1166
+ } catch (err) {
1167
+ return err;
1168
+ }
1169
+ };
1170
+ let directLeaveError: unknown;
1122
1171
  try {
1123
- const route = routeModel(config, internalBody.model as string, evidenceFromBody(internalBody));
1172
+ const route = routeModel(config, internalBody.model as string, directPlan ? directPlan.evidence : evidenceFromBody(internalBody));
1124
1173
  // Settle the wire once so the sampling decision below reads the effective
1125
1174
  // adapter rather than the provider-wide default (#404).
1126
1175
  route.staticPolicy = captureRouteStaticPolicy(
@@ -1138,6 +1187,8 @@ async function handleClaudeMessagesWithBudget(
1138
1187
  route.provider = resolveWireProtocolOverride(route.providerName, route.modelId, route.provider, "anthropic", route.staticPolicy);
1139
1188
  logCtx.routeDecision = route.routeDecision;
1140
1189
  settledRoute = route;
1190
+ // Before any route-specific shaping, so a request that leaves the lane is shaped as the bridge's.
1191
+ if (directPlan && !messagesDirectPlanHolds(directPlan, route, anthropicBody as Rec, config)) directLeaveError = leaveDirectPlan();
1141
1192
  if (directEncodersApply(config, route)) {
1142
1193
  clientEncoder = { protocol: "messages", stream, model: requestedModel, inputTokenFloor: claudeRequestTokenFloor() };
1143
1194
  }
@@ -1183,6 +1234,14 @@ async function handleClaudeMessagesWithBudget(
1183
1234
  }
1184
1235
  /* unknown model: let handleResponses shape the 404 */
1185
1236
  }
1237
+ if (directPlan && !settledRoute) directLeaveError ??= leaveDirectPlan();
1238
+ if (directLeaveError !== undefined) return translationFailure(directLeaveError, logCtx, logIds);
1239
+ const directRoute = directPlan && settledRoute ? settledRoute : undefined;
1240
+ // The direct lane answers through the direct encoder whatever `directEncoders` says: neither
1241
+ // direction passes through the internal Responses body.
1242
+ if (directRoute) {
1243
+ clientEncoder = { protocol: "messages", stream, model: requestedModel, inputTokenFloor: claudeRequestTokenFloor(), requestLane: "direct" };
1244
+ }
1186
1245
 
1187
1246
  // PF-08: a managed-key Anthropic route sends its Messages body natively. The caller-forward
1188
1247
  // passthrough above was decided on the caller's own credential and never reaches this point.
@@ -1206,11 +1265,20 @@ async function handleClaudeMessagesWithBudget(
1206
1265
  return anthropicErrorResponse(400, nativeProjectionOnlyError.message, "invalid_request_error");
1207
1266
  }
1208
1267
  // With the switch on, a declined route says why on its bridge mark, as native Chat does.
1209
- if (nativeDecline && nativeDecline !== "rollout-disabled") {
1268
+ const antigravityDecline = settledRoute && !nativeMessagesRoute && !directRoute
1269
+ ? messagesDirectTraceDecline(settledRoute, anthropicBody, String(internalBody.model ?? ""), config)
1270
+ : undefined;
1271
+ if (directRoute) {
1272
+ markProtocolEntry(logCtx, { inbound: "messages", lane: "direct", features: messagesFeatures });
1273
+ } else if ((nativeDecline && nativeDecline !== "rollout-disabled") || antigravityDecline) {
1210
1274
  markProtocolEntry(logCtx, {
1211
1275
  inbound: "messages",
1212
1276
  lane: "bridge",
1213
- reasonCodes: [...(effortRow ? ["effort-row" as const] : fastRow ? ["fast-row" as const] : []), nativeDecline],
1277
+ reasonCodes: [
1278
+ ...(effortRow ? ["effort-row" as const] : fastRow ? ["fast-row" as const] : []),
1279
+ ...(nativeDecline && nativeDecline !== "rollout-disabled" ? [nativeDecline] : []),
1280
+ ...(antigravityDecline ? [antigravityDecline] : []),
1281
+ ],
1214
1282
  features: messagesFeatures,
1215
1283
  });
1216
1284
  }
@@ -1218,7 +1286,11 @@ async function handleClaudeMessagesWithBudget(
1218
1286
  if (envelope && settledRoute && !settledRoute.combo && settledRoute.routeKind !== "policy") {
1219
1287
  const verdict = checkRepresentable({
1220
1288
  inbound: "messages",
1221
- requestPath: requestPathForLane("messages", nativeMessagesRoute ? "native" : "bridge", upstreamWireForAdapter(settledRoute.provider.adapter)),
1289
+ requestPath: requestPathForLane(
1290
+ "messages",
1291
+ nativeMessagesRoute ? "native" : directRoute ? "direct" : "bridge",
1292
+ upstreamWireForAdapter(settledRoute.provider.adapter),
1293
+ ),
1222
1294
  features: envelope.features(),
1223
1295
  policy: "reject",
1224
1296
  });
@@ -1348,6 +1420,12 @@ async function handleClaudeMessagesWithBudget(
1348
1420
  // Without this the replay would look native and a Responses-scoped wire default
1349
1421
  // would fire, disagreeing with the pre-flight decision above.
1350
1422
  inboundWire: "anthropic",
1423
+ // An explicit empty `tools` array is a deny-all declaration the translation drops with
1424
+ // the rest of the catalog; carried separately so the undeclared-tool guard can still
1425
+ // enforce it while partial and absent catalogs stay deferred.
1426
+ clientDeclaredEmptyToolCatalog: isRec(anthropicBody)
1427
+ && Array.isArray(anthropicBody.tools)
1428
+ && anthropicBody.tools.length === 0,
1351
1429
  nativeReasoningReplay,
1352
1430
  nativeReasoningMint,
1353
1431
  claudeGoAffinity: { sessionLane: claudeGoSessionLane },
@@ -1362,6 +1440,9 @@ async function handleClaudeMessagesWithBudget(
1362
1440
  onNativePassthroughTerminal: status => finalizeNativeLog(httpStatusForRequestLogTerminal(status, logCtx), { terminalStatus: status, closeReason: "terminal" }),
1363
1441
  onNativePassthroughCancel: () => finalizeNativeLog(499, { closeReason: "client_cancel" }),
1364
1442
  ...(clientEncoder ? { clientEncoder } : {}),
1443
+ ...(directRoute && directPlan
1444
+ ? { directRequest: messagesDirectRequestOption(directPlan, anthropicBody, cc, translatorBudget) }
1445
+ : {}),
1365
1446
  });
1366
1447
  const response = logIds ? responseWithDeferredRequestLog(upstream, logIds.requestId, logIds.start, logCtx) : upstream;
1367
1448
  // Already in the Messages wire (direct encoder): no conversion.
@@ -1374,11 +1455,13 @@ async function handleClaudeMessagesWithBudget(
1374
1455
  // Re-shape the OpenAI-style error envelope into the Anthropic one, preserving status.
1375
1456
  let message = `upstream error (${response.status})`;
1376
1457
  let contextError = false;
1458
+ let catalogRefusal = false;
1377
1459
  try {
1378
1460
  const text = await response.text();
1379
1461
  try {
1380
1462
  const parsed = JSON.parse(text) as { error?: { message?: string; type?: string; code?: unknown } | string; message?: string };
1381
1463
  contextError = typeof parsed?.error === "object" && parsed.error?.code === "context_length_exceeded";
1464
+ catalogRefusal = typeof parsed?.error === "object" && parsed.error?.code === UNDECLARED_TOOL_CALL_ERROR_CODE;
1382
1465
  const nested = typeof parsed?.error === "object" && parsed.error ? parsed.error.message : undefined;
1383
1466
  const flat = typeof parsed?.error === "string" ? parsed.error : parsed?.message;
1384
1467
  message = nested || flat || (text ? `upstream error (${response.status}): ${text.slice(0, 400)}` : message);
@@ -1407,7 +1490,10 @@ async function handleClaudeMessagesWithBudget(
1407
1490
  const nativeMainFence = response.status === 503
1408
1491
  && upstreamRetryAfter?.trim() === "1"
1409
1492
  && message === CODEX_MAIN_PROFILE_MAINTENANCE_MESSAGE;
1410
- const transient = !replayRefusal && !nativeMainFence && !contextError && isTransientUpstreamStatus(response.status);
1493
+ // The undeclared-tool guard's refusal is deterministic: the same request produces the
1494
+ // same verdict, so it must not arrive as a retryable overload like a transient 5xx.
1495
+ const transient = !replayRefusal && !nativeMainFence && !contextError && !catalogRefusal
1496
+ && isTransientUpstreamStatus(response.status);
1411
1497
  const outStatus = replayRefusal
1412
1498
  ? REPLAY_REFUSED_STATUS
1413
1499
  : nativeMainFence ? 503 : contextError ? 400 : transient ? 529 : response.status;
@@ -1419,7 +1505,11 @@ async function handleClaudeMessagesWithBudget(
1419
1505
  outStatus,
1420
1506
  message,
1421
1507
  undefined,
1422
- replayRefusal ? UPSTREAM_RESET_REPLAY_REFUSED_CODE : contextError ? "context_length_exceeded" : undefined,
1508
+ replayRefusal
1509
+ ? UPSTREAM_RESET_REPLAY_REFUSED_CODE
1510
+ : contextError
1511
+ ? "context_length_exceeded"
1512
+ : catalogRefusal ? UNDECLARED_TOOL_CALL_ERROR_CODE : undefined,
1423
1513
  )), {
1424
1514
  status: outStatus,
1425
1515
  headers: outHeaders,