@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.
- package/README.md +0 -4
- package/bin/ocx.mjs +61 -3
- package/gui/dist/assets/App-BjlttVLy.js +58 -0
- package/gui/dist/assets/Tray-DyR9yBee.js +1 -0
- package/gui/dist/assets/{index-BAbcj51J.css → index-BB6uK6tU.css} +1 -1
- package/gui/dist/assets/index-BMssP4k3.js +94 -0
- package/gui/dist/assets/quota-summary-D8M1bc_R.js +1 -0
- package/gui/dist/index.html +2 -2
- package/package.json +2 -3
- package/src/adapters/anthropic/beta-allowlist.ts +87 -8
- package/src/adapters/anthropic/client-identity.ts +1 -1
- package/src/adapters/anthropic/passthrough.ts +33 -4
- package/src/adapters/azure.ts +19 -1
- package/src/adapters/client-fingerprint.ts +24 -0
- package/src/adapters/coding-agent/protocol.ts +15 -8
- package/src/adapters/coding-agent/turn.ts +22 -9
- package/src/adapters/cursor/arg-codec.ts +17 -5
- package/src/adapters/cursor/catalog.ts +8 -0
- package/src/adapters/cursor/effort-map.ts +3 -0
- package/src/adapters/cursor/freeform-args.ts +75 -0
- package/src/adapters/cursor/protobuf-events.ts +36 -23
- package/src/adapters/google-antigravity-wire.ts +22 -1
- package/src/adapters/google.ts +2 -2
- package/src/adapters/ollama-native.ts +29 -1
- package/src/adapters/openai-chat/messages.ts +8 -4
- package/src/adapters/openai-chat/response-events.ts +59 -1
- package/src/adapters/openai-chat.ts +8 -5
- package/src/adapters/openai-responses/empty-assistant.ts +32 -0
- package/src/adapters/openai-responses/passthrough.ts +3 -0
- package/src/adapters/openai-responses/request-strips.ts +10 -2
- package/src/adapters/qoder/adapter.ts +56 -15
- package/src/advisor/consult.ts +220 -0
- package/src/advisor/context.ts +226 -0
- package/src/advisor/disclosure.ts +20 -0
- package/src/advisor/runtime.ts +305 -0
- package/src/advisor/settings.ts +135 -0
- package/src/advisor/state.ts +369 -0
- package/src/advisor/synthetic-tool.ts +33 -0
- package/src/bridge/errors.ts +24 -0
- package/src/bridge.ts +1 -1
- package/src/chat/inbound.ts +30 -6
- package/src/chat/outbound.ts +5 -0
- package/src/claude/context-windows.ts +54 -1
- package/src/claude/desktop-3p-library.ts +43 -3
- package/src/claude/desktop-3p.ts +22 -8
- package/src/claude/desktop-picker-profile.ts +11 -4
- package/src/claude/desktop-profile.ts +10 -1
- package/src/claude/desktop-remote-store-io.ts +5 -3
- package/src/claude/inbound.ts +121 -50
- package/src/claude/intercept/connect-proxy.ts +10 -3
- package/src/claude/intercept/local-ca.ts +8 -1
- package/src/claude/intercept/picker-ca-persistence.ts +3 -1
- package/src/claude/intercept/picker-listener.ts +49 -1
- package/src/claude/intercept/settings-no-proxy.ts +146 -0
- package/src/claude/intercept/settings.ts +101 -9
- package/src/claude/model-default-settings.ts +154 -0
- package/src/claude/model-info.ts +7 -7
- package/src/claude/outbound.ts +13 -6
- package/src/cli/account-api.ts +65 -0
- package/src/cli/account-auth.ts +26 -2
- package/src/cli/account.ts +41 -5
- package/src/cli/advisor.ts +151 -0
- package/src/cli/auxiliary-listener-preflight.ts +37 -0
- package/src/cli/capabilities-access-remote.ts +8 -0
- package/src/cli/capabilities-agents-routing.ts +18 -0
- package/src/cli/capabilities-base.ts +14 -2
- package/src/cli/catalog-command-result.ts +2 -1
- package/src/cli/chatgpt-command.ts +35 -14
- package/src/cli/claude.ts +60 -11
- package/src/cli/config-command.ts +8 -2
- package/src/cli/debug.ts +8 -0
- package/src/cli/dispatch.ts +21 -4
- package/src/cli/doctor.ts +9 -0
- package/src/cli/ensure-desktop-cold-start.ts +329 -0
- package/src/cli/export-command.ts +15 -2
- package/src/cli/help.ts +2 -1
- package/src/cli/hub-disable.ts +99 -0
- package/src/cli/hub.ts +4 -2
- package/src/cli/index.ts +26 -8
- package/src/cli/integration-plan-dto.ts +2 -0
- package/src/cli/link-child.ts +104 -0
- package/src/cli/link-enrollment.ts +96 -0
- package/src/cli/link.ts +39 -24
- package/src/cli/observe-stream.ts +6 -1
- package/src/cli/observe.ts +10 -2
- package/src/cli/provider.ts +12 -0
- package/src/cli/registry.ts +20 -7
- package/src/cli/restart-scope.ts +3 -1
- package/src/cli/runtime-api.ts +27 -2
- package/src/cli/status.ts +104 -20
- package/src/client/connect.ts +39 -4
- package/src/client/hub-client.ts +14 -7
- package/src/client/hub-state.ts +25 -14
- package/src/client/link-home.ts +248 -0
- package/src/client/link-management-relay.ts +61 -0
- package/src/client/link-relay.ts +6 -1
- package/src/client/link-status.ts +14 -0
- package/src/client/machine-api.ts +14 -7
- package/src/client/machine-listener.ts +29 -4
- package/src/clients/config-export/contracts.ts +1 -0
- package/src/clients/config-export/model-metadata.ts +3 -2
- package/src/clients/config-export/qoder.ts +93 -0
- package/src/clients/config-export.ts +59 -16
- package/src/clients/omo-role-models.ts +6 -3
- package/src/codex/agent-role-models.ts +26 -8
- package/src/codex/auth-api/account-list.ts +17 -2
- package/src/codex/auth-api/account-selection.ts +17 -7
- package/src/codex/auth-api/pool-mode-gate.ts +5 -2
- package/src/codex/auth-api/routes.ts +5 -1
- package/src/codex/auth-context.ts +79 -5
- package/src/codex/autostart-health.ts +7 -1
- package/src/codex/catalog/build-entries.ts +11 -2
- package/src/codex/catalog/bundled.ts +5 -1
- package/src/codex/catalog/configured-tiers.ts +50 -0
- package/src/codex/catalog/effort.ts +6 -3
- package/src/codex/catalog/metadata.ts +4 -2
- package/src/codex/catalog/model-hints.ts +21 -3
- package/src/codex/catalog/native-models.ts +16 -2
- package/src/codex/catalog/native-ultrafast.ts +106 -0
- package/src/codex/catalog/parsing.ts +5 -0
- package/src/codex/catalog/provider-models.ts +4 -0
- package/src/codex/catalog/retained-sync.ts +2 -0
- package/src/codex/catalog/routed-gather.ts +22 -4
- package/src/codex/catalog/vendor-context.ts +82 -0
- package/src/codex/config-write-lock.ts +40 -2
- package/src/codex/convergence.ts +2 -0
- package/src/codex/desktop-app/types.ts +2 -0
- package/src/codex/desktop-app/windows.ts +29 -16
- package/src/codex/desktop-app-restart.ts +4 -2
- package/src/codex/desktop-compatibility/certificate-service.ts +99 -0
- package/src/codex/desktop-compatibility/certificate-store.ts +193 -0
- package/src/codex/desktop-compatibility/connection-store.ts +87 -0
- package/src/codex/desktop-compatibility/installed-build.ts +55 -0
- package/src/codex/desktop-compatibility/json-body.ts +29 -0
- package/src/codex/desktop-compatibility/native-identity.ts +62 -0
- package/src/codex/desktop-compatibility/relay-listener.ts +93 -0
- package/src/codex/desktop-compatibility/routing-binding.ts +10 -0
- package/src/codex/desktop-compatibility/routing-preflight.ts +69 -0
- package/src/codex/desktop-compatibility/runtime-ownership.ts +28 -0
- package/src/codex/desktop-compatibility/runtime.ts +173 -0
- package/src/codex/desktop-compatibility/service.ts +30 -0
- package/src/codex/desktop-compatibility/startup-settings.ts +50 -0
- package/src/codex/desktop-compatibility/usage-activation.ts +86 -0
- package/src/codex/desktop-compatibility/usage-controlled-fetch.ts +59 -0
- package/src/codex/desktop-compatibility/usage-controller.ts +107 -0
- package/src/codex/desktop-compatibility/usage-policy.ts +59 -0
- package/src/codex/desktop-compatibility/usage-refresh.ts +33 -0
- package/src/codex/desktop-compatibility/usage-sse-controller.ts +86 -0
- package/src/codex/desktop-compatibility/windows-activation-source.ts +44 -0
- package/src/codex/desktop-compatibility/windows-certificate-trust.ts +81 -0
- package/src/codex/desktop-compatibility/windows-key-protection.ts +59 -0
- package/src/codex/desktop-compatibility/windows-package-command.ts +86 -0
- package/src/codex/desktop-compatibility/windows-package-launch.ts +28 -0
- package/src/codex/history-provider.ts +1 -1
- package/src/codex/inject/config-write-section.ts +4 -1
- package/src/codex/inject/remove.ts +18 -6
- package/src/codex/journal.ts +16 -1
- package/src/codex/main-account.ts +99 -15
- package/src/codex/native-main-refresh-mode.ts +217 -0
- package/src/codex/native-profile-startup.ts +4 -1
- package/src/codex/prompt-journal.ts +20 -2
- package/src/codex/prompt-layers/encoding.ts +49 -0
- package/src/codex/prompt-layers/import-source.ts +30 -0
- package/src/codex/prompt-layers/toml-edit.ts +269 -37
- package/src/codex/prompt-layers/toml-read.ts +52 -4
- package/src/codex/prompt-layers.ts +383 -20
- package/src/codex/prompt-lock-claim.ts +13 -3
- package/src/codex/prompt-lock-evidence.ts +3 -3
- package/src/codex/prompt-lock-owner.ts +14 -6
- package/src/codex/prompt-text-probe.ts +34 -5
- package/src/codex/quota.ts +13 -6
- package/src/codex/role-auto-assign.ts +2 -5
- package/src/codex/routing/active-account.ts +21 -0
- package/src/codex/routing/health-store.ts +1 -1
- package/src/codex/routing/pool-recovery.ts +79 -0
- package/src/codex/routing.ts +1 -1
- package/src/codex/runtime.ts +196 -8
- package/src/codex/shim-diagnostics.ts +6 -0
- package/src/codex/shim-overlay.ts +15 -6
- package/src/codex/shim.ts +1 -1
- package/src/combos/failover.ts +3 -0
- package/src/config/api-key-reconcile.ts +34 -0
- package/src/config/diagnostics.ts +18 -1
- package/src/config/feature-flags.ts +5 -0
- package/src/config/live-reconcile.ts +56 -29
- package/src/config/live-topology.ts +29 -0
- package/src/config/load-degrade.ts +8 -2
- package/src/config/paths.ts +13 -3
- package/src/config/salvage.ts +3 -1
- package/src/config/schema/config-schema.ts +19 -0
- package/src/config/schema/desktop-compatibility.ts +9 -0
- package/src/config/schema/leaf-validators.ts +1 -0
- package/src/generated/compatibility-version.json +713 -285
- package/src/generated/model-metadata.ts +1 -1
- package/src/grok/grpc-web.ts +23 -1
- package/src/grok/reset-coupon-ledger.ts +75 -5
- package/src/grok/reset-coupons.ts +4 -2
- package/src/integrations/catalog-refresh.ts +1 -1
- package/src/integrations/config-io.ts +20 -2
- package/src/integrations/journal.ts +15 -1
- package/src/integrations/mutation-plan.ts +14 -7
- package/src/integrations/registry.ts +52 -11
- package/src/integrations/state.ts +19 -7
- package/src/integrations/store.ts +3 -0
- package/src/integrations/target.ts +42 -5
- package/src/integrations/writer.ts +24 -7
- package/src/lib/advisor-activation.ts +16 -0
- package/src/lib/app-owned-memory-stores.ts +4 -0
- package/src/lib/app-owned-memory.ts +11 -0
- package/src/lib/background-work.ts +65 -0
- package/src/lib/bounded-file-read.ts +58 -0
- package/src/lib/bun-path-runtime.d.mts +10 -1
- package/src/lib/bun-path-runtime.mjs +62 -3
- package/src/lib/desktop-proxy-route.ts +30 -0
- package/src/lib/desktop-upstream-tunnel.ts +107 -0
- package/src/lib/errors.ts +7 -0
- package/src/lib/json-byte-size.ts +31 -0
- package/src/lib/local-internal-call-capability.ts +55 -0
- package/src/lib/optional-shutdown-hooks.ts +35 -0
- package/src/lib/outbound-credential-registry.ts +210 -0
- package/src/lib/package-tree-integrity.ts +2 -2
- package/src/lib/passive-read.ts +35 -0
- package/src/lib/shadow-call.ts +44 -5
- package/src/lib/socks5-fetch.ts +48 -71
- package/src/lib/socks5-handshake.ts +112 -0
- package/src/lib/translator-budget.ts +7 -1
- package/src/link/codex-settings.ts +155 -0
- package/src/link/management-routes.ts +89 -0
- package/src/link/remote-probe.ts +38 -0
- package/src/link/ssh-argv.ts +26 -5
- package/src/link/ssh-runner.ts +58 -27
- package/src/link/status-projection.ts +22 -4
- package/src/link/supervisor.ts +144 -49
- package/src/link/tunnel-state.ts +11 -4
- package/src/oauth/anthropic-continuity.ts +4 -3
- package/src/oauth/anthropic-identity.ts +13 -2
- package/src/oauth/anthropic-model-quota.ts +13 -1
- package/src/oauth/anthropic-routing.ts +230 -23
- package/src/oauth/anthropic.ts +18 -4
- package/src/oauth/generic-account-failover.ts +29 -5
- package/src/oauth/google-antigravity.ts +24 -7
- package/src/oauth/index.ts +28 -17
- package/src/oauth/kiro-account-identity.ts +106 -0
- package/src/oauth/kiro-device-login.ts +2 -1
- package/src/oauth/kiro.ts +55 -6
- package/src/oauth/plan.ts +29 -0
- package/src/oauth/store.ts +40 -3
- package/src/oauth/types.ts +2 -0
- package/src/plugins/loader.ts +11 -0
- package/src/protocols/codecs/chat-ir.ts +467 -0
- package/src/protocols/codecs/chat.ts +10 -1
- package/src/protocols/codecs/messages-ir.ts +500 -0
- package/src/protocols/features.ts +10 -2
- package/src/protocols/path.ts +11 -1
- package/src/protocols/plan-snapshot.ts +39 -16
- package/src/protocols/plan.ts +9 -1
- package/src/protocols/settings.ts +8 -0
- package/src/providers/anthropic-billing-ownership.ts +74 -0
- package/src/providers/anthropic-billing-recovery.ts +31 -0
- package/src/providers/anthropic-billing-refusal.ts +55 -0
- package/src/providers/anthropic-reset-admission.ts +62 -0
- package/src/providers/anthropic-reset-grant-ledger.ts +28 -6
- package/src/providers/antigravity-account-roster.ts +102 -0
- package/src/providers/antigravity-models.ts +85 -7
- package/src/providers/api-key-resolve.ts +17 -2
- package/src/providers/key-failover.ts +52 -5
- package/src/providers/model-rename-fields.ts +2 -0
- package/src/providers/model-rename-migration.ts +5 -3
- package/src/providers/model-rename-startup.ts +5 -3
- package/src/providers/quota/account-cache.ts +11 -0
- package/src/providers/quota/anthropic-account-quota.ts +4 -2
- package/src/providers/quota/report-cache.ts +14 -0
- package/src/providers/quota/vendor-probes-oauth.ts +3 -3
- package/src/providers/quota.ts +4 -1
- package/src/providers/registry/entries-core.ts +21 -20
- package/src/providers/registry/entries-extended.ts +19 -1
- package/src/providers/registry/model-seeds.ts +17 -0
- package/src/providers/request-pacing.ts +9 -0
- package/src/providers/stale-no-vision-seed-migration.ts +72 -0
- package/src/responses/compaction.ts +14 -0
- package/src/responses/parser.ts +37 -20
- package/src/responses/state.ts +32 -29
- package/src/routing/history/build-file.ts +92 -0
- package/src/routing/history/indexer.ts +248 -216
- package/src/routing/history/ingest.ts +238 -0
- package/src/routing/history/rebuild-job.ts +111 -0
- package/src/routing/history/rebuild-protocol.ts +15 -0
- package/src/routing/history/rebuild-worker.ts +34 -0
- package/src/server/admission-model-scope.ts +4 -1
- package/src/server/antigravity-direct-eligibility.ts +208 -0
- package/src/server/auth-cors.ts +46 -0
- package/src/server/chat-completions.ts +75 -9
- package/src/server/claude-messages.ts +110 -20
- package/src/server/configured-credentials.ts +194 -0
- package/src/server/index/desktop-compatibility-startup.ts +56 -0
- package/src/server/index/link-management.ts +58 -0
- package/src/server/index/optional-listeners.ts +5 -1
- package/src/server/index/serve-options.ts +15 -2
- package/src/server/index/startup-warnings.ts +9 -0
- package/src/server/index/websocket-handler.ts +9 -0
- package/src/server/index.ts +13 -10
- package/src/server/inference/client-encoder-delivery.ts +6 -5
- package/src/server/lifecycle.ts +2 -1
- package/src/server/login-shell-path.ts +146 -0
- package/src/server/management/advisor-routes.ts +191 -0
- package/src/server/management/agent-settings-routes.ts +50 -14
- package/src/server/management/anthropic-reset-grant-routes.ts +17 -1
- package/src/server/management/codex-agent-role-routes.ts +19 -6
- package/src/server/management/codex-prompt-routes.ts +136 -5
- package/src/server/management/codex-role-auto-assign.ts +1 -1
- package/src/server/management/companion-routes.ts +4 -0
- package/src/server/management/config-routes.ts +2 -2
- package/src/server/management/context.ts +4 -0
- package/src/server/management/desktop-compatibility-routes.ts +46 -0
- package/src/server/management/desktop-compatibility-runtime-routes.ts +46 -0
- package/src/server/management/desktop-compatibility-settings-routes.ts +27 -0
- package/src/server/management/grok-coupon-routes.ts +78 -27
- package/src/server/management/link-enrollment.ts +192 -0
- package/src/server/management/link-route-state.ts +185 -0
- package/src/server/management/link-routes.ts +82 -402
- package/src/server/management/model-routes.ts +2 -2
- package/src/server/management/model-rows.ts +3 -1
- package/src/server/management/protocol-settings-patch.ts +1 -0
- package/src/server/management/provider-overwrite-carry.ts +10 -0
- package/src/server/management/provider-routes.ts +1 -0
- package/src/server/management/route-registry.ts +16 -3
- package/src/server/management/shared.ts +3 -0
- package/src/server/management/sibling-guard.ts +1 -0
- package/src/server/management-api.ts +9 -1
- package/src/server/messages-antigravity-direct.ts +195 -0
- package/src/server/messages-native.ts +73 -43
- package/src/server/relay-eager.ts +2 -0
- package/src/server/request-log-filter.ts +6 -1
- package/src/server/request-log-terminal-status.ts +63 -6
- package/src/server/request-log.ts +7 -0
- package/src/server/responses/adapter-continuation.ts +35 -18
- package/src/server/responses/adapter-delivery.ts +24 -2
- package/src/server/responses/adapter-dispatch.ts +261 -81
- package/src/server/responses/advisor-plan-slot.ts +27 -0
- package/src/server/responses/advisor-slot.ts +336 -0
- package/src/server/responses/code-mode-collab-surface.ts +313 -0
- package/src/server/responses/codex-ws-exchange.ts +147 -6
- package/src/server/responses/codex-ws-wire.ts +203 -0
- package/src/server/responses/collaboration.ts +36 -7
- package/src/server/responses/compact.ts +126 -48
- package/src/server/responses/context-overflow.ts +13 -1
- package/src/server/responses/core-codex-account.ts +105 -1
- package/src/server/responses/core-combo-failure.ts +24 -1
- package/src/server/responses/core-combo.ts +28 -2
- package/src/server/responses/core-errors.ts +29 -1
- package/src/server/responses/core-lifetime.ts +2 -0
- package/src/server/responses/core-normalize.ts +11 -0
- package/src/server/responses/core-options.ts +48 -0
- package/src/server/responses/core-replay.ts +7 -4
- package/src/server/responses/core.ts +1 -1
- package/src/server/responses/deferred-collab-surface.ts +158 -0
- package/src/server/responses/deferred-send-reporter.ts +25 -0
- package/src/server/responses/fetch-helpers.ts +12 -1
- package/src/server/responses/passthrough-delivery.ts +20 -11
- package/src/server/responses/passthrough-dispatch.ts +29 -7
- package/src/server/responses/request-prepare.ts +74 -9
- package/src/server/responses/request-transport.ts +22 -8
- package/src/server/responses/run-turn-execution.ts +70 -8
- package/src/server/responses/sidecar-execution.ts +89 -5
- package/src/server/responses/terminal-guard.ts +2 -1
- package/src/server/responses/ws-upstream.ts +9 -1
- package/src/server/responses-undeclared-tool-guard.ts +30 -9
- package/src/server/startup-action-control.ts +38 -4
- package/src/server/startup-health-cache.ts +5 -0
- package/src/server/system-env-shell.ts +7 -2
- package/src/server/system-env.ts +11 -1
- package/src/service/cli.ts +5 -1
- package/src/service/desktop-startup.ts +161 -34
- package/src/service/desktop-supervision.d.mts +27 -1
- package/src/service/desktop-supervision.mjs +111 -5
- package/src/service/health-sentinel-launch.ts +176 -0
- package/src/service/health-sentinel-main.ts +466 -0
- package/src/service/health-sentinel-policy.ts +241 -0
- package/src/service/health-sentinel-probe.ts +71 -0
- package/src/service/health-sentinel-windows.ts +87 -0
- package/src/service/linux-linger.ts +20 -0
- package/src/service/logging.ts +72 -0
- package/src/tray/windows-tray.ps1 +43 -24
- package/src/types/config.ts +68 -6
- package/src/types/provider.ts +7 -0
- package/src/types/request.ts +15 -0
- package/src/types/tools.ts +2 -0
- package/src/update/async-check.ts +1 -1
- package/src/update/badge.ts +2 -2
- package/src/update/index.ts +88 -15
- package/src/update/job-supervised-restart.ts +107 -0
- package/src/update/job.ts +32 -32
- package/src/update/notify.ts +1 -1
- package/src/update/refresh-scheduler.ts +2 -2
- package/src/update/supervised-recovery.d.mts +27 -0
- package/src/update/supervised-recovery.mjs +157 -0
- package/src/usage/cache-diagnostic.ts +2 -0
- package/src/usage/expected-prices.ts +16 -0
- package/src/usage/log.ts +19 -1
- package/src/web-search/gemini-executor.ts +2 -2
- package/assets/sponsors/packycode.png +0 -0
- package/gui/dist/assets/App-Ba8uG696.js +0 -54
- package/gui/dist/assets/Tray-IImbNPRy.js +0 -1
- package/gui/dist/assets/index-BHRZdJuv.js +0 -94
- package/gui/dist/assets/quota-summary-DiJO6sYY.js +0 -1
package/src/server/auth-cors.ts
CHANGED
|
@@ -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 {
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
476
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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: [
|
|
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(
|
|
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
|
-
|
|
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
|
|
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,
|