@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
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import type { ResponsesRequestContext, ResponsesAdmissionState, ResponsesDispatchers } from "./core-options";
|
|
1
|
+
import type { HandleResponsesOptions, ResponsesRequestContext, ResponsesAdmissionState, ResponsesDispatchers } from "./core-options";
|
|
2
|
+
import type { PolicyRequestEvidence } from "../../routing/evaluator";
|
|
3
|
+
import type { RequestLogContext } from "../request-log";
|
|
4
|
+
import { markProtocolEntry, protocolMarkFeatures } from "../../protocols/trace";
|
|
5
|
+
import { isAntigravityRoute } from "../antigravity-direct-eligibility";
|
|
2
6
|
import { capturePolicyRequestRoute, assertPolicyNormalizedRoute, recordPolicyPreparedDestination, policyCandidateRefusalResponse } from "./policy-request-scope";
|
|
3
7
|
import {
|
|
4
8
|
agentTaskRecoveryConfig,
|
|
@@ -33,6 +37,7 @@ import { resolveContextPrincipal } from "../auth-cors";
|
|
|
33
37
|
import { resolveSkillsSnapshotScopeKey, snapshotSkillsCatalogInBody } from "./skills-snapshot";
|
|
34
38
|
import {
|
|
35
39
|
isShadowSourceModel,
|
|
40
|
+
shadowCallPreservesSelectedModel,
|
|
36
41
|
shadowSourceModelPrefix,
|
|
37
42
|
shouldInterceptShadowCall,
|
|
38
43
|
} from "../../lib/shadow-call";
|
|
@@ -154,6 +159,48 @@ import {
|
|
|
154
159
|
|
|
155
160
|
import { redactHostedImageDisplayPaths } from "../responses-hosted-image-display";
|
|
156
161
|
|
|
162
|
+
/** Policy-routing evidence. The direct lane's settings-only body has no conversation to scan. */
|
|
163
|
+
function routeEvidence(options: Pick<HandleResponsesOptions, "directRequest">, parsed: OcxParsedRequest): PolicyRequestEvidence {
|
|
164
|
+
return options.directRequest?.evidence ?? evidenceFromBody(parsed._rawBody);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Leave the Antigravity direct lane for this request: put the bridge conversation back on the
|
|
169
|
+
* Responses body so every `_rawBody` reader sees what the bridge would have sent, and trace the
|
|
170
|
+
* request as the bridge. The IR conversation already decoded stays: it equals what
|
|
171
|
+
* `parseRequest` builds from the restored body (`tests/protocols/chat-ir-parity.test.ts`).
|
|
172
|
+
*/
|
|
173
|
+
function restoreDirectRequestConversation(options: HandleResponsesOptions, body: unknown, logCtx: RequestLogContext): void {
|
|
174
|
+
const direct = options.directRequest;
|
|
175
|
+
if (!direct) return;
|
|
176
|
+
options.directRequest = undefined;
|
|
177
|
+
if (body && typeof body === "object" && !Array.isArray(body)) Object.assign(body, direct.bridgeConversation());
|
|
178
|
+
if (options.clientEncoder) options.clientEncoder = { ...options.clientEncoder, requestLane: "bridge" };
|
|
179
|
+
markProtocolEntry(logCtx, {
|
|
180
|
+
inbound: direct.protocol,
|
|
181
|
+
lane: "bridge",
|
|
182
|
+
reasonCodes: ["not-migrated"],
|
|
183
|
+
features: protocolMarkFeatures(logCtx) ?? [],
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Leave the direct lane as soon as the route stops being the Antigravity route it was decided
|
|
189
|
+
* for. Called after every route assignment, so readers that run between a reroute and dispatch
|
|
190
|
+
* (canonical sanitization, ciphertext stripping, final normalization) see the bridge body.
|
|
191
|
+
*/
|
|
192
|
+
function leaveDirectLaneIfRerouted(
|
|
193
|
+
options: HandleResponsesOptions,
|
|
194
|
+
route: Pick<RouteResult, "providerName" | "modelId" | "provider">,
|
|
195
|
+
parsed: OcxParsedRequest,
|
|
196
|
+
logCtx: RequestLogContext,
|
|
197
|
+
): void {
|
|
198
|
+
const direct = options.directRequest;
|
|
199
|
+
if (!direct) return;
|
|
200
|
+
if (route.providerName === direct.providerName && route.modelId === direct.modelId && isAntigravityRoute(route)) return;
|
|
201
|
+
restoreDirectRequestConversation(options, parsed._rawBody, logCtx);
|
|
202
|
+
}
|
|
203
|
+
|
|
157
204
|
/** Parses, selects, and admits one request without changing the dispatch policy. */
|
|
158
205
|
export async function prepareResponsesRequest(
|
|
159
206
|
requestContext: Pick<ResponsesRequestContext, "options" | "config" | "req" | "logCtx">,
|
|
@@ -275,10 +322,11 @@ export async function prepareResponsesRequest(
|
|
|
275
322
|
}
|
|
276
323
|
}
|
|
277
324
|
let shadowCallIntercepted = false;
|
|
278
|
-
//
|
|
279
|
-
//
|
|
325
|
+
// Luna is also selectable by users/children. Both intercept sites use the same
|
|
326
|
+
// per-turn provenance; WS frames must not inherit the handshake's classification.
|
|
280
327
|
const threadSpawn = isThreadSpawnRequest(req.headers);
|
|
281
|
-
|
|
328
|
+
const preserveShadowModel = shadowCallPreservesSelectedModel(body, req.headers, options.inboundTransport);
|
|
329
|
+
if (!concreteSelection && !options.compactionRoutingOverride && !options.internalDecisionCall && !preserveShadowModel && !memoryModelApplies && body && typeof body === "object" && !Array.isArray(body)) {
|
|
282
330
|
const shadowIntercept = config.shadowCallIntercept;
|
|
283
331
|
const rawShadowModel = (body as { model?: unknown }).model;
|
|
284
332
|
if (shadowIntercept?.enabled && shadowIntercept.model && typeof rawShadowModel === "string"
|
|
@@ -304,6 +352,9 @@ export async function prepareResponsesRequest(
|
|
|
304
352
|
}
|
|
305
353
|
if (comboId && Object.hasOwn(config.combos ?? {}, comboId)) {
|
|
306
354
|
options.onRequestBodyRead?.();
|
|
355
|
+
// The Antigravity direct lane is decided for one concrete route; a body that reached a combo
|
|
356
|
+
// goes through the combo's own child loop with the full bridge conversation.
|
|
357
|
+
restoreDirectRequestConversation(options, body, logCtx);
|
|
307
358
|
return requestDispatchers.handleComboResponses(req, body, comboId, config, logCtx, {
|
|
308
359
|
...options,
|
|
309
360
|
// Concrete combo child selectors no longer match the shadow source model. Carry the
|
|
@@ -381,6 +432,10 @@ export async function prepareResponsesRequest(
|
|
|
381
432
|
let toolBridgeMaps: ReturnType<typeof buildToolBridgeMaps>;
|
|
382
433
|
try {
|
|
383
434
|
parsed = parseRequest(body);
|
|
435
|
+
// Antigravity direct lane: the body carried only the request settings, so the parse above
|
|
436
|
+
// produced them with an empty conversation; the conversation itself comes straight from the
|
|
437
|
+
// client body. Inside this try so a decode error answers exactly like a parse error.
|
|
438
|
+
if (options.directRequest) parsed = options.directRequest.decode(parsed);
|
|
384
439
|
if (options.inboundWire === "anthropic") {
|
|
385
440
|
parsed._nativeReasoningReplay = options.nativeReasoningReplay;
|
|
386
441
|
parsed._nativeReasoningMint = options.nativeReasoningMint;
|
|
@@ -580,8 +635,8 @@ export async function prepareResponsesRequest(
|
|
|
580
635
|
const resolved = concreteSelection
|
|
581
636
|
? routeConcreteModel(config, modelId)
|
|
582
637
|
: parsed._compactionRequest === true
|
|
583
|
-
? routeCompactionModel(config, modelId,
|
|
584
|
-
: routeModel(config, modelId,
|
|
638
|
+
? routeCompactionModel(config, modelId, routeEvidence(options, parsed))
|
|
639
|
+
: routeModel(config, modelId, routeEvidence(options, parsed));
|
|
585
640
|
// A Pool 2 selector may leave Pool 2 only through an operator-configured cross-provider
|
|
586
641
|
// blocked-model redirect, which the router marks as a credential-domain rewrite. Any other
|
|
587
642
|
// destination means the selector fell through to default resolution, which stays refused.
|
|
@@ -613,7 +668,7 @@ export async function prepareResponsesRequest(
|
|
|
613
668
|
}
|
|
614
669
|
const _sci = config.shadowCallIntercept;
|
|
615
670
|
let shadowRoute: RouteResult | undefined;
|
|
616
|
-
if (!concreteSelection && !memoryRoute && !options.memoryModelPhase && !options.compactionRoutingOverride && !options.internalDecisionCall && !
|
|
671
|
+
if (!concreteSelection && !memoryRoute && !options.memoryModelPhase && !options.compactionRoutingOverride && !options.internalDecisionCall && !preserveShadowModel && _sci?.enabled && _sci.model && isShadowSourceModel(parsed.modelId, _sci.sourceModels)) {
|
|
617
672
|
const sourcePrefix = shadowSourceModelPrefix(parsed.modelId, _sci.sourceModels)!;
|
|
618
673
|
let sourceIdentity = { providerName: OPENAI_CODEX_PROVIDER_ID, modelId: sourcePrefix };
|
|
619
674
|
try {
|
|
@@ -652,6 +707,7 @@ export async function prepareResponsesRequest(
|
|
|
652
707
|
if (parsed._compactionRequest === true || options.compactionRoutingOverride) parsed._cursorIsolateConversation = true;
|
|
653
708
|
route = memoryRoute ?? shadowRoute ?? resolveRoute(options.policyFallbackCandidate
|
|
654
709
|
? `${options.policyFallbackCandidate.provider}/${options.policyFallbackCandidate.model}` : parsed.modelId);
|
|
710
|
+
leaveDirectLaneIfRerouted(options, route, parsed, logCtx);
|
|
655
711
|
// Name the phase in the persisted route decision, so the request log says why this turn went to
|
|
656
712
|
// the memory destination instead of leaving it looking like a plain user selection. Set here, on
|
|
657
713
|
// the resolved route, so a combo child's own route carries it too.
|
|
@@ -891,8 +947,9 @@ export async function prepareResponsesRequest(
|
|
|
891
947
|
if (fallback?.to && !slugsEquivalent(fallback.to, route.modelId)) {
|
|
892
948
|
try {
|
|
893
949
|
route = captureInboundRoutePolicy(
|
|
894
|
-
routeModel(config, fallback.to,
|
|
950
|
+
routeModel(config, fallback.to, routeEvidence(options, parsed)),
|
|
895
951
|
);
|
|
952
|
+
leaveDirectLaneIfRerouted(options, route, parsed, logCtx);
|
|
896
953
|
credentialDomainWasRewritten = true;
|
|
897
954
|
logCtx.routeDecision = route.routeDecision;
|
|
898
955
|
logCtx.policyEligibility = route.policyEligibility;
|
|
@@ -1107,8 +1164,9 @@ export async function prepareResponsesRequest(
|
|
|
1107
1164
|
if (fallback?.to && !slugsEquivalent(fallback.to, route.modelId)) {
|
|
1108
1165
|
try {
|
|
1109
1166
|
route = captureInboundRoutePolicy(
|
|
1110
|
-
routeModel(config, fallback.to,
|
|
1167
|
+
routeModel(config, fallback.to, routeEvidence(options, parsed)),
|
|
1111
1168
|
);
|
|
1169
|
+
leaveDirectLaneIfRerouted(options, route, parsed, logCtx);
|
|
1112
1170
|
credentialDomainWasRewritten = true;
|
|
1113
1171
|
logCtx.routeDecision = route.routeDecision;
|
|
1114
1172
|
logCtx.policyEligibility = route.policyEligibility;
|
|
@@ -1255,6 +1313,8 @@ export async function prepareResponsesRequest(
|
|
|
1255
1313
|
// for streaming clients.
|
|
1256
1314
|
const clientRequestedStream = parsed.stream;
|
|
1257
1315
|
const admittedPolicyRoute = { providerName: route.providerName, modelId: route.modelId };
|
|
1316
|
+
// Final guard before normalization may inject guidance into the raw body.
|
|
1317
|
+
leaveDirectLaneIfRerouted(options, route, parsed, logCtx);
|
|
1258
1318
|
await applyFinalRouteRequestNormalization({
|
|
1259
1319
|
parsed,
|
|
1260
1320
|
route,
|
|
@@ -1450,6 +1510,11 @@ export async function prepareResponsesRequest(
|
|
|
1450
1510
|
commitSkillsSnapshot?.();
|
|
1451
1511
|
return {
|
|
1452
1512
|
inboundWire,
|
|
1513
|
+
clientDeclaredEmptyToolCatalog: options.clientDeclaredEmptyToolCatalog === true,
|
|
1514
|
+
// Admission facts later route rewrites must not re-derive: the selector the caller sent and
|
|
1515
|
+
// the key scope as it stood when this request was admitted.
|
|
1516
|
+
inboundSelector,
|
|
1517
|
+
admissionScope,
|
|
1453
1518
|
translatorBudget,
|
|
1454
1519
|
parsed,
|
|
1455
1520
|
toolBridgeMaps,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { SendBudgetExhaustedError } from "../../lib/upstream-retry";
|
|
2
|
+
import { anthropicBillingPhysicalFetch } from "../../providers/anthropic-billing-ownership";
|
|
2
3
|
import { rebindPhysicalSend } from "../../lib/request-execution-budget";
|
|
3
4
|
import { anthropicModelQuotaFor } from "../../oauth/anthropic-model-quota";
|
|
4
5
|
import { anthropicRatePolicyFor } from "../../oauth/anthropic-rate-limit-policy";
|
|
@@ -55,6 +56,11 @@ import { stampOAuthAccountLabel, usesApiKeyAccount } from "../../providers/label
|
|
|
55
56
|
import { resolveProviderTransport } from "../../providers/xai-transport";
|
|
56
57
|
import { captureRouteStaticPolicy } from "../../router";
|
|
57
58
|
import { CopilotAutoHttpError, resolveCopilotAuto } from "../../providers/github-copilot-auto";
|
|
59
|
+
import {
|
|
60
|
+
AdmissionModelDeniedError,
|
|
61
|
+
admissionModelDeniedResponse,
|
|
62
|
+
assertRouteAllowedByScope,
|
|
63
|
+
} from "../admission-model-scope";
|
|
58
64
|
import { renameRoutedIdentityInContext } from "../../adapters/identity";
|
|
59
65
|
import { resolveCopilotApiBaseUrl } from "../../oauth/github-copilot";
|
|
60
66
|
import {
|
|
@@ -87,7 +93,7 @@ import {
|
|
|
87
93
|
import type { AttemptRecoveryKind } from "../../usage/log";
|
|
88
94
|
import { bindAttemptDeliveryRecorder } from "../../usage/attempt-delivery";
|
|
89
95
|
import { resolvePassiveRouteSubjectId } from "../passive-route-linker";
|
|
90
|
-
import { clientCancelledResponse } from "./core-errors";
|
|
96
|
+
import { anthropicPoolCooldownResponse, clientCancelledResponse } from "./core-errors";
|
|
91
97
|
import { acquireAccountLease, KIRO_ACCOUNT_WAIT_MS } from "../../oauth/kiro-account-load";
|
|
92
98
|
|
|
93
99
|
/** Return a renewed Chat wire to the native owner before sending inference. */
|
|
@@ -108,6 +114,8 @@ export async function prepareResponsesTransport(
|
|
|
108
114
|
| "inboundWire"
|
|
109
115
|
| "selectedForwardHeaders"
|
|
110
116
|
| "translatorBudget"
|
|
117
|
+
| "inboundSelector"
|
|
118
|
+
| "admissionScope"
|
|
111
119
|
>,
|
|
112
120
|
) {
|
|
113
121
|
const { config, logCtx, options, req } = requestContext;
|
|
@@ -296,6 +304,7 @@ export async function prepareResponsesTransport(
|
|
|
296
304
|
const copilotRequestedModel = route.modelId;
|
|
297
305
|
/** Project only bounded Copilot refusal metadata; upstream bodies and session tokens never reach the client. */
|
|
298
306
|
const copilotRefusalResponse = (error: unknown): Response | undefined => {
|
|
307
|
+
if (error instanceof AdmissionModelDeniedError) return admissionModelDeniedResponse(error);
|
|
299
308
|
if (!(error instanceof CopilotAutoHttpError) || ![401, 403, 429].includes(error.status)) return;
|
|
300
309
|
const response = formatErrorResponse(error.status, error.status === 429 ? "rate_limit_error" : "authentication_error",
|
|
301
310
|
error.message);
|
|
@@ -356,6 +365,13 @@ export async function prepareResponsesTransport(
|
|
|
356
365
|
replayOAuthCredentialSnapshot = { accountId: refreshed.accountId, generation: refreshed.generation };
|
|
357
366
|
selected = await negotiate();
|
|
358
367
|
}
|
|
368
|
+
assertRouteAllowedByScope(
|
|
369
|
+
// The scope captured at admission: a key edited or removed while negotiation was in
|
|
370
|
+
// flight must not widen this request. The refusal names the caller's own selector.
|
|
371
|
+
requestState.admissionScope,
|
|
372
|
+
requestState.inboundSelector,
|
|
373
|
+
{ providerName: route.providerName, modelId: selected.modelId },
|
|
374
|
+
);
|
|
359
375
|
copilotSessionExpiresAt = selected.expiresAt ?? Infinity;
|
|
360
376
|
route.provider = selected.provider;
|
|
361
377
|
route.modelId = selected.modelId;
|
|
@@ -697,7 +713,7 @@ export async function prepareResponsesTransport(
|
|
|
697
713
|
}
|
|
698
714
|
commitKeyAttemptSend();
|
|
699
715
|
response = await sendWithConnectionPolicy(
|
|
700
|
-
fetchImpl,
|
|
716
|
+
anthropicBillingPhysicalFetch(fetchImpl, route.providerName, route.provider),
|
|
701
717
|
destination,
|
|
702
718
|
{ ...dispatchInit, redirect: "manual" },
|
|
703
719
|
{ providerName: route.providerName, provider: route.provider },
|
|
@@ -724,6 +740,7 @@ export async function prepareResponsesTransport(
|
|
|
724
740
|
let nextAdapter: ProviderAdapter;
|
|
725
741
|
try { nextAdapter = await refreshDispatchAdapter(requestParsed); }
|
|
726
742
|
catch (error) {
|
|
743
|
+
if (error instanceof AdmissionModelDeniedError) throw error;
|
|
727
744
|
const refusal = copilotRefusalResponse(error);
|
|
728
745
|
if (refusal) return refusal;
|
|
729
746
|
throw error;
|
|
@@ -784,12 +801,9 @@ export async function prepareResponsesTransport(
|
|
|
784
801
|
// Route names may resemble account IDs; log only the matched rule position.
|
|
785
802
|
if (anthropicRouteDecision) console.warn(`[${anthropicInstance}-pool] route:#${anthropicRouteDecision.position} ${selection.reason}; answering locally`);
|
|
786
803
|
if (selection.reason === "all-cooled") {
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
"rate_limit_error",
|
|
791
|
-
anthropicRouteDecision ? "Anthropic OAuth accounts for this model route are temporarily rate-limited" : "All Anthropic OAuth accounts are temporarily rate-limited",
|
|
792
|
-
retryAfterSec !== null ? { retryAfter: String(retryAfterSec) } : undefined,
|
|
804
|
+
return anthropicPoolCooldownResponse(
|
|
805
|
+
anthropicRouting!.anthropicPoolCooldownError(Date.now(), anthropicRouteDecision, route.modelId),
|
|
806
|
+
{ comboAttempt: options.comboAttempt, logCtx },
|
|
793
807
|
);
|
|
794
808
|
}
|
|
795
809
|
return formatErrorResponse(401, "authentication_error", anthropicRouteDecision ? "No eligible Anthropic OAuth account for this model route" : "No eligible Anthropic OAuth account available");
|
|
@@ -23,6 +23,7 @@ import { normalizeLogConversationId } from "../request-log-conversation";
|
|
|
23
23
|
import { normalizeDeclaredToolName, type AdapterEvent, type OcxProviderContinuationState } from "../../types";
|
|
24
24
|
import { adapterFailureFromMessage, SEND_BUDGET_EXHAUSTED_CODE, SPEND_LEDGER_STORAGE_UNAVAILABLE_CODE, type OcxErrorPayload } from "../../lib/errors";
|
|
25
25
|
import { SendBudgetExhaustedError, markResponseNonReplayable } from "../../lib/upstream-retry";
|
|
26
|
+
import { hasKeyPoolFailover, rotateProviderTransportOn401, rotateProviderTransportOn429 } from "../../providers/key-failover";
|
|
26
27
|
import {
|
|
27
28
|
hasEligibleGenericOAuthFailoverTarget,
|
|
28
29
|
isGenericOAuthFailoverEnabled,
|
|
@@ -224,6 +225,11 @@ export async function executeResponsesRunTurn(
|
|
|
224
225
|
// in-flight; only the latest dispatch may merge adapter-written route
|
|
225
226
|
// state back onto `parsed`, or the superseded attempt would restore the
|
|
226
227
|
// failed account's cursor/continuation over the rotation's rebind.
|
|
228
|
+
type KeyBinding = Extract<NonNullable<ReturnType<typeof adapterBindings.get>>, { kind: "api-key" }>;
|
|
229
|
+
const failedKeyBindings = new WeakMap<Extract<AdapterEvent, { type: "error" }>, KeyBinding>();
|
|
230
|
+
const failedKeys = new Set<string>();
|
|
231
|
+
const keyAttemptLimit = route.provider.apiKeyPool?.length ?? 0;
|
|
232
|
+
let lastKeyFailure: Extract<AdapterEvent, { type: "error" }> | undefined;
|
|
227
233
|
let runTurnAttemptSeq = 0;
|
|
228
234
|
const runTurnAttempt = async (
|
|
229
235
|
targetQueue: AdapterEventQueue,
|
|
@@ -238,7 +244,9 @@ export async function executeResponsesRunTurn(
|
|
|
238
244
|
const attemptSeq = ++runTurnAttemptSeq;
|
|
239
245
|
if (attemptSeq === 1 && options.comboInitialSend) options.comboInitialSend.producerOwned = true;
|
|
240
246
|
let pacingSlot = preacquiredSlot;
|
|
247
|
+
let sentKeyBinding: KeyBinding | undefined;
|
|
241
248
|
const emit = (event: AdapterEvent) => {
|
|
249
|
+
if (event.type === "error" && sentKeyBinding) failedKeyBindings.set(event, sentKeyBinding);
|
|
242
250
|
options.onCompactionRecoveryAdapterEvent?.(event);
|
|
243
251
|
targetQueue.push(event);
|
|
244
252
|
};
|
|
@@ -249,6 +257,12 @@ export async function executeResponsesRunTurn(
|
|
|
249
257
|
pacingSlot = await waitForProviderRequestSlot(route.providerName, route.provider, route.modelId, runTurnAbort.signal);
|
|
250
258
|
}
|
|
251
259
|
await refreshRunTurnSelection();
|
|
260
|
+
const selectedBinding = adapterBindings.get(transportState.runTurnAdapter);
|
|
261
|
+
if (lastKeyFailure && selectedBinding?.kind === "api-key"
|
|
262
|
+
&& failedKeys.has(selectedBinding.provider.apiKey ?? "")) {
|
|
263
|
+
emit(lastKeyFailure);
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
252
266
|
// LOCAL PATCH (runturn-websearch): refreshRunTurnSelection binds route
|
|
253
267
|
// state onto `parsed`; mirror it onto the iteration-local copy the
|
|
254
268
|
// adapter actually receives.
|
|
@@ -293,13 +307,14 @@ export async function executeResponsesRunTurn(
|
|
|
293
307
|
// The only way the request budget reaches a transport the adapter owns. Without it
|
|
294
308
|
// a Cursor turn's inner ladder was three physical sends the cap read as one.
|
|
295
309
|
...(adapterDispatchBudget ? { sendBudget: adapterDispatchBudget } : {}),
|
|
296
|
-
onPhysicalSend: send =>
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
310
|
+
onPhysicalSend: send => {
|
|
311
|
+
// Capture once at this attempt's send, before later selection or recovery changes it.
|
|
312
|
+
const binding = adapterBindings.get(transportState.runTurnAdapter);
|
|
313
|
+
if (!sentKeyBinding && binding?.kind === "api-key") sentKeyBinding = binding;
|
|
314
|
+
noteAdapterPhysicalSend(logCtx.usageLogInputTokens,
|
|
315
|
+
{ ...send, ...(send.recovery ?? recovery ? { recovery: send.recovery ?? recovery } : {}) },
|
|
316
|
+
{ includeFirst: reportsOwnSends });
|
|
317
|
+
},
|
|
303
318
|
onRecoveryWithheld: noteAdapterRecoveryWithheld,
|
|
304
319
|
},
|
|
305
320
|
emit,
|
|
@@ -361,6 +376,7 @@ export async function executeResponsesRunTurn(
|
|
|
361
376
|
const runTurn = async (): Promise<void> => runTurnAttempt(queue, undefined, true, wsFirstParsed, initialPacingSlot);
|
|
362
377
|
const runTurnFailoverArmed = () =>
|
|
363
378
|
route.provider.authMode === "oauth"
|
|
379
|
+
|| (transportState.runTurnAdapter.name === "qoder" && hasKeyPoolFailover(route.provider))
|
|
364
380
|
|| !!(transportState.genericFailoverAccountId
|
|
365
381
|
&& isGenericOAuthFailoverEnabled(config, route.providerName));
|
|
366
382
|
const dispatchSearchIteration = (iterParsed: PreparedResponsesRequest["parsed"]): AsyncIterable<AdapterEvent> => {
|
|
@@ -464,9 +480,51 @@ export async function executeResponsesRunTurn(
|
|
|
464
480
|
return false;
|
|
465
481
|
}
|
|
466
482
|
};
|
|
483
|
+
const recoverRunTurnKey = async (
|
|
484
|
+
error: Extract<AdapterEvent, { type: "error" }>,
|
|
485
|
+
): Promise<AttemptRecoveryKind | undefined> => {
|
|
486
|
+
const binding = failedKeyBindings.get(error);
|
|
487
|
+
if (error.apiKeyRecovery !== true || (error.status !== 401 && error.status !== 429)
|
|
488
|
+
|| !binding || runTurnAbort.signal.aborted || !hasKeyPoolFailover(route.provider)
|
|
489
|
+
|| failedKeys.size >= keyAttemptLimit) return undefined;
|
|
490
|
+
const failed = binding.provider;
|
|
491
|
+
if (!failed.apiKey || !failed._apiKeyAttempt || failedKeys.has(failed.apiKey)) return undefined;
|
|
492
|
+
const hop = reserveCredentialHop("auth-recovery", `${route.providerName}|${route.modelId}|runturn-key-${error.status}`);
|
|
493
|
+
if (!hop.allowed) return undefined;
|
|
494
|
+
failedKeys.add(failed.apiKey);
|
|
495
|
+
lastKeyFailure = error;
|
|
496
|
+
try {
|
|
497
|
+
const rotate = error.status === 401 ? rotateProviderTransportOn401 : rotateProviderTransportOn429;
|
|
498
|
+
const rotated = rotate(config, route.providerName, failed, {
|
|
499
|
+
attemptedKey: failed.apiKey, attemptedSelection: failed._apiKeyAttempt,
|
|
500
|
+
promptCacheKey: parsed.options.promptCacheKey,
|
|
501
|
+
});
|
|
502
|
+
if (!rotated || failedKeys.size >= keyAttemptLimit || failedKeys.has(rotated.apiKey ?? "") || runTurnAbort.signal.aborted) {
|
|
503
|
+
hop.permit?.release();
|
|
504
|
+
return undefined;
|
|
505
|
+
}
|
|
506
|
+
route.provider = rotated;
|
|
507
|
+
const adapter = await refreshRunTurnAdapter(parsed);
|
|
508
|
+
if (!adapter.runTurn || failedKeys.has(route.provider.apiKey ?? "") || runTurnAbort.signal.aborted) {
|
|
509
|
+
hop.permit?.release();
|
|
510
|
+
return undefined;
|
|
511
|
+
}
|
|
512
|
+
bindRouteReasoningReplayScope({ parsed, providerName: route.providerName, provider: route.provider,
|
|
513
|
+
adapterName: adapter.name, forwardHeaders: requestState.selectedForwardHeaders });
|
|
514
|
+
sealRequestAttemptIdentity(logCtx.activeAttempt, logCtx.provider, adapter.name, logCtx.accountLogLabel);
|
|
515
|
+
recordAttemptCredentialSource(logCtx.activeAttempt, route.providerName, route.provider, adapter.name);
|
|
516
|
+
sendBudgetState.pendingHopPermit = hop.permit;
|
|
517
|
+
return error.status === 401 ? "key-401" : "key-429";
|
|
518
|
+
} catch {
|
|
519
|
+
hop.permit?.release();
|
|
520
|
+
return undefined;
|
|
521
|
+
}
|
|
522
|
+
};
|
|
467
523
|
const recoverRunTurnAdapterOnPreflightError = async (
|
|
468
524
|
error: Extract<AdapterEvent, { type: "error" }>,
|
|
469
525
|
): Promise<AttemptRecoveryKind | undefined> => {
|
|
526
|
+
const keyRecovery = await recoverRunTurnKey(error);
|
|
527
|
+
if (keyRecovery) return keyRecovery;
|
|
470
528
|
if (await recoverRunTurnAdapterOnPreflight401(error)) return "oauth-401";
|
|
471
529
|
if (await rotateRunTurnAdapterOnPreflight429(error)) return "oauth-account-429";
|
|
472
530
|
return undefined;
|
|
@@ -683,7 +741,11 @@ export async function executeResponsesRunTurn(
|
|
|
683
741
|
const stallTimeoutSec = wsPlan?.stallTimeoutSec ?? config.stallTimeoutSec;
|
|
684
742
|
// A disabled watchdog (0) is not a zero preflight: it would commit SSE before Devin's first
|
|
685
743
|
// event and deliver a pre-output 429 in-stream. Fall back to the default finite bound.
|
|
686
|
-
|
|
744
|
+
// A key-pool preflight must not outlast the stall budget, or a silent CLI holds the SSE
|
|
745
|
+
// Response (heartbeats, stall watchdog) hostage; the OAuth preflight wait is unchanged.
|
|
746
|
+
const keyPoolPreflight = transportState.runTurnAdapter.name === "qoder"
|
|
747
|
+
&& route.provider.authMode !== "oauth" && hasKeyPoolFailover(route.provider);
|
|
748
|
+
const preflightDeadlineAt = grokDevinPreflight || keyPoolPreflight
|
|
687
749
|
? Date.now() + (resolveStallTimeoutSec(stallTimeoutSec) || DEFAULT_STALL_TIMEOUT_SEC) * 1_000
|
|
688
750
|
: undefined;
|
|
689
751
|
if (runTurnFailoverArmed()) {
|
|
@@ -7,6 +7,9 @@ import type { ResponsesEffects } from "./response-effects";
|
|
|
7
7
|
import type { ResponsesSendBudget } from "./request-send-budget";
|
|
8
8
|
import { formatErrorResponse } from "../../bridge";
|
|
9
9
|
import { planWebSearch, buildWebSearchTool, runWithWebSearch } from "../../web-search";
|
|
10
|
+
import { createRegisteredAdvisorPlan } from "./advisor-plan-slot";
|
|
11
|
+
import { ADVISOR_TOOL_NAME } from "./advisor-slot";
|
|
12
|
+
import { buildToolBridgeMaps } from "./collaboration";
|
|
10
13
|
import {
|
|
11
14
|
planImageBridge,
|
|
12
15
|
planVideoBridge,
|
|
@@ -30,9 +33,11 @@ import {
|
|
|
30
33
|
} from "../../oauth/generic-account-failover";
|
|
31
34
|
import { classifyKiroRefusal } from "../../adapters/kiro-refusal";
|
|
32
35
|
import { isNonReplayableResponse } from "../../lib/upstream-retry";
|
|
36
|
+
import { unwrapUpstreamRetryEvidenceError } from "../../codex/auth-context";
|
|
37
|
+
import { AdmissionModelDeniedError, admissionModelDeniedResponse } from "../admission-model-scope";
|
|
33
38
|
import { noteKiroMonthlyRefusal, noteKiroServedSuccess } from "../../providers/kiro-usage";
|
|
34
39
|
import { persistKiroAccountState } from "../../providers/kiro-account-state-disk";
|
|
35
|
-
import { readDisplaySafeErrorText } from "./core-errors";
|
|
40
|
+
import { anthropicPoolCooldownResponse, readDisplaySafeErrorText } from "./core-errors";
|
|
36
41
|
import {
|
|
37
42
|
ANTHROPIC_POOL_MAX_FAILOVERS_PER_REQUEST,
|
|
38
43
|
anthropicRoutingFor,
|
|
@@ -60,6 +65,7 @@ export async function executeResponsesSidecars(
|
|
|
60
65
|
| "inboundWire"
|
|
61
66
|
| "selectedForwardHeaders"
|
|
62
67
|
| "translatorBudget"
|
|
68
|
+
| "toolBridgeMaps"
|
|
63
69
|
| "rememberKiroDeliveredFinalAnswer"
|
|
64
70
|
| "responseStateOptions"
|
|
65
71
|
>,
|
|
@@ -169,6 +175,25 @@ export async function executeResponsesSidecars(
|
|
|
169
175
|
}
|
|
170
176
|
}
|
|
171
177
|
|
|
178
|
+
// Advisor sidecar plan (optional subsystem; null when disabled, unconfigured, or when this
|
|
179
|
+
// request IS an advisor loopback consultation — the recursion fence). The plan carries
|
|
180
|
+
// request-scoped state only; preflight keeps fixed policy separate from user-role advice
|
|
181
|
+
// before the worker is dispatched. Registration seam: the core path sees only
|
|
182
|
+
// `parsed._advisorGuard`; only an activated factory can load the optional implementation.
|
|
183
|
+
const advisorPlan = options.advisorInternal === true
|
|
184
|
+
? null
|
|
185
|
+
: await createRegisteredAdvisorPlan({
|
|
186
|
+
config,
|
|
187
|
+
workerIdentity: `${route.modelId} (provider ${route.providerName})`,
|
|
188
|
+
workerModelId: route.modelId,
|
|
189
|
+
abortSignal: options.abortSignal,
|
|
190
|
+
});
|
|
191
|
+
// Preflight only on real worker turns: a routed-compaction turn is a summarization request,
|
|
192
|
+
// and injecting advice into it would pollute the summary Codex replaces its history with.
|
|
193
|
+
if (advisorPlan && !routedCompaction) {
|
|
194
|
+
await advisorPlan.preflightInject(parsed);
|
|
195
|
+
}
|
|
196
|
+
|
|
172
197
|
// Image / web-search sidecars: plan once, then dispatch with runTurn-aware priority.
|
|
173
198
|
// Routed-compaction turns must NOT hit the image bridge: compaction clears tools/_webSearch but
|
|
174
199
|
// leaves _imageGeneration, so planImageBridge would activate and return a normal Responses
|
|
@@ -198,6 +223,20 @@ export async function executeResponsesSidecars(
|
|
|
198
223
|
// A single request may rotate once for a verification refusal. Keep this separate from the
|
|
199
224
|
// broader OAuth failover count: a sidecar turn can also spend that count on rate-limit recovery.
|
|
200
225
|
let antigravityValidationRotationAttempted = false;
|
|
226
|
+
// A replacement credential whose Copilot Auto negotiation leaves the key's scope ends the turn.
|
|
227
|
+
// The loops only see "no rotation" and fail with the original, replayable 429, so the refusal
|
|
228
|
+
// is held here and answers in place of that failure.
|
|
229
|
+
let sidecarScopeDenial: AdmissionModelDeniedError | undefined;
|
|
230
|
+
const noteSidecarScopeDenial = (error: unknown): void => {
|
|
231
|
+
const refusal = unwrapUpstreamRetryEvidenceError(error);
|
|
232
|
+
if (refusal instanceof AdmissionModelDeniedError) sidecarScopeDenial ??= refusal;
|
|
233
|
+
};
|
|
234
|
+
const sidecarScopeRefusal = (response: Response): Response | undefined => {
|
|
235
|
+
if (!sidecarScopeDenial || response.ok) return undefined;
|
|
236
|
+
void response.body?.cancel().catch(() => {});
|
|
237
|
+
releaseSearchProbeLease();
|
|
238
|
+
return admissionModelDeniedResponse(sidecarScopeDenial);
|
|
239
|
+
};
|
|
201
240
|
/** Admit a replacement credential, or leave the loop's original refusal authoritative. */
|
|
202
241
|
const rotateSidecarProviderOn429 = async (
|
|
203
242
|
retryAfter: string | null,
|
|
@@ -264,7 +303,8 @@ export async function executeResponsesSidecars(
|
|
|
264
303
|
return null;
|
|
265
304
|
}
|
|
266
305
|
transportState.genericFailovers += 1;
|
|
267
|
-
} catch {
|
|
306
|
+
} catch (error) {
|
|
307
|
+
noteSidecarScopeDenial(error);
|
|
268
308
|
hop.permit?.release();
|
|
269
309
|
return null;
|
|
270
310
|
}
|
|
@@ -284,7 +324,10 @@ export async function executeResponsesSidecars(
|
|
|
284
324
|
} else if (rotated) {
|
|
285
325
|
route.provider = rotated;
|
|
286
326
|
try { await resolveCopilotSelection(retryParsed ?? parsed); }
|
|
287
|
-
catch {
|
|
327
|
+
catch (error) { // Keep the sidecar's original refusal when replacement negotiation fails.
|
|
328
|
+
noteSidecarScopeDenial(error);
|
|
329
|
+
return null;
|
|
330
|
+
}
|
|
288
331
|
} else if (
|
|
289
332
|
// A POSITIVE gate, not an early return. An early `return null` here made every later arm
|
|
290
333
|
// unreachable: Anthropic never has a genericFailoverAccountId (isGenericFailoverProvider
|
|
@@ -326,7 +369,8 @@ export async function executeResponsesSidecars(
|
|
|
326
369
|
return null;
|
|
327
370
|
}
|
|
328
371
|
if (route.providerName === "kiro") transportState.genericFailovers += 1;
|
|
329
|
-
} catch {
|
|
372
|
+
} catch (error) {
|
|
373
|
+
noteSidecarScopeDenial(error);
|
|
330
374
|
hop.permit?.release();
|
|
331
375
|
return null;
|
|
332
376
|
}
|
|
@@ -527,6 +571,8 @@ export async function executeResponsesSidecars(
|
|
|
527
571
|
notifyResponseComplete(response);
|
|
528
572
|
},
|
|
529
573
|
});
|
|
574
|
+
const imgScopeRefusal = sidecarScopeRefusal(imgResponse);
|
|
575
|
+
if (imgScopeRefusal) return imgScopeRefusal;
|
|
530
576
|
if (imgResponse.body) {
|
|
531
577
|
const imgTurnAc = new AbortController();
|
|
532
578
|
imgTurnAc.signal.addEventListener("abort", cancelResponseCompletion, { once: true });
|
|
@@ -555,7 +601,7 @@ export async function executeResponsesSidecars(
|
|
|
555
601
|
modelId: route.modelId,
|
|
556
602
|
...sidecarBudget.fetchOptions(transportState.adapter),
|
|
557
603
|
})(input, init)) as typeof globalThis.fetch;
|
|
558
|
-
const
|
|
604
|
+
const loopResponse = await runWithWebSearch({
|
|
559
605
|
parsed, adapter: transportState.adapter,
|
|
560
606
|
onIterationEnd: sidecarBudget.releaseUnsentHop,
|
|
561
607
|
fetchForRequest: (request, iterParsed, adapter) => providerFetch(route.provider, options.codexWsRuntimeIdentity, {
|
|
@@ -622,6 +668,17 @@ export async function executeResponsesSidecars(
|
|
|
622
668
|
notifyResponseComplete(response);
|
|
623
669
|
},
|
|
624
670
|
});
|
|
671
|
+
const wsScopeRefusal = sidecarScopeRefusal(loopResponse);
|
|
672
|
+
if (wsScopeRefusal) return wsScopeRefusal;
|
|
673
|
+
// A routed Anthropic model whose last pool account refused on spent quota fails the loop's
|
|
674
|
+
// first iteration with a generic 429 before any search runs. Answer the usage limit instead,
|
|
675
|
+
// as the main path does (#6991), so Codex stops rather than retrying the whole turn.
|
|
676
|
+
const poolUsageLimit = loopResponse.status === 429 && anthropicInstance && route.provider.authMode === "oauth"
|
|
677
|
+
&& !options.comboAttempt
|
|
678
|
+
? anthropicRoutingFor(anthropicInstance).anthropicPoolUsageLimitAfterRefusal(Date.now(), transportState.anthropicRouteDecision, route.modelId)
|
|
679
|
+
: null;
|
|
680
|
+
if (poolUsageLimit) void loopResponse.body?.cancel().catch(() => {});
|
|
681
|
+
const wsResponse = poolUsageLimit ? anthropicPoolCooldownResponse(poolUsageLimit, { logCtx }) : loopResponse;
|
|
625
682
|
// Register the sidecar stream as an active turn so drainAndShutdown waits for (or aborts)
|
|
626
683
|
// in-flight web-search turns instead of skipping them during graceful shutdown.
|
|
627
684
|
if (wsResponse.body) {
|
|
@@ -636,5 +693,32 @@ export async function executeResponsesSidecars(
|
|
|
636
693
|
return wsResponse;
|
|
637
694
|
}
|
|
638
695
|
|
|
696
|
+
// Advisor synthetic tool + stream guard: attach ONLY when this function is about to hand the
|
|
697
|
+
// request back to the normal translated exchange (no sidecar loop claimed the turn, not a
|
|
698
|
+
// run-turn adapter, not compaction). The web-search/image loops own their own event handling;
|
|
699
|
+
// run-turn adapters execute their own loop; both would leak the synthetic tool call they
|
|
700
|
+
// cannot intercept. Preflight (above) still applies to those paths — only the tool does not.
|
|
701
|
+
if (advisorPlan && !routedCompaction && !transportState.adapter.runTurn && !wsPlan && !imgPlan && !vidPlan) {
|
|
702
|
+
// A client-declared tool named "advisor" would collide with the synthetic injection (one
|
|
703
|
+
// wire name, two schemas); the synthetic runtime owns the name for this turn.
|
|
704
|
+
parsed.context.tools = [
|
|
705
|
+
...(parsed.context.tools ?? []).filter(t => !t.advisor && t.name !== ADVISOR_TOOL_NAME),
|
|
706
|
+
advisorPlan.tool,
|
|
707
|
+
];
|
|
708
|
+
// The advisor tool joined AFTER prepare computed the bridge maps; recompute so the tool is
|
|
709
|
+
// declared (undeclared-tool guard, tool_choice mapping, schema repair) on this turn.
|
|
710
|
+
//
|
|
711
|
+
// Rebuild WITHOUT the budget: prepare already charged every tool in the catalog, and
|
|
712
|
+
// buildToolBridgeMaps charges each name it registers, so re-passing the budget would bill the
|
|
713
|
+
// whole catalog a second time and can trip `translation_buffer_limit` for a large MCP catalog.
|
|
714
|
+
// Charge only the one entry that is genuinely new — the synthetic advisor tool is a bare
|
|
715
|
+
// function tool, so it retains its wire name and its bare name (collaboration.ts:187, :233).
|
|
716
|
+
requestState.toolBridgeMaps = buildToolBridgeMaps(parsed);
|
|
717
|
+
translatorBudget.chargeRetained(new TextEncoder().encode(ADVISOR_TOOL_NAME).byteLength * 2, {
|
|
718
|
+
kind: "retained_collectors",
|
|
719
|
+
});
|
|
720
|
+
advisorPlan.attachGuard(parsed);
|
|
721
|
+
}
|
|
722
|
+
|
|
639
723
|
return undefined;
|
|
640
724
|
}
|
|
@@ -170,7 +170,8 @@ export function isTerminalGuardPassthroughOnly(event: AdapterEvent): boolean {
|
|
|
170
170
|
return event.type === "heartbeat" || event.type === "tool_call_delta";
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
-
|
|
173
|
+
/** Merge two reported usages (canonical Responses convention). Shared with the advisor guard. */
|
|
174
|
+
export function mergeUsage(first: OcxUsage | undefined, second: OcxUsage | undefined): OcxUsage | undefined {
|
|
174
175
|
if (!first) return second;
|
|
175
176
|
if (!second) return first;
|
|
176
177
|
const sumOptional = (key: keyof OcxUsage): number | undefined => {
|
|
@@ -23,8 +23,9 @@ import { CODEX_RESPONSES_HTTP_URL, CODEX_RESPONSES_WS_URL, CODEX_WS_FRAME_HEADER
|
|
|
23
23
|
import { codexWsExchange } from "./codex-ws-exchange";
|
|
24
24
|
import { CodexWsSession } from "./codex-ws-session";
|
|
25
25
|
import { codexWsPool, codexWsReuseIdentity } from "./codex-ws-pool";
|
|
26
|
-
import { codexWsCreateFrameExceedsLimit } from "./codex-ws-wire";
|
|
26
|
+
import { codexWsCreateFrameExceedsLimit, type CodexWsPreOutputRefusalHold } from "./codex-ws-wire";
|
|
27
27
|
import { isLoopbackUrl, rewriteWebSocketDial } from "../../plugins/upstream-hooks";
|
|
28
|
+
import { outboundCredentials } from "../../lib/outbound-credential-registry";
|
|
28
29
|
export { CODEX_WS_LIVENESS_PING_INTERVAL_MS, CODEX_WS_RESPONSE_PRELUDE_TIMEOUT_MS, MAX_CODEX_WS_FRAME_BYTES, MAX_CODEX_WS_QUEUE_BYTES,
|
|
29
30
|
MAX_CODEX_WS_CREATE_FRAME_BYTES, CODEX_WS_CREATE_FRAME_LIMIT_BYTES, codexWsCreateFrameExceedsLimit,
|
|
30
31
|
isCodexWsQuotaObservedResponse, isCodexWsUpstreamResponse, isCodexWsPreludeProjection } from "./codex-ws-wire";
|
|
@@ -162,6 +163,8 @@ export async function codexWsUpstreamFetch(
|
|
|
162
163
|
nativeControl?: NativeResponseControl,
|
|
163
164
|
beforeContinuation?: () => Promise<void>,
|
|
164
165
|
onPhysicalDispatch?: () => void,
|
|
166
|
+
/** Opt-in pre-output refusal hold (#6969); see codexWsExchange. */
|
|
167
|
+
preOutputRefusalHold?: CodexWsPreOutputRefusalHold,
|
|
165
168
|
): Promise<Response> {
|
|
166
169
|
const prepared = prepareCodexWsRequest(url, init);
|
|
167
170
|
if (!prepared) return sseFallback(url, prepareCodexHttpInit(url, init));
|
|
@@ -221,6 +224,10 @@ export async function codexWsUpstreamFetch(
|
|
|
221
224
|
// reused for a different destination or with stale plugin headers.
|
|
222
225
|
const dial = planCodexWsDial(wsUrl, headers, proxy);
|
|
223
226
|
if (!dial) return sseFallback(url, init);
|
|
227
|
+
// The final dial headers, after plugin rewrites, are what the WebSocket upstream receives; the
|
|
228
|
+
// per-turn headers planning drops from the upgrade still reach it inside the frame metadata.
|
|
229
|
+
outboundCredentials().remember(headers);
|
|
230
|
+
outboundCredentials().remember(dial.headers);
|
|
224
231
|
const identity = control ? null : codexWsReuseIdentity(url, dial.headers, frameText, dial.proxy, dial.url);
|
|
225
232
|
const reuseAcrossTurns = identity !== null && codexWsReuseAcrossTurnsEnabled();
|
|
226
233
|
const pooled = identity
|
|
@@ -243,6 +250,7 @@ export async function codexWsUpstreamFetch(
|
|
|
243
250
|
session, url, init, prepared, sseFallback, onQuota, beforeDispatch,
|
|
244
251
|
nativeControl: control,
|
|
245
252
|
beforeContinuation, onPhysicalDispatch,
|
|
253
|
+
preOutputRefusalHold,
|
|
246
254
|
bunVersion: typeof runtime === "string" ? runtime : runtime.version,
|
|
247
255
|
});
|
|
248
256
|
}
|