@bitkyc08/opencodex 2.59.0 → 2.61.0-preview.20260922

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 (374) hide show
  1. package/AGENTS_INSTALL.md +64 -0
  2. package/README.md +28 -1
  3. package/bin/ocx.mjs +382 -209
  4. package/gui/dist/assets/App-E64Rzjap.js +50 -0
  5. package/gui/dist/assets/Tray-_nfzD8k4.js +1 -0
  6. package/gui/dist/assets/index-DpdfZWMK.js +86 -0
  7. package/gui/dist/assets/index-_bpvxJu0.css +1 -0
  8. package/gui/dist/assets/usage-companion-chart-DtoK7T6h.js +1 -0
  9. package/gui/dist/favicon.png +0 -0
  10. package/gui/dist/index.html +2 -2
  11. package/gui/dist/provider-icons/stepfun-color.svg +1 -0
  12. package/package.json +8 -1
  13. package/src/adapters/anthropic-image-codec.ts +16 -2
  14. package/src/adapters/anthropic-image-normalize.ts +49 -2
  15. package/src/adapters/anthropic.ts +20 -1
  16. package/src/adapters/base.ts +23 -0
  17. package/src/adapters/coding-agent/protocol.ts +36 -6
  18. package/src/adapters/coding-agent/turn.ts +32 -4
  19. package/src/adapters/command-code.ts +52 -4
  20. package/src/adapters/cursor/catalog.ts +51 -7
  21. package/src/adapters/cursor/checkpoint-store.ts +3 -0
  22. package/src/adapters/cursor/discovery.ts +11 -8
  23. package/src/adapters/cursor/live-transport.ts +26 -9
  24. package/src/adapters/cursor/protobuf-request.ts +6 -3
  25. package/src/adapters/cursor/request-builder.ts +20 -4
  26. package/src/adapters/cursor/transport.ts +19 -0
  27. package/src/adapters/cursor.ts +14 -3
  28. package/src/adapters/declaration-carrier.ts +45 -0
  29. package/src/adapters/devin/cloud-direct/chat.ts +3 -1
  30. package/src/adapters/devin/cloud-direct/stated-reset-retry.ts +42 -5
  31. package/src/adapters/devin.ts +125 -36
  32. package/src/adapters/google-antigravity-replay.ts +1 -1
  33. package/src/adapters/google-antigravity-wire.ts +34 -7
  34. package/src/adapters/google-errors.ts +7 -1
  35. package/src/adapters/google-http.ts +49 -10
  36. package/src/adapters/google-tool-schema.ts +595 -31
  37. package/src/adapters/google-wire-compiler.ts +93 -10
  38. package/src/adapters/google-wire-shape.ts +461 -0
  39. package/src/adapters/google.ts +66 -11
  40. package/src/adapters/image.ts +4 -1
  41. package/src/adapters/input-media-guard.ts +21 -9
  42. package/src/adapters/kiro/usage.ts +3 -2
  43. package/src/adapters/kiro-tool-fallback.ts +1 -1
  44. package/src/adapters/ollama-native.ts +6 -0
  45. package/src/adapters/openai-chat/developer-role.ts +61 -0
  46. package/src/adapters/openai-chat/messages.ts +46 -27
  47. package/src/adapters/openai-chat/parallel-tool-calls.ts +32 -0
  48. package/src/adapters/openai-chat/passthrough.ts +33 -9
  49. package/src/adapters/openai-chat/reasoning-wire.ts +89 -0
  50. package/src/adapters/openai-chat-images.ts +3 -1
  51. package/src/adapters/openai-chat.ts +23 -58
  52. package/src/adapters/openai-responses/image-gen.ts +8 -6
  53. package/src/adapters/openai-responses/passthrough.ts +17 -3
  54. package/src/adapters/openai-responses/reasoning.ts +7 -0
  55. package/src/adapters/opencode-go-additional-tools.ts +12 -2
  56. package/src/adapters/registry.ts +3 -2
  57. package/src/adapters/run-turn-queue.ts +178 -29
  58. package/src/adapters/xai-web-search.ts +16 -1
  59. package/src/bridge/errors.ts +8 -2
  60. package/src/bridge/response-json.ts +9 -1
  61. package/src/bridge/sse.ts +13 -151
  62. package/src/chat/inbound.ts +141 -5
  63. package/src/claude/desktop-3p.ts +7 -1
  64. package/src/claude/desktop-first-party.ts +183 -0
  65. package/src/claude/desktop-gateway-state.ts +41 -0
  66. package/src/claude/inbound-content-options.ts +6 -0
  67. package/src/claude/inbound.ts +32 -6
  68. package/src/claude/intercept/connect-proxy.ts +179 -0
  69. package/src/claude/intercept/listener.ts +122 -0
  70. package/src/claude/intercept/local-ca.ts +298 -0
  71. package/src/claude/intercept/runtime.ts +98 -0
  72. package/src/claude/intercept/settings.ts +189 -0
  73. package/src/cli/access.ts +87 -0
  74. package/src/cli/account-auth.ts +19 -0
  75. package/src/cli/account-extended.ts +4 -4
  76. package/src/cli/capabilities.ts +31 -0
  77. package/src/cli/claude-desktop.ts +206 -16
  78. package/src/cli/codex-shim-autorestore.ts +3 -0
  79. package/src/cli/companion.ts +56 -0
  80. package/src/cli/dispatch.ts +46 -7
  81. package/src/cli/doctor.ts +28 -9
  82. package/src/cli/ensure-desired-integrations.ts +43 -5
  83. package/src/cli/help.ts +7 -9
  84. package/src/cli/hub.ts +3 -2
  85. package/src/cli/index.ts +203 -59
  86. package/src/cli/init.ts +8 -0
  87. package/src/cli/integrations.ts +7 -1
  88. package/src/cli/opencode.ts +2 -2
  89. package/src/cli/provider.ts +13 -1
  90. package/src/cli/registry.ts +41 -2
  91. package/src/cli/resolve.ts +230 -0
  92. package/src/cli/root.ts +24 -1
  93. package/src/cli/start-ownership-publication.ts +56 -0
  94. package/src/cli/status-probes.ts +2 -18
  95. package/src/cli/status.ts +62 -0
  96. package/src/cli/stop-report.ts +143 -0
  97. package/src/cli/uninstall-plan.ts +9 -0
  98. package/src/client/machine-api.ts +2 -2
  99. package/src/client/machine-listener.ts +4 -7
  100. package/src/client/runtime.ts +26 -2
  101. package/src/clients/aside-profiles.ts +4 -0
  102. package/src/clients/config-export/zcode-store.ts +157 -0
  103. package/src/clients/config-export.ts +36 -0
  104. package/src/codex/account-store.ts +65 -0
  105. package/src/codex/app-server-processes.ts +72 -40
  106. package/src/codex/auth-api/account-list.ts +19 -11
  107. package/src/codex/auth-api/login-flow.ts +6 -1
  108. package/src/codex/auth-api/pool-quota-probe.ts +30 -7
  109. package/src/codex/autostart-health.ts +28 -0
  110. package/src/codex/catalog/build-entries.ts +2 -2
  111. package/src/codex/catalog/effort.ts +3 -3
  112. package/src/codex/catalog/gather-capture.ts +21 -2
  113. package/src/codex/catalog/model-hints.ts +29 -28
  114. package/src/codex/catalog/parsing.ts +7 -0
  115. package/src/codex/catalog/provider-models.ts +43 -17
  116. package/src/codex/catalog/retained-sync.ts +24 -28
  117. package/src/codex/catalog/routed-gather.ts +19 -0
  118. package/src/codex/context-compat.ts +5 -2
  119. package/src/codex/convergence.ts +2 -2
  120. package/src/codex/desired-state.ts +4 -1
  121. package/src/codex/history-job.ts +6 -6
  122. package/src/codex/history-provider.ts +31 -166
  123. package/src/codex/history-rollout-read.ts +174 -0
  124. package/src/codex/inject/config-toml.ts +41 -6
  125. package/src/codex/inject/paginated-openai-compat.ts +90 -0
  126. package/src/codex/inject.ts +18 -15
  127. package/src/codex/injected-marker.ts +18 -0
  128. package/src/codex/internal/catalog-writer.ts +33 -1
  129. package/src/codex/main-account.ts +6 -0
  130. package/src/codex/model-cache.ts +99 -6
  131. package/src/codex/model-entitlement-admission.ts +59 -0
  132. package/src/codex/model-entitlements.ts +116 -54
  133. package/src/codex/native-main-admission.ts +83 -0
  134. package/src/codex/observed-model-denials.ts +101 -8
  135. package/src/codex/prompt-text-probe.ts +9 -6
  136. package/src/codex/routing/health-store.ts +39 -0
  137. package/src/codex/routing/selection.ts +37 -1
  138. package/src/codex/routing.ts +12 -42
  139. package/src/codex/shim-templates.ts +29 -3
  140. package/src/codex/shim.ts +1 -1
  141. package/src/codex/subagent-model-fallback.ts +22 -4
  142. package/src/combos/failover.ts +3 -0
  143. package/src/companion/settings.ts +132 -0
  144. package/src/config/admitted-identity.ts +222 -0
  145. package/src/config/atomic-write.ts +117 -5
  146. package/src/config/diagnostics.ts +22 -1
  147. package/src/config/feature-flags.ts +5 -0
  148. package/src/config/load-degrade.ts +52 -7
  149. package/src/config/process-state.ts +1 -1
  150. package/src/config/proxy-env.ts +8 -2
  151. package/src/config/schema/compaction-triggers.ts +11 -0
  152. package/src/config/schema/config-schema.ts +31 -1
  153. package/src/config/schema/leaf-validators.ts +59 -0
  154. package/src/config.ts +3 -3
  155. package/src/generated/compatibility-version.json +604 -280
  156. package/src/grok/reset-coupons.ts +38 -19
  157. package/src/images/loop.ts +6 -1
  158. package/src/integrations/aside-profile-context.ts +37 -3
  159. package/src/integrations/aside-profile-journal.ts +68 -3
  160. package/src/integrations/aside-profiles.ts +128 -3
  161. package/src/integrations/config-io.ts +44 -10
  162. package/src/integrations/merge.ts +120 -13
  163. package/src/integrations/mutation-plan.ts +921 -0
  164. package/src/integrations/registry.ts +38 -0
  165. package/src/integrations/state.ts +78 -45
  166. package/src/integrations/target.ts +208 -0
  167. package/src/integrations/writer.ts +134 -110
  168. package/src/lab/conformance/fixture-provider.ts +5 -0
  169. package/src/lab/live/transport.ts +4 -0
  170. package/src/lab/live/types.ts +5 -0
  171. package/src/lab/subject/behavior-fingerprint.ts +1 -1
  172. package/src/lib/admin-secrets.ts +9 -1
  173. package/src/lib/browser-launch-notice.ts +59 -0
  174. package/src/lib/bun-runtime.ts +6 -2
  175. package/src/lib/debug-log-buffer.ts +6 -1
  176. package/src/lib/debug.ts +63 -0
  177. package/src/lib/errors.ts +79 -0
  178. package/src/lib/http-response-semantics.ts +57 -0
  179. package/src/lib/lab-live-pinned-sender.ts +26 -12
  180. package/src/lib/open-url.ts +51 -7
  181. package/src/lib/package-tree-integrity.ts +2 -1
  182. package/src/lib/package-version.ts +8 -0
  183. package/src/lib/pinned-http.ts +142 -2
  184. package/src/lib/plain-data.ts +103 -0
  185. package/src/lib/process-control.ts +13 -5
  186. package/src/lib/provider-egress.ts +310 -0
  187. package/src/lib/provider-outbound.ts +109 -16
  188. package/src/lib/proxy-env.ts +82 -7
  189. package/src/lib/request-execution-budget.ts +72 -0
  190. package/src/lib/request-failure-attribution.ts +183 -0
  191. package/src/lib/request-failure-model.ts +236 -0
  192. package/src/lib/request-resend-gate.ts +138 -0
  193. package/src/lib/socks5-fetch.ts +136 -26
  194. package/src/lib/spend-ledger-owner.ts +364 -0
  195. package/src/lib/spend-reservation-ledger.ts +218 -27
  196. package/src/lib/standalone.ts +16 -0
  197. package/src/lib/upstream-retry.ts +167 -16
  198. package/src/lib/windows-system-proxy.ts +16 -11
  199. package/src/lib/winsw.ts +2 -2
  200. package/src/oauth/callback-server.ts +4 -3
  201. package/src/oauth/generic-account-failover.ts +1 -0
  202. package/src/oauth/health.ts +12 -1
  203. package/src/oauth/index.ts +27 -108
  204. package/src/oauth/login-cli.ts +80 -29
  205. package/src/oauth/login-flow-state.ts +127 -0
  206. package/src/providers/api-key-resolve.ts +133 -0
  207. package/src/providers/api-key-selection.ts +5 -1
  208. package/src/providers/derive.ts +34 -17
  209. package/src/providers/devin-cli-authmode-migration.ts +14 -10
  210. package/src/providers/key-failover.ts +97 -19
  211. package/src/providers/key-store.ts +34 -110
  212. package/src/providers/model-rename-fields.ts +147 -0
  213. package/src/providers/model-rename-migration.ts +179 -38
  214. package/src/providers/model-rename-startup.ts +7 -5
  215. package/src/providers/openai-virtual-models.ts +42 -2
  216. package/src/providers/quota/antigravity.ts +22 -2
  217. package/src/providers/quota/vendor-probes-key.ts +38 -23
  218. package/src/providers/reasoning-metadata.ts +43 -18
  219. package/src/providers/registry/entries-core.ts +47 -20
  220. package/src/providers/registry/entries-extended.ts +63 -4
  221. package/src/providers/registry/model-ids.ts +168 -0
  222. package/src/providers/registry/model-seeds.ts +56 -10
  223. package/src/providers/registry/types.ts +2 -0
  224. package/src/providers/resolved-model-policy-merge.ts +167 -0
  225. package/src/providers/resolved-model-policy.ts +406 -0
  226. package/src/providers/stale-vision-classification-migration.ts +137 -0
  227. package/src/providers/xai-transport.ts +12 -1
  228. package/src/reasoning-effort.ts +8 -0
  229. package/src/responses/apply-patch-envelope.ts +0 -12
  230. package/src/responses/freeform-wrapper-scan.ts +279 -0
  231. package/src/responses/function-call-compat.ts +38 -1
  232. package/src/responses/inline-document.ts +65 -0
  233. package/src/responses/input-media.ts +42 -8
  234. package/src/responses/legacy-dotted-tool-name-repair.ts +134 -0
  235. package/src/responses/muse-tool-name-alias.ts +19 -0
  236. package/src/responses/parser-content.ts +8 -2
  237. package/src/responses/parser-tools.ts +3 -0
  238. package/src/responses/parser.ts +3 -1
  239. package/src/responses/progressive-freeform-input.ts +130 -0
  240. package/src/responses/reasoning-envelope.ts +30 -0
  241. package/src/responses/schema.ts +3 -0
  242. package/src/responses/state.ts +5 -12
  243. package/src/responses/tool-name-aliases.ts +15 -1
  244. package/src/router.ts +108 -117
  245. package/src/routing/compatibility/behavior.ts +9 -0
  246. package/src/routing/compatibility/subject.ts +16 -1
  247. package/src/server/adapter-resolve.ts +9 -0
  248. package/src/server/admission-model-scope.ts +219 -0
  249. package/src/server/audio-live.ts +9 -3
  250. package/src/server/audio-upstream.ts +18 -0
  251. package/src/server/auth-cors.ts +29 -0
  252. package/src/server/chat-completions.ts +60 -4
  253. package/src/server/chat-native.ts +19 -4
  254. package/src/server/claude-messages.ts +61 -20
  255. package/src/server/effort-row.ts +11 -3
  256. package/src/server/grok-responses-control-frame.ts +160 -1
  257. package/src/server/grok-responses-snapshot-repair.ts +113 -11
  258. package/src/server/gui-freshness.ts +103 -0
  259. package/src/server/gui-static.ts +7 -9
  260. package/src/server/images.ts +59 -6
  261. package/src/server/index/claude-intercept-lifecycle.ts +49 -0
  262. package/src/server/index/serve-options.ts +142 -39
  263. package/src/server/index/spend-ledger-lifecycle.ts +92 -0
  264. package/src/server/index/startup-warnings.ts +24 -0
  265. package/src/server/index/websocket-handler.ts +6 -1
  266. package/src/server/index.ts +34 -38
  267. package/src/server/lifecycle.ts +4 -4
  268. package/src/server/live-call-bindings.ts +6 -0
  269. package/src/server/live.ts +88 -3
  270. package/src/server/management/agent-settings-routes.ts +121 -36
  271. package/src/server/management/aside-profile-routes.ts +266 -7
  272. package/src/server/management/companion-routes.ts +77 -0
  273. package/src/server/management/config-routes.ts +18 -2
  274. package/src/server/management/context.ts +3 -0
  275. package/src/server/management/integration-routes.ts +287 -5
  276. package/src/server/management/logs-usage-routes.ts +19 -0
  277. package/src/server/management/metrics-routes.ts +20 -0
  278. package/src/server/management/model-rows.ts +224 -12
  279. package/src/server/management/native-integration-routes.ts +103 -6
  280. package/src/server/management/oauth-account-routes.ts +45 -7
  281. package/src/server/management/route-registry.ts +20 -0
  282. package/src/server/management/shared.ts +28 -4
  283. package/src/server/management/system-restart.ts +7 -2
  284. package/src/server/management/system-routes.ts +2 -0
  285. package/src/server/management/usage-aggregate-cache.ts +4 -0
  286. package/src/server/management/usage-timeline-routes.ts +44 -0
  287. package/src/server/management-api.ts +10 -9
  288. package/src/server/management-auth.ts +15 -1
  289. package/src/server/proxy-liveness.ts +75 -0
  290. package/src/server/readiness.ts +29 -10
  291. package/src/server/relay-eager.ts +24 -2
  292. package/src/server/relay.ts +138 -12
  293. package/src/server/request-log-failure-attribution.ts +99 -0
  294. package/src/server/request-log.ts +169 -2
  295. package/src/server/request-metrics.ts +298 -0
  296. package/src/server/responses/adapter-continuation.ts +3 -3
  297. package/src/server/responses/adapter-dispatch.ts +11 -6
  298. package/src/server/responses/codex-ws-wire.ts +34 -8
  299. package/src/server/responses/combo-stream-preflight.ts +168 -6
  300. package/src/server/responses/compact.ts +43 -10
  301. package/src/server/responses/compaction-routing.ts +111 -0
  302. package/src/server/responses/core-codex-account.ts +8 -3
  303. package/src/server/responses/core-combo.ts +7 -7
  304. package/src/server/responses/core-normalize.ts +6 -12
  305. package/src/server/responses/core-opaque-recovery.ts +91 -0
  306. package/src/server/responses/core-options.ts +4 -0
  307. package/src/server/responses/encrypted-payload.ts +20 -2
  308. package/src/server/responses/fetch-helpers.ts +124 -8
  309. package/src/server/responses/input-admission.ts +10 -0
  310. package/src/server/responses/passthrough-delivery.ts +45 -15
  311. package/src/server/responses/passthrough-dispatch.ts +215 -44
  312. package/src/server/responses/passthrough-error.ts +27 -8
  313. package/src/server/responses/policy-fallback.ts +5 -13
  314. package/src/server/responses/request-prepare.ts +109 -18
  315. package/src/server/responses/request-send-budget.ts +17 -1
  316. package/src/server/responses/request-sidecar-auth.ts +1 -1
  317. package/src/server/responses/request-transport.ts +26 -6
  318. package/src/server/responses/reset-replay.ts +108 -0
  319. package/src/server/responses/run-turn-execution.ts +25 -3
  320. package/src/server/responses/sidecar-execution.ts +17 -2
  321. package/src/server/responses/ws-upstream.ts +14 -27
  322. package/src/server/responses-custom-tool-repair.ts +27 -54
  323. package/src/server/responses-request-tool-scope.ts +214 -0
  324. package/src/server/responses-undeclared-tool-guard.ts +35 -2
  325. package/src/server/search.ts +25 -1
  326. package/src/server/sse-payload-rewrite.ts +1 -1
  327. package/src/server/usage-ledger-retention.ts +73 -0
  328. package/src/service/cli.ts +48 -2
  329. package/src/service/health.ts +3 -2
  330. package/src/service/install-state-contract.d.mts +27 -0
  331. package/src/service/install-state-contract.mjs +34 -0
  332. package/src/service/launchd.ts +1 -1
  333. package/src/service/orchestration.ts +2 -4
  334. package/src/service/ownership-compatibility.ts +164 -0
  335. package/src/service/ownership-mutation-lease.d.mts +32 -0
  336. package/src/service/ownership-mutation-lease.mjs +211 -0
  337. package/src/service/repair.ts +45 -1
  338. package/src/service/state-lock.ts +269 -0
  339. package/src/service/state-record.d.mts +36 -0
  340. package/src/service/state-record.mjs +138 -0
  341. package/src/service/state.ts +582 -68
  342. package/src/service/windows-taskxml.ts +11 -10
  343. package/src/service.ts +7 -3
  344. package/src/tray/windows-tray.ps1 +156 -4
  345. package/src/types/config.ts +48 -3
  346. package/src/types/provider.ts +91 -0
  347. package/src/types/request.ts +30 -2
  348. package/src/types/tools.ts +33 -0
  349. package/src/types.ts +4 -0
  350. package/src/update/index.ts +207 -63
  351. package/src/update/job.ts +9 -5
  352. package/src/update/ownership-transaction.ts +47 -0
  353. package/src/update/restart-ownership.ts +54 -0
  354. package/src/update/runtime-ownership.d.mts +40 -0
  355. package/src/update/runtime-ownership.mjs +122 -0
  356. package/src/usage/attempt-delivery.ts +198 -0
  357. package/src/usage/cache-diagnostic.ts +305 -0
  358. package/src/usage/failure-fingerprint.ts +118 -0
  359. package/src/usage/failure-projection-cache.ts +174 -0
  360. package/src/usage/failure-projection.ts +174 -0
  361. package/src/usage/ledger-retention.ts +165 -0
  362. package/src/usage/log.ts +126 -79
  363. package/src/usage/request-outcome.ts +150 -0
  364. package/src/usage/retention-contract.ts +28 -0
  365. package/src/usage/summary.ts +2 -2
  366. package/src/usage/telemetry-contract.ts +237 -0
  367. package/src/usage/timeline.ts +236 -0
  368. package/src/vision/eligibility.ts +88 -9
  369. package/src/vision/plan.ts +34 -10
  370. package/src/web-search/alpha-search.ts +21 -1
  371. package/src/web-search/executor.ts +41 -2
  372. package/src/web-search/loop.ts +6 -1
  373. package/gui/dist/assets/index-C5IebErG.js +0 -136
  374. package/gui/dist/assets/index-OESInAjC.css +0 -1
@@ -2,15 +2,11 @@ import { existsSync, readFileSync, statSync } from "node:fs";
2
2
  import { basename, extname, isAbsolute, join, relative, resolve } from "node:path";
3
3
  import { browserSecurityHeaders } from "./auth-cors";
4
4
  import type { GuiSessionBootstrap } from "./gui-session";
5
+ import { packageVersion } from "../lib/package-version";
6
+ import { isStandaloneBinary, standaloneRoot } from "../lib/standalone";
5
7
 
6
8
  /** opencodex version, read from the packaged package.json (same source as the server bootstrap). */
7
- const VERSION = (() => {
8
- try {
9
- return JSON.parse(readFileSync(new URL("../../package.json", import.meta.url), "utf8")).version as string;
10
- } catch {
11
- return "0.0.0";
12
- }
13
- })();
9
+ const VERSION = packageVersion("0.0.0");
14
10
 
15
11
  const MIME_TYPES: Record<string, string> = {
16
12
  ".html": "text/html", ".js": "application/javascript", ".css": "text/css",
@@ -24,11 +20,13 @@ const MIME_TYPES: Record<string, string> = {
24
20
  */
25
21
  const HASHED_ASSET_PATTERN = /-[a-zA-Z0-9_-]{8,}\.[a-zA-Z0-9]+$/;
26
22
 
27
- function findGuiDist(): string | null {
23
+ export function findGuiDist(): string | null {
28
24
  const candidates = [
25
+ process.env.OPENCODEX_GUI_DIST,
26
+ ...(isStandaloneBinary() ? [join(standaloneRoot(), "gui", "dist")] : []),
29
27
  join(import.meta.dir, "..", "..", "gui", "dist"),
30
28
  join(import.meta.dir, "..", "..", "..", "gui", "dist"),
31
- ];
29
+ ].filter((candidate): candidate is string => Boolean(candidate));
32
30
  for (const c of candidates) {
33
31
  if (existsSync(join(c, "index.html"))) return c;
34
32
  }
@@ -33,6 +33,8 @@ import { selectProactiveApiKeyTransport } from "../providers/key-failover";
33
33
  import { getProviderRegistryEntry } from "../providers/registry";
34
34
  import { readJsonRequestBody, resolveInboundBodyLimitBytes } from "./request-decompress";
35
35
  import { ForwardAdmissionCredentialError, validateForwardAdmissionCredential } from "./auth-cors";
36
+ import type { DataPlaneAdmission } from "./auth-cors";
37
+ import { admissionScopeDenial } from "./admission-model-scope";
36
38
  import type { RequestLogContext } from "./request-log";
37
39
  import { codexLogAccountId, decodeRequestErrorResponse } from "./responses";
38
40
  import { getValidAccessToken, getOAuthCredentialProjectId } from "../oauth/index";
@@ -115,6 +117,18 @@ export async function readImageResponseBytes(
115
117
  }
116
118
 
117
119
  const CCA_IMAGE_MODEL = "gemini-3.1-flash-image";
120
+ const XAI_IMAGE_BRIDGE_MODEL = "grok-imagine-image-quality";
121
+
122
+ /**
123
+ * The model this request names, or undefined when it names none.
124
+ *
125
+ * An absent `model` is relayed as an absent `model`: the upstream picks, so
126
+ * there is no destination to echo and none a model list can allow.
127
+ */
128
+ function requestedImageSelector(body: unknown): string | undefined {
129
+ const model = (body as { model?: unknown } | null)?.model;
130
+ return typeof model === "string" && model.trim() ? model : undefined;
131
+ }
118
132
 
119
133
  /**
120
134
  * Google Gemini finishReasons that indicate a permanent content/safety block.
@@ -207,11 +221,21 @@ async function tryCcaImageGeneration(
207
221
  logCtx: RequestLogContext,
208
222
  signal: AbortSignal,
209
223
  endpoint: ImagesEndpoint,
224
+ admission: DataPlaneAdmission | undefined,
210
225
  ): Promise<Response | undefined> {
211
226
  if (endpoint !== "generations") return undefined;
212
227
  const provider = config.providers?.["google-antigravity"];
213
228
  if (!provider || provider.disabled) return undefined;
214
229
 
230
+ // The destination is decided here, not by the caller: this branch always
231
+ // bills Antigravity for CCA_IMAGE_MODEL whatever the body named. That is the
232
+ // pair a scoped key is held to.
233
+ const denial = admissionScopeDenial(config, admission, requestedImageSelector(body), {
234
+ providerName: "google-antigravity",
235
+ modelId: CCA_IMAGE_MODEL,
236
+ });
237
+ if (denial) return denial;
238
+
215
239
  const prompt = (body as { prompt?: unknown })?.prompt;
216
240
  if (typeof prompt !== "string" || !prompt.trim()) {
217
241
  return formatErrorResponse(400, "invalid_request_error", "prompt is required and must not be empty");
@@ -446,10 +470,20 @@ async function tryXaiImageRelay(
446
470
  logCtx: RequestLogContext,
447
471
  signal: AbortSignal | undefined,
448
472
  endpoint: ImagesEndpoint,
473
+ admission: DataPlaneAdmission | undefined,
449
474
  ): Promise<Response | undefined> {
450
475
  if (config.images?.bridgeEnabled !== true) return undefined;
451
476
  const found = findXaiProvider(config);
452
477
  if (!found) return undefined;
478
+ const bridgeModel = config.images?.bridgeModel ?? XAI_IMAGE_BRIDGE_MODEL;
479
+ // The bridge sends this request to the configured xAI provider and the bridge
480
+ // model regardless of the selector in the body, so that is what the key is
481
+ // checked against.
482
+ const denial = admissionScopeDenial(config, admission, requestedImageSelector(body), {
483
+ providerName: found.name,
484
+ modelId: bridgeModel,
485
+ });
486
+ if (denial) return denial;
453
487
  const obj = body && typeof body === "object" && !Array.isArray(body)
454
488
  ? body as Record<string, unknown>
455
489
  : {};
@@ -496,11 +530,11 @@ async function tryXaiImageRelay(
496
530
  }
497
531
  if (!token) return xaiImageAuthMissing();
498
532
  logCtx.provider = "xai";
499
- logCtx.model = config.images?.bridgeModel ?? "grok-imagine-image-quality";
533
+ logCtx.model = bridgeModel;
500
534
  const result = await callXaiImages(
501
535
  {
502
536
  prompt,
503
- model: logCtx.model,
537
+ model: bridgeModel,
504
538
  n,
505
539
  size,
506
540
  quality,
@@ -602,6 +636,7 @@ export async function handleImages(
602
636
  endpoint: ImagesEndpoint,
603
637
  logCtx: RequestLogContext,
604
638
  turnAdmissionLease?: AdmissionLease,
639
+ admission?: DataPlaneAdmission,
605
640
  ): Promise<Response> {
606
641
  let body: unknown;
607
642
  try {
@@ -616,7 +651,7 @@ export async function handleImages(
616
651
  // Explicit images.provider owns the route, including its validation errors.
617
652
  // Do not divert that selection to the xAI Imagine relay.
618
653
  if (config.images?.provider === undefined) {
619
- const xaiRelay = await tryXaiImageRelay(body, config, logCtx, req.signal, endpoint);
654
+ const xaiRelay = await tryXaiImageRelay(body, config, logCtx, req.signal, endpoint, admission);
620
655
  if (xaiRelay) return xaiRelay;
621
656
  }
622
657
  if (candidates.error) {
@@ -641,7 +676,7 @@ export async function handleImages(
641
676
  const canUseOpenAiForward = !skipOpenAiForwardForAdmissionBearer && candidates.forwardCandidates.length > 0;
642
677
 
643
678
  if (!canUseOpenAiForward && !candidates.keyed) {
644
- const ccaResponse = await tryCcaImageGeneration(body, config, logCtx, req.signal, endpoint);
679
+ const ccaResponse = await tryCcaImageGeneration(body, config, logCtx, req.signal, endpoint, admission);
645
680
  if (ccaResponse) return ccaResponse;
646
681
  // 400, not 5xx: codex retries every 5xx up to 5 total attempts, and this is a permanent
647
682
  // configuration state that must surface on the first attempt.
@@ -688,7 +723,20 @@ export async function handleImages(
688
723
 
689
724
  const headers: Record<string, string> = { "content-type": "application/json" };
690
725
  let url: string;
726
+ // Both relay branches copy the body upstream, so the destination is the
727
+ // provider chosen in that branch and the model the caller named. Each branch
728
+ // is checked as it is entered, before it resolves a credential or commits a
729
+ // key rotation, so a refused request spends nothing.
730
+ const relaySelector = requestedImageSelector(body);
691
731
  if (forward) {
732
+ const denial = admissionScopeDenial(config, admission, relaySelector, {
733
+ providerName: forward.providerName,
734
+ modelId: relaySelector,
735
+ });
736
+ if (denial) {
737
+ forward.releaseProbeLease?.();
738
+ return denial;
739
+ }
692
740
  const { provider } = forward;
693
741
  if (provider.headers) Object.assign(headers, provider.headers);
694
742
  for (const [name, value] of forward.headers) headers[name] = value;
@@ -697,13 +745,18 @@ export async function handleImages(
697
745
  } else if (forwardAuthError) {
698
746
  // Before surfacing the OpenAI auth failure, try CCA — the user may have a
699
747
  // valid Google Antigravity login even though their OpenAI pool is broken.
700
- const ccaResponse = await tryCcaImageGeneration(body, config, logCtx, req.signal, endpoint);
748
+ const ccaResponse = await tryCcaImageGeneration(body, config, logCtx, req.signal, endpoint, admission);
701
749
  if (ccaResponse) return ccaResponse;
702
750
  // No CCA either: a configured OpenAI pool mode owns its authentication failure.
703
751
  // Do not hide a broken/expired pool behind separately billed API-key image generation.
704
752
  return forwardAuthError;
705
753
  } else if (candidates.keyed) {
706
754
  const { providerName } = candidates.keyed;
755
+ const denial = admissionScopeDenial(config, admission, relaySelector, {
756
+ providerName,
757
+ modelId: relaySelector,
758
+ });
759
+ if (denial) return denial;
707
760
  // The keyed image path builds its own URL and Authorization header and never enters
708
761
  // handleResponses, so the pre-dispatch key pick happens here.
709
762
  //
@@ -737,7 +790,7 @@ export async function handleImages(
737
790
  url = `${provider.baseUrl.replace(/\/v1\/?$/, "")}/v1/images/${endpoint}`;
738
791
  } else {
739
792
  // No usable OpenAI credential — try CCA before giving up.
740
- const ccaResponse = await tryCcaImageGeneration(body, config, logCtx, req.signal, endpoint);
793
+ const ccaResponse = await tryCcaImageGeneration(body, config, logCtx, req.signal, endpoint, admission);
741
794
  if (ccaResponse) return ccaResponse;
742
795
  return formatErrorResponse(
743
796
  401,
@@ -0,0 +1,49 @@
1
+ import type { Server } from "bun";
2
+ import {
3
+ startClaudeIntercept,
4
+ type ClaudeInterceptHandle,
5
+ type StartClaudeInterceptOptions,
6
+ } from "../../claude/intercept/runtime";
7
+
8
+ /**
9
+ * Owns the Claude intercept pair (CONNECT proxy + TLS listener) on behalf of `startServer`.
10
+ * The pair is an optional integration: a bind failure degrades to a warning, never to a
11
+ * startup failure, because every other client keeps working without it. `startServer` stays
12
+ * synchronous, so the start is fire-and-forget and `stop()` awaits whatever it produced.
13
+ */
14
+ export interface ClaudeInterceptLifecycle<T> {
15
+ /** True once the TLS listener has bound and `requestServer` is it. */
16
+ ownsListener(requestServer: Server<T>): boolean;
17
+ start(options: StartClaudeInterceptOptions<T>): void;
18
+ stop(): Promise<void>;
19
+ }
20
+
21
+ export function createClaudeInterceptLifecycle<T>(): ClaudeInterceptLifecycle<T> {
22
+ let listener: Server<T> | null = null;
23
+ let pending: Promise<ClaudeInterceptHandle<T> | null> = Promise.resolve(null);
24
+ return {
25
+ ownsListener: requestServer => listener !== null && requestServer === listener,
26
+ start(options) {
27
+ const dispatch = options.dispatch;
28
+ pending = startClaudeIntercept<T>({
29
+ ...options,
30
+ dispatch: (req, requestServer) => {
31
+ listener ??= requestServer;
32
+ return dispatch(req, requestServer);
33
+ },
34
+ }).then(handle => {
35
+ if (handle) {
36
+ listener = handle.listener;
37
+ console.log(`🔐 Claude intercept proxy active on http://127.0.0.1:${handle.proxyPort} (CONNECT api.anthropic.com → local TLS)`);
38
+ }
39
+ return handle;
40
+ }).catch((error: unknown) => {
41
+ console.warn(`⚠ Claude intercept proxy could not start: ${error instanceof Error ? error.message : String(error)}`);
42
+ return null;
43
+ });
44
+ },
45
+ async stop() {
46
+ await (await pending)?.stop();
47
+ },
48
+ };
49
+ }