@bitkyc08/opencodex 2.84.0 → 2.87.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-ZfYbLHkB.js +58 -0
- package/gui/dist/assets/App-wT3mXqFz.css +1 -0
- package/gui/dist/assets/{Tray-C9TGtwad.js → Tray-DCd-A1az.js} +1 -1
- package/gui/dist/assets/index-CfYDMj34.js +94 -0
- package/gui/dist/assets/index-DPeB9HpV.css +1 -0
- package/gui/dist/assets/quota-summary-ChvmKGOk.js +1 -0
- package/gui/dist/index.html +2 -2
- package/package.json +1 -2
- package/src/adapters/anthropic/beta-allowlist.ts +91 -11
- package/src/adapters/anthropic/beta-body-pairs.ts +93 -0
- package/src/adapters/anthropic/client-identity.ts +1 -1
- package/src/adapters/anthropic/passthrough.ts +16 -3
- package/src/adapters/anthropic-progress.ts +84 -0
- package/src/adapters/anthropic.ts +108 -21
- package/src/adapters/azure.ts +19 -1
- package/src/adapters/base.ts +2 -0
- package/src/adapters/claude-cli/adapter.ts +111 -23
- package/src/adapters/claude-cli/profiles.ts +2 -1
- package/src/adapters/claude-cli/stable-replay.ts +108 -0
- package/src/adapters/claude-cli/usage-admission.ts +230 -0
- package/src/adapters/client-fingerprint.ts +24 -0
- package/src/adapters/coding-agent/protocol.ts +1 -1
- package/src/adapters/coding-agent/turn.ts +40 -11
- 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/dsh-account.ts +97 -0
- package/src/adapters/google-antigravity-wire.ts +22 -1
- package/src/adapters/google.ts +37 -10
- package/src/adapters/openai-chat/messages.ts +4 -3
- package/src/adapters/openai-chat/passthrough.ts +8 -2
- package/src/adapters/openai-chat/reasoning-replay.ts +52 -0
- package/src/adapters/openai-chat/response-events.ts +59 -1
- package/src/adapters/openai-chat.ts +8 -5
- package/src/adapters/openai-responses/compaction-search-history.ts +14 -7
- package/src/adapters/openai-responses/passthrough.ts +57 -125
- package/src/adapters/openai-responses/request-strips.ts +25 -3
- package/src/adapters/openai-responses/transport.ts +81 -0
- package/src/adapters/qoder/adapter.ts +44 -3
- package/src/adapters/registry.ts +5 -0
- package/src/adapters/zed.ts +26 -1
- package/src/advisor/activity.ts +55 -0
- package/src/advisor/budget.ts +62 -0
- package/src/advisor/consult.ts +223 -0
- package/src/advisor/context.ts +238 -0
- package/src/advisor/disclosure.ts +20 -0
- package/src/advisor/evidence-tool.ts +21 -0
- package/src/advisor/evidence.ts +117 -0
- package/src/advisor/runtime.ts +353 -0
- package/src/advisor/settings.ts +144 -0
- package/src/advisor/state.ts +369 -0
- package/src/advisor/synthetic-tool.ts +33 -0
- package/src/bridge/errors.ts +24 -1
- package/src/bridge/internal.ts +3 -1
- package/src/bridge/response-json.ts +18 -3
- package/src/bridge/sse.ts +58 -8
- package/src/bridge/terminal-refusal.ts +59 -0
- package/src/chat/inbound.ts +30 -6
- package/src/chat/outbound.ts +5 -0
- package/src/claude/desktop-3p-library.ts +72 -7
- package/src/claude/desktop-3p.ts +75 -19
- package/src/claude/desktop-first-party.ts +1 -1
- package/src/claude/desktop-picker-platform.ts +4 -0
- package/src/claude/desktop-picker-profile.ts +11 -4
- package/src/claude/desktop-picker.ts +15 -8
- package/src/claude/desktop-profile.ts +10 -1
- package/src/claude/desktop-remote-store-io.ts +5 -3
- package/src/claude/desktop-system-proxy.ts +50 -4
- package/src/claude/inbound-content-options.ts +11 -0
- package/src/claude/inbound.ts +154 -50
- package/src/claude/intercept/local-ca.ts +8 -1
- package/src/claude/intercept/picker-ca-startup.ts +2 -1
- package/src/claude/intercept/picker-runtime.ts +6 -5
- package/src/claude/intercept/picker-trust-windows.ts +338 -0
- package/src/claude/intercept/picker-trust.ts +19 -0
- package/src/claude/intercept/port-policy.ts +30 -0
- package/src/claude/intercept/runtime.ts +97 -94
- package/src/claude/model-default-settings.ts +154 -0
- package/src/claude/model-info.ts +7 -7
- package/src/claude/outbound.ts +51 -19
- package/src/cli/account-api.ts +1 -0
- package/src/cli/account-policy-dto.ts +6 -0
- package/src/cli/account-policy.ts +34 -5
- package/src/cli/account-preservation.ts +44 -0
- package/src/cli/account.ts +8 -2
- package/src/cli/advisor.ts +165 -0
- package/src/cli/auxiliary-listener-preflight.ts +37 -0
- package/src/cli/capabilities-accounts.ts +20 -1
- package/src/cli/capabilities-agents-routing.ts +23 -2
- package/src/cli/capabilities-base.ts +39 -11
- package/src/cli/capabilities-observe-system.ts +63 -3
- package/src/cli/capabilities-provider-models.ts +11 -0
- package/src/cli/chatgpt-command.ts +35 -14
- package/src/cli/claude.ts +46 -6
- package/src/cli/codex-cli-update.ts +151 -41
- package/src/cli/codex-provider.ts +44 -0
- package/src/cli/combo-input.ts +10 -1
- package/src/cli/combo-result.ts +1 -1
- package/src/cli/combo.ts +2 -1
- package/src/cli/cross-home-owner.ts +17 -14
- package/src/cli/dispatch.ts +30 -5
- package/src/cli/doctor.ts +15 -1
- package/src/cli/ensure-desktop-cold-start.ts +382 -0
- package/src/cli/export-command.ts +1 -1
- package/src/cli/help.ts +4 -2
- package/src/cli/index.ts +14 -5
- package/src/cli/integration-plan-dto.ts +3 -0
- package/src/cli/log-history.ts +172 -0
- package/src/cli/models-runtime-subcommands.ts +1 -0
- package/src/cli/models-runtime.ts +72 -8
- package/src/cli/observe-stream.ts +2 -0
- package/src/cli/observe.ts +16 -4
- package/src/cli/provider.ts +27 -5
- package/src/cli/registry.ts +41 -9
- package/src/cli/restart-scope.ts +3 -1
- package/src/cli/runtime-api.ts +5 -3
- package/src/cli/system-command.ts +40 -8
- package/src/cli/uninstall-client-state.ts +32 -0
- package/src/cli/usage-compare.ts +309 -0
- package/src/cli/usage-model-search.ts +4 -4
- package/src/cli/usage-report.ts +2 -2
- package/src/cli/v2.ts +1 -1
- package/src/client/machine-listener.ts +1 -3
- package/src/client/runtime.ts +1 -0
- package/src/clients/client-path.ts +51 -0
- package/src/clients/config-export/contracts.ts +3 -1
- 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 +35 -48
- package/src/clients/gentle-shell-path.ts +87 -0
- package/src/codex/account-auto-switch.ts +2 -0
- package/src/codex/account-identity.ts +29 -0
- package/src/codex/account-label.ts +1 -1
- package/src/codex/account-lifecycle.ts +21 -5
- package/src/codex/account-preservation.ts +28 -0
- package/src/codex/account-use-remaining.ts +23 -0
- package/src/codex/app-server-processes.ts +97 -7
- package/src/codex/app-server-restart-service.ts +55 -4
- package/src/codex/auth-api/account-list.ts +4 -0
- package/src/codex/auth-api/account-pause-group.ts +6 -50
- package/src/codex/auth-api/account-preservation.ts +57 -0
- package/src/codex/auth-api/pool-mode-gate.ts +5 -2
- package/src/codex/auth-api/routes.ts +8 -0
- package/src/codex/auth-api/use-remaining-route.ts +36 -0
- package/src/codex/auth-api.ts +0 -1
- package/src/codex/auth-context.ts +81 -7
- package/src/codex/autostart-health.ts +4 -0
- package/src/codex/catalog/auto-review.ts +20 -3
- package/src/codex/catalog/build-entries.ts +19 -2
- package/src/codex/catalog/combo-member.ts +14 -5
- package/src/codex/catalog/configured-tiers.ts +50 -0
- package/src/codex/catalog/effort.ts +4 -1
- package/src/codex/catalog/gather-capture.ts +2 -0
- package/src/codex/catalog/metadata.ts +11 -9
- package/src/codex/catalog/model-hints.ts +42 -19
- package/src/codex/catalog/model-visibility.ts +3 -1
- package/src/codex/catalog/native-ultrafast.ts +106 -0
- package/src/codex/catalog/parsing.ts +35 -86
- package/src/codex/catalog/provider-models.ts +40 -34
- package/src/codex/catalog/retained-sync.ts +2 -0
- package/src/codex/catalog/routed-gather.ts +33 -16
- package/src/codex/catalog/vendor-context.ts +82 -0
- package/src/codex/cli-install-provenance.ts +105 -17
- package/src/codex/cli-update-plan.ts +545 -0
- package/src/codex/config-write-lock.ts +63 -4
- package/src/codex/convergence.ts +2 -0
- package/src/codex/desktop-app/picker-freshness.ts +96 -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 +25 -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 +73 -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 +182 -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 +165 -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/features.ts +85 -45
- package/src/codex/history-provider.ts +1 -1
- package/src/codex/inject/config-write-section.ts +4 -1
- package/src/codex/inject/history-failure.ts +27 -0
- package/src/codex/inject/remove.ts +18 -6
- package/src/codex/inject/restore.ts +24 -2
- package/src/codex/inject/routing-classify.ts +4 -3
- package/src/codex/inject.ts +18 -27
- package/src/codex/journal.ts +16 -1
- package/src/codex/main-account-hard-lock.ts +30 -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 +1 -1
- package/src/codex/ocx-compaction-history.ts +12 -10
- package/src/codex/paths.ts +86 -1
- package/src/codex/provider-routing.ts +174 -0
- package/src/codex/refresh.ts +2 -2
- package/src/codex/routing/health-store.ts +2 -2
- package/src/codex/routing/pool-recovery.ts +79 -0
- package/src/codex/routing/selection.ts +4 -3
- package/src/codex/routing/thread-affinity.ts +1 -1
- package/src/codex/shim-diagnostics.ts +6 -0
- package/src/codex/shim-overlay.ts +7 -2
- package/src/codex/signed-in-identity.ts +43 -0
- package/src/codex/subagent-model-fallback.ts +1 -1
- package/src/codex/sync.ts +6 -1
- package/src/codex/user-identity.ts +16 -5
- package/src/codex/windows-local-appdata.ts +84 -0
- package/src/combos/failover.ts +6 -1
- package/src/combos/index.ts +1 -0
- package/src/combos/resolve.ts +46 -10
- package/src/combos/types.ts +15 -0
- package/src/config/diagnostics.ts +35 -7
- package/src/config/live-reconcile.ts +51 -2
- package/src/config/load-degrade.ts +23 -1
- package/src/config/opencode-wire-upgrade.ts +54 -0
- package/src/config/opencode-zen-upgrade.ts +52 -0
- package/src/config/owner-registry.ts +22 -17
- package/src/config/paths.ts +10 -3
- package/src/config/provider-validation.ts +45 -2
- package/src/config/schema/config-schema.ts +39 -0
- package/src/config/schema/desktop-compatibility.ts +9 -0
- package/src/config/schema/leaf-validators.ts +39 -9
- package/src/config/schema/main-account-hard-lock-fallback.ts +26 -0
- package/src/config/schema/native-reasoning-retention.ts +26 -0
- package/src/config.ts +2 -0
- package/src/generated/compatibility-version.json +962 -350
- package/src/generated/model-metadata.ts +1 -1
- package/src/grok/sync.ts +1 -1
- package/src/images/loop.ts +46 -8
- 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 +2 -0
- package/src/integrations/registry.ts +15 -0
- package/src/integrations/store.ts +3 -0
- package/src/integrations/writer.ts +10 -1
- package/src/lib/advisor-activation.ts +16 -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/codex-restart-contract.ts +15 -0
- package/src/lib/desktop-proxy-route.ts +30 -0
- package/src/lib/desktop-upstream-tunnel.ts +72 -0
- package/src/lib/empty-tool-catalog.ts +62 -0
- package/src/lib/errors.ts +26 -3
- package/src/lib/gcp-adc.ts +96 -9
- package/src/lib/gcp-credential-input.ts +12 -0
- package/src/lib/lab-live-route-production.ts +5 -0
- package/src/lib/local-internal-call-capability.ts +55 -0
- package/src/lib/opaque-proxy-tunnel.ts +217 -0
- package/src/lib/package-tree-integrity.ts +2 -2
- package/src/lib/redact.ts +2 -2
- 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/oauth/account-quota-rank.ts +69 -2
- package/src/oauth/anthropic-account-refusal.ts +20 -2
- package/src/oauth/anthropic-oauth-definitions.ts +3 -1
- package/src/oauth/anthropic-routing.ts +1 -21
- package/src/oauth/anthropic.ts +38 -7
- package/src/oauth/callback-server.ts +27 -1
- package/src/oauth/chatgpt-device.ts +1 -1
- package/src/oauth/chatgpt-shared.ts +83 -0
- package/src/oauth/chatgpt.ts +8 -72
- package/src/oauth/dsh.ts +203 -0
- package/src/oauth/generic-account-failover.ts +62 -12
- package/src/oauth/generic-session-affinity.ts +125 -0
- package/src/oauth/index.ts +188 -111
- package/src/oauth/kiro-device-login.ts +2 -1
- package/src/oauth/kiro.ts +7 -1
- package/src/oauth/local-token-detect.ts +21 -1
- package/src/oauth/login-cli-errors.ts +44 -0
- package/src/oauth/login-cli.ts +69 -7
- package/src/oauth/login-flow-state.ts +371 -23
- package/src/oauth/meta-muse-errors.ts +41 -0
- package/src/oauth/meta-muse-keychain.ts +52 -0
- package/src/oauth/meta-muse.ts +51 -84
- package/src/oauth/model-discovery-auth.ts +3 -0
- package/src/oauth/pool-settings-capability.ts +4 -2
- package/src/oauth/refresh-flight-retirement.ts +48 -0
- package/src/oauth/session-affinity-key.ts +23 -0
- package/src/oauth/store-anthropic-instance.ts +9 -2
- package/src/oauth/store.ts +12 -5
- 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/encoders/adapter-events.ts +5 -4
- package/src/protocols/encoders/messages.ts +9 -6
- 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/provider-summary.ts +2 -1
- package/src/protocols/settings.ts +8 -0
- package/src/providers/antigravity-account-roster.ts +102 -0
- package/src/providers/antigravity-models.ts +85 -7
- package/src/providers/api-key-cooldowns.ts +49 -0
- package/src/providers/api-key-resolve.ts +11 -6
- package/src/providers/api-key-response-ownership.ts +57 -0
- package/src/providers/api-key-selection.ts +31 -6
- package/src/providers/api-keys.ts +214 -3
- package/src/providers/claude-cli-usage.ts +96 -0
- package/src/providers/dsh-account-destination.ts +31 -0
- package/src/providers/dsh-account-models.ts +41 -0
- package/src/providers/gcp-credential-cleanup.ts +59 -0
- package/src/providers/key-failover.ts +54 -50
- package/src/providers/key-store.ts +7 -2
- package/src/providers/model-rename-fields.ts +5 -0
- package/src/providers/model-rename-migration.ts +7 -5
- package/src/providers/openai-tiers-destination.ts +11 -2
- package/src/providers/openai-tiers.ts +5 -12
- package/src/providers/opencode-go-transport.ts +3 -1
- package/src/providers/quota/account-cache.ts +6 -5
- package/src/providers/quota/account-row.ts +34 -0
- package/src/providers/quota/antigravity.ts +28 -7
- package/src/providers/quota/dsh-account.ts +70 -0
- package/src/providers/quota/vendor-probes-oauth.ts +1 -93
- package/src/providers/quota-types.ts +2 -0
- package/src/providers/quota.ts +14 -20
- package/src/providers/registry/entries-core.ts +44 -14
- package/src/providers/registry/entries-extended.ts +19 -0
- package/src/providers/registry/types.ts +1 -1
- package/src/providers/request-pacing.ts +77 -19
- package/src/providers/routed-config.ts +339 -0
- package/src/providers/service-tier.ts +2 -1
- package/src/quota/window-mapping.ts +5 -2
- package/src/responses/anthropic-progress.ts +34 -0
- package/src/responses/assistant-replay.ts +35 -0
- package/src/responses/compaction-history-framing.ts +65 -0
- package/src/responses/compaction.ts +6 -1
- package/src/responses/incomplete-reason.ts +31 -0
- package/src/responses/parser.ts +78 -22
- package/src/responses/reasoning-envelope.ts +4 -0
- package/src/responses/reasoning-replay-cache.ts +24 -2
- package/src/responses/reasoning-retention.ts +173 -0
- package/src/responses/retained-compaction.ts +46 -0
- package/src/responses/schema.ts +23 -0
- package/src/responses/state.ts +32 -29
- package/src/router.ts +8 -321
- package/src/routing/health.ts +37 -22
- package/src/routing/history/build-file.ts +92 -0
- package/src/routing/history/indexer.ts +254 -221
- package/src/routing/history/ingest.ts +242 -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/routing/profile.ts +1 -1
- package/src/server/admission-model-scope.ts +21 -1
- package/src/server/antigravity-direct-eligibility.ts +208 -0
- package/src/server/auth-cors.ts +15 -0
- package/src/server/chat-completions.ts +82 -10
- package/src/server/chat-native-reasoning.ts +123 -0
- package/src/server/chat-native-sse.ts +110 -7
- package/src/server/chat-native.ts +76 -17
- package/src/server/claude-messages.ts +164 -433
- package/src/server/effort-policy.ts +1 -1
- package/src/server/images.ts +15 -2
- package/src/server/index/desktop-compatibility-startup.ts +56 -0
- package/src/server/index/model-alias-id.ts +36 -0
- package/src/server/index/optional-listeners.ts +2 -0
- package/src/server/index/serve-options.ts +11 -3
- package/src/server/index/startup-warnings.ts +11 -0
- package/src/server/index/websocket-handler.ts +9 -0
- package/src/server/index.ts +14 -11
- package/src/server/inference/advisor-diagnostics.ts +31 -0
- package/src/server/inference/client-encoder-delivery.ts +6 -5
- package/src/server/login-shell-path.ts +146 -0
- package/src/server/management/advisor-routes.ts +202 -0
- package/src/server/management/agent-settings-routes.ts +22 -4
- package/src/server/management/claude-desktop-picker-routes.ts +1 -1
- package/src/server/management/combo-routes.ts +176 -50
- package/src/server/management/companion-routes.ts +4 -0
- package/src/server/management/config-routes.ts +77 -6
- package/src/server/management/context.ts +4 -0
- package/src/server/management/cursor-integration-routes.ts +1 -1
- 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/model-routes.ts +13 -8
- package/src/server/management/model-rows.ts +14 -0
- package/src/server/management/native-integration-routes.ts +2 -7
- package/src/server/management/native-reasoning-retention-routes.ts +41 -0
- package/src/server/management/oauth-account-routes.ts +87 -14
- package/src/server/management/protocol-settings-patch.ts +1 -0
- package/src/server/management/provider-gcp-credential-routes.ts +29 -0
- package/src/server/management/provider-key-pacing-routes.ts +31 -0
- package/src/server/management/provider-overwrite-carry.ts +7 -0
- package/src/server/management/provider-routes.ts +25 -30
- package/src/server/management/route-registry.ts +21 -1
- 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-tool-guard.ts +185 -0
- package/src/server/messages-native.ts +106 -22
- package/src/server/messages-shared.ts +431 -0
- package/src/server/relay.ts +1 -1
- package/src/server/request-log-terminal-status.ts +1 -1
- package/src/server/request-log.ts +13 -1
- package/src/server/responses/account-change-state.ts +8 -2
- package/src/server/responses/adapter-continuation.ts +48 -19
- package/src/server/responses/adapter-delivery.ts +43 -8
- package/src/server/responses/adapter-dispatch.ts +48 -4
- package/src/server/responses/advisor-plan-slot.ts +27 -0
- package/src/server/responses/advisor-retention.ts +117 -0
- package/src/server/responses/advisor-slot.ts +368 -0
- package/src/server/responses/code-mode-collab-surface.ts +116 -8
- package/src/server/responses/codex-ws-wire.ts +15 -8
- package/src/server/responses/compact.ts +303 -125
- package/src/server/responses/compaction-recovery.ts +18 -10
- package/src/server/responses/compaction-retention.ts +61 -0
- package/src/server/responses/context-overflow.ts +11 -6
- package/src/server/responses/core-auth.ts +6 -0
- package/src/server/responses/core-codex-account.ts +11 -1
- package/src/server/responses/core-combo-failure.ts +12 -1
- package/src/server/responses/core-combo.ts +141 -36
- package/src/server/responses/core-errors.ts +3 -3
- package/src/server/responses/core-lifetime.ts +7 -2
- package/src/server/responses/core-normalize.ts +11 -0
- package/src/server/responses/core-opaque-recovery.ts +35 -0
- package/src/server/responses/core-options.ts +51 -0
- package/src/server/responses/core-pipeline.ts +141 -0
- package/src/server/responses/core-replay.ts +25 -6
- package/src/server/responses/core.ts +3 -116
- package/src/server/responses/fetch-helpers.ts +73 -19
- package/src/server/responses/main-hard-lock-routing.ts +109 -0
- package/src/server/responses/native-response-control.ts +4 -2
- package/src/server/responses/passthrough-delivery.ts +19 -11
- package/src/server/responses/passthrough-dispatch.ts +29 -16
- package/src/server/responses/request-prepare.ts +144 -17
- package/src/server/responses/request-sidecar-auth.ts +2 -2
- package/src/server/responses/request-transport.ts +120 -27
- package/src/server/responses/response-effects.ts +1 -0
- package/src/server/responses/run-turn-execution.ts +166 -38
- package/src/server/responses/sidecar-execution.ts +98 -5
- package/src/server/responses/terminal-guard.ts +4 -17
- package/src/server/responses-undeclared-tool-guard.ts +30 -9
- package/src/server/startup-action-control.ts +16 -2
- package/src/server/startup-health-cache.ts +1 -0
- package/src/service/auth-preflight.ts +71 -0
- package/src/service/cli.ts +4 -2
- package/src/service/definition.ts +97 -0
- package/src/service/desktop-startup.ts +14 -0
- package/src/service/guards.ts +1 -61
- package/src/service/health-sentinel-linux.ts +112 -0
- package/src/service/health-sentinel-main.ts +99 -61
- package/src/service/health-sentinel-probe.ts +24 -7
- package/src/service/health.ts +5 -89
- package/src/service/launchd.ts +27 -7
- package/src/service/logging.ts +135 -0
- package/src/service/repair.ts +2 -1
- package/src/service/state.ts +7 -8
- package/src/service/systemd.ts +1 -1
- package/src/service/windows-guarded-manager.ts +2 -1
- package/src/service/windows-ops.ts +1 -1
- package/src/service/windows-scheduler.ts +2 -7
- package/src/service/windows-taskxml.ts +2 -2
- package/src/service/windows-wscript.ts +14 -0
- package/src/service.ts +4 -2
- package/src/sidecar/auth.ts +2 -0
- package/src/tray/windows-tray.ps1 +43 -24
- package/src/types/config.ts +124 -6
- package/src/types/opencode-go.ts +61 -0
- package/src/types/opencode-zen.ts +29 -0
- package/src/types/provider.ts +39 -6
- package/src/types/request.ts +38 -2
- package/src/types/tools.ts +2 -0
- package/src/types/wire.ts +29 -37
- package/src/types.ts +1 -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/npm-invocation.mjs +12 -2
- 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/cost.ts +48 -12
- package/src/usage/expected-prices.ts +15 -0
- package/src/usage/ledger-retention.ts +2 -1
- package/src/usage/ledger-scanner.ts +6 -5
- package/src/usage/log.ts +17 -8
- package/src/usage/summary.ts +22 -9
- package/src/usage/user-cost-overlays.ts +14 -19
- package/src/usage/user-cost-validation.ts +38 -0
- package/src/vision/eligibility.ts +13 -0
- package/src/web-search/gemini-executor.ts +2 -2
- package/src/web-search/loop.ts +55 -5
- package/src/web-search/progress-stream.ts +4 -1
- package/src/web-search/run-turn-loop.ts +9 -1
- package/assets/sponsors/packycode.png +0 -0
- package/gui/dist/assets/App-CSJvmjNs.css +0 -1
- package/gui/dist/assets/App-CZtqiGcw.js +0 -56
- package/gui/dist/assets/index-BB6uK6tU.css +0 -1
- package/gui/dist/assets/index-CTm4l-HQ.js +0 -94
- package/gui/dist/assets/quota-summary-BbEeRfPY.js +0 -1
|
@@ -23,6 +23,7 @@ import { getRoutingProfile, POLICY_NAMESPACE, resolvePolicyProfileId } from "../
|
|
|
23
23
|
import { resolveWireProtocolOverride } from "../server/adapter-resolve";
|
|
24
24
|
import { nativeChatDeclineReason } from "../server/chat-native-eligibility";
|
|
25
25
|
import { nativeMessagesDeclineReason } from "../server/messages-native-eligibility";
|
|
26
|
+
import { antigravityDirectDeclineReason, antigravityMessagesDeclineReason } from "../server/antigravity-direct-eligibility";
|
|
26
27
|
import { messagesSelectorTargetsSecondaryInstance, messagesSecondaryInstanceUnavailable } from "../server/messages-native-selector";
|
|
27
28
|
import { parseSyntheticRowId } from "../server/fast-row";
|
|
28
29
|
import type { OcxConfig } from "../types";
|
|
@@ -125,6 +126,7 @@ function candidateFor(
|
|
|
125
126
|
const adapter = provider.adapter ?? "openai-responses";
|
|
126
127
|
let declineReasons: ProtocolReasonCode[] = [];
|
|
127
128
|
let nativeEligible = false;
|
|
129
|
+
let directEligible = false;
|
|
128
130
|
// With `nativeChatCombos` on, the combo loop judges each Chat candidate as the concrete route it
|
|
129
131
|
// is (PF-07), so the preview must too; a policy still resolves one candidate on the bridge.
|
|
130
132
|
const comboChildNative = inbound === "chat" && routeKind === "combo"
|
|
@@ -136,25 +138,46 @@ function candidateFor(
|
|
|
136
138
|
...(routeKind === "direct" || comboChildNative ? {} : { routeKind }),
|
|
137
139
|
};
|
|
138
140
|
if (inbound === "chat") {
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
if (reason) declineReasons = [reason];
|
|
142
|
-
} else if (inbound === "messages" && resolveProtocolSettings(config, settled.providerName).rollout.managedMessagesNative) {
|
|
143
|
-
// The runtime rule itself. With the switch off nothing is judged, so the default preview
|
|
144
|
-
// is exactly what it was before the managed native lane existed.
|
|
145
|
-
// Pinned effort is judged from config and the route; blocked-skill elision and the web-search
|
|
146
|
-
// sidecar depend on body content no feature describes, so a preview cannot predict them.
|
|
147
|
-
// Anthropic OAuth (PF-10) is judged from config alone: the rollout switch, the provider, its
|
|
148
|
-
// host and `anthropicAccountPool.enabled`. The stored-account quorum a sender supplies is
|
|
149
|
-
// never read here, so a preview selects, resolves and refreshes no account.
|
|
150
|
-
const reason = nativeMessagesDeclineReason(settled, messagesBodyForFeatures(features), config, {
|
|
151
|
-
...rows,
|
|
152
|
-
claudeCode: config.claudeCode,
|
|
153
|
-
});
|
|
141
|
+
const body = chatBodyForFeatures(features);
|
|
142
|
+
const reason = rows.effortRow ? "effort-row" : nativeChatDeclineReason(settled, body, config);
|
|
154
143
|
nativeEligible = reason === undefined;
|
|
155
144
|
if (reason) declineReasons = [reason];
|
|
145
|
+
// The Antigravity direct lane, judged by the ingress's own rule, for a standalone route only:
|
|
146
|
+
// a combo child is dispatched from the full Responses body even when `nativeChatCombos`
|
|
147
|
+
// judges it as a concrete route above. A hosted web-search tool and the other body-content
|
|
148
|
+
// declines are not described by any Chat feature, so a preview cannot predict them.
|
|
149
|
+
directEligible = !nativeEligible && routeKind === "direct"
|
|
150
|
+
&& antigravityDirectDeclineReason(settled, { ...body, model: rows.routeSelector }, config) === undefined;
|
|
151
|
+
} else if (inbound === "messages") {
|
|
152
|
+
const body = messagesBodyForFeatures(features);
|
|
153
|
+
if (resolveProtocolSettings(config, settled.providerName).rollout.managedMessagesNative) {
|
|
154
|
+
// The runtime rule itself. With the switch off nothing is judged, so the default preview
|
|
155
|
+
// is exactly what it was before the managed native lane existed.
|
|
156
|
+
// Pinned effort is judged from config and the route; blocked-skill elision and the web-search
|
|
157
|
+
// sidecar depend on body content no feature describes, so a preview cannot predict them.
|
|
158
|
+
// Anthropic OAuth (PF-10) is judged from config alone: the rollout switch, the provider, its
|
|
159
|
+
// host and `anthropicAccountPool.enabled`. The stored-account quorum a sender supplies is
|
|
160
|
+
// never read here, so a preview selects, resolves and refreshes no account.
|
|
161
|
+
const reason = nativeMessagesDeclineReason(settled, body, config, {
|
|
162
|
+
...rows,
|
|
163
|
+
claudeCode: config.claudeCode,
|
|
164
|
+
});
|
|
165
|
+
nativeEligible = reason === undefined;
|
|
166
|
+
if (reason) declineReasons = [reason];
|
|
167
|
+
}
|
|
168
|
+
// The Antigravity direct lane for a standalone route, as for Chat. The ingress plans it on the
|
|
169
|
+
// resolved selector, which is `rows.routeSelector`.
|
|
170
|
+
directEligible = !nativeEligible && routeKind === "direct"
|
|
171
|
+
&& antigravityMessagesDeclineReason(settled, body, rows.routeSelector, config) === undefined;
|
|
156
172
|
}
|
|
157
|
-
return {
|
|
173
|
+
return {
|
|
174
|
+
provider: route.providerName,
|
|
175
|
+
model: route.modelId,
|
|
176
|
+
adapter,
|
|
177
|
+
nativeEligible,
|
|
178
|
+
...(directEligible ? { directEligible } : {}),
|
|
179
|
+
declineReasons,
|
|
180
|
+
};
|
|
158
181
|
}
|
|
159
182
|
|
|
160
183
|
/** Whether the Messages ingress could forward this selector with a caller's own credential. */
|
package/src/protocols/plan.ts
CHANGED
|
@@ -28,6 +28,12 @@ export interface ProtocolPlanCandidateInput {
|
|
|
28
28
|
adapter: string;
|
|
29
29
|
/** True when the ingress would send its own wire to this candidate. */
|
|
30
30
|
nativeEligible: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* True when the ingress would decode its own wire straight into the IR for this candidate (the
|
|
33
|
+
* Antigravity direct lane). Only meaningful toward an `other` upstream; `path.ts` ignores it
|
|
34
|
+
* anywhere else.
|
|
35
|
+
*/
|
|
36
|
+
directEligible?: boolean;
|
|
31
37
|
/** Why the ingress declined its native lane, when it did. */
|
|
32
38
|
declineReasons: readonly ProtocolReasonCode[];
|
|
33
39
|
}
|
|
@@ -80,7 +86,9 @@ function planCandidate(
|
|
|
80
86
|
const upstream = upstreamWireForAdapter(candidate.adapter);
|
|
81
87
|
// A native lane only exists toward the ingress's own wire; an inconsistent snapshot must
|
|
82
88
|
// not produce a path that claims the source body reached a different wire untouched.
|
|
83
|
-
const lane: ProtocolLane = candidate.nativeEligible && upstream === input.inbound
|
|
89
|
+
const lane: ProtocolLane = candidate.nativeEligible && upstream === input.inbound
|
|
90
|
+
? "native"
|
|
91
|
+
: candidate.directEligible === true ? "direct" : "bridge";
|
|
84
92
|
const requestPath = requestPathForLane(input.inbound, lane, upstream);
|
|
85
93
|
const responsePath = responsePathForLane(input.inbound, lane, upstream);
|
|
86
94
|
const mode = deliveryModeForLane(input.inbound, lane, upstream);
|
|
@@ -55,12 +55,13 @@ function usableModelId(model: unknown): model is string {
|
|
|
55
55
|
function candidateModelIds(name: string, provider: Readonly<OcxProviderConfig>): string[] {
|
|
56
56
|
const registry = PROVIDER_REGISTRY.find(entry => entry.id === name);
|
|
57
57
|
const ids = new Set<string>();
|
|
58
|
+
/** Collect valid candidate IDs without exceeding the protocol-summary scan limit. */
|
|
58
59
|
const add = (model: unknown) => {
|
|
59
60
|
if (ids.size < MODEL_SCAN_LIMIT && usableModelId(model)) ids.add(model);
|
|
60
61
|
};
|
|
61
62
|
for (const model of Object.keys(provider.modelAdapters ?? {})) add(model);
|
|
62
63
|
for (const model of Object.keys(registry?.modelWireDefaults ?? {})) add(model);
|
|
63
|
-
for (const model of Object.keys(captureWireAdapterHardPins(name))) add(model);
|
|
64
|
+
for (const model of Object.keys(captureWireAdapterHardPins(name, provider))) add(model);
|
|
64
65
|
add(provider.defaultModel);
|
|
65
66
|
for (const model of provider.models ?? []) add(model);
|
|
66
67
|
return [...ids].sort((left, right) => left.localeCompare(right));
|
|
@@ -64,6 +64,7 @@ export interface ProtocolRolloutSettings {
|
|
|
64
64
|
managedMessagesNative: boolean;
|
|
65
65
|
managedMessagesNativeOAuth: boolean;
|
|
66
66
|
directEncoders: boolean;
|
|
67
|
+
antigravityDirect: boolean;
|
|
67
68
|
shadowPlan: boolean;
|
|
68
69
|
}
|
|
69
70
|
|
|
@@ -101,6 +102,12 @@ export function resolveProtocolSettings(config: ProtocolConfig, providerName?: s
|
|
|
101
102
|
// OAuth extension is meaningless without the key-auth path it extends.
|
|
102
103
|
managedMessagesNativeOAuth: managedMessagesNative && nativeOn("managedMessagesNativeOAuth"),
|
|
103
104
|
directEncoders: on("directEncoders"),
|
|
105
|
+
// Default on: an absent switch takes the Antigravity direct lane, which falls back to the
|
|
106
|
+
// bridge per request whenever its eligibility rule declines. Only an explicit `false` turns
|
|
107
|
+
// it off; a non-boolean value or a malformed container fails closed like every other switch.
|
|
108
|
+
antigravityDirect: validContainers && (Object.hasOwn(rollout, "antigravityDirect")
|
|
109
|
+
? rollout.antigravityDirect === true
|
|
110
|
+
: true),
|
|
104
111
|
shadowPlan: on("shadowPlan"),
|
|
105
112
|
}),
|
|
106
113
|
});
|
|
@@ -145,6 +152,7 @@ export function protocolPolicyRevision(config: ProtocolConfig & Pick<OcxConfig,
|
|
|
145
152
|
secondarySettings.rollout.managedMessagesNative,
|
|
146
153
|
secondarySettings.rollout.managedMessagesNativeOAuth,
|
|
147
154
|
settings.rollout.directEncoders,
|
|
155
|
+
settings.rollout.antigravityDirect,
|
|
148
156
|
settings.rollout.shadowPlan,
|
|
149
157
|
],
|
|
150
158
|
});
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { getConfigDir } from "../config/paths";
|
|
2
|
+
import { ANTIGRAVITY_MODEL_ALIASES, canonicalAntigravityUsageModel, type AntigravityAvailableModel } from "./antigravity-models";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Per-account Antigravity model rosters, taken from each account's own live
|
|
6
|
+
* `:fetchAvailableModels` discovery.
|
|
7
|
+
*
|
|
8
|
+
* Google gates models per account, not per client. Measured 2026-10-14: one account lists
|
|
9
|
+
* `claude-sonnet-4-6` / `claude-opus-4-6-thinking` and answers 404 "Requested entity was not
|
|
10
|
+
* found" for every `claude-{opus,sonnet}-5-5-*` id, while other accounts list only the 5.5
|
|
11
|
+
* families. Gemini 4 Argon is likewise Ultra / Enterprise only. Google documents Claude 5.5 as
|
|
12
|
+
* needing a directly billed, non-trial Pro or Ultra plan and rolls it out gradually, so the static
|
|
13
|
+
* lineup is a superset and only discovery says what one account can actually call.
|
|
14
|
+
*
|
|
15
|
+
* The catalog already exposes only the ACTIVE account's discovered rows (an authoritative live
|
|
16
|
+
* roster drops static rows it did not return). This store keeps every account's last roster so
|
|
17
|
+
* that account selection can prefer an account that lists the requested model, instead of
|
|
18
|
+
* landing a 5.5 request on a 4.6-only account. Absence of a roster is "unknown", never "no":
|
|
19
|
+
* an account that was never active during discovery stays fully usable.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/** A roster older than this is treated as unknown; entitlements change with plan and rollout. */
|
|
23
|
+
export const ANTIGRAVITY_ACCOUNT_ROSTER_TTL_MS = 6 * 60 * 60 * 1000;
|
|
24
|
+
|
|
25
|
+
interface AccountRoster {
|
|
26
|
+
readonly ids: ReadonlySet<string>;
|
|
27
|
+
readonly modelIds: readonly string[];
|
|
28
|
+
readonly observedAt: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const rosters = new Map<string, AccountRoster>();
|
|
32
|
+
|
|
33
|
+
function rosterKey(providerName: string, accountId: string): string {
|
|
34
|
+
return `${getConfigDir()}\0${providerName}\0${accountId}`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Record one account's discovered roster: picker ids, representative wire ids, and tier wire ids. */
|
|
38
|
+
export function recordAntigravityAccountRoster(
|
|
39
|
+
providerName: string,
|
|
40
|
+
accountId: string,
|
|
41
|
+
models: readonly AntigravityAvailableModel[],
|
|
42
|
+
now = Date.now(),
|
|
43
|
+
): void {
|
|
44
|
+
if (!providerName || !accountId) return;
|
|
45
|
+
const ids = new Set<string>();
|
|
46
|
+
for (const model of models) {
|
|
47
|
+
ids.add(model.id);
|
|
48
|
+
ids.add(model.wireModelId);
|
|
49
|
+
for (const wire of Object.values(model.effortWireModelIds ?? {})) if (wire) ids.add(wire);
|
|
50
|
+
}
|
|
51
|
+
rosters.set(rosterKey(providerName, accountId), {
|
|
52
|
+
ids,
|
|
53
|
+
modelIds: models.map(model => model.id),
|
|
54
|
+
observedAt: now,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function freshRoster(providerName: string, accountId: string, now: number): AccountRoster | undefined {
|
|
59
|
+
const roster = rosters.get(rosterKey(providerName, accountId));
|
|
60
|
+
if (!roster || now - roster.observedAt > ANTIGRAVITY_ACCOUNT_ROSTER_TTL_MS) return undefined;
|
|
61
|
+
return roster;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** The picker ids one account's last discovery returned, or `undefined` when unknown. */
|
|
65
|
+
export function antigravityAccountRosterModelIds(
|
|
66
|
+
providerName: string,
|
|
67
|
+
accountId: string,
|
|
68
|
+
now = Date.now(),
|
|
69
|
+
): readonly string[] | undefined {
|
|
70
|
+
return freshRoster(providerName, accountId, now)?.modelIds;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Whether one account's roster lists `modelId`: `true` / `false` from its last discovery,
|
|
75
|
+
* `undefined` when that account has no fresh roster. A picker id, one of its tier wire ids, or a
|
|
76
|
+
* compatibility alias of either all count as listed.
|
|
77
|
+
*/
|
|
78
|
+
export function antigravityAccountSupportsModel(
|
|
79
|
+
providerName: string,
|
|
80
|
+
accountId: string,
|
|
81
|
+
modelId: string,
|
|
82
|
+
now = Date.now(),
|
|
83
|
+
): boolean | undefined {
|
|
84
|
+
const roster = freshRoster(providerName, accountId, now);
|
|
85
|
+
if (!roster) return undefined;
|
|
86
|
+
const id = modelId.trim();
|
|
87
|
+
if (!id) return undefined;
|
|
88
|
+
if (roster.ids.has(id)) return true;
|
|
89
|
+
const canonical = canonicalAntigravityUsageModel(id);
|
|
90
|
+
if (roster.ids.has(canonical)) return true;
|
|
91
|
+
const alias = Object.hasOwn(ANTIGRAVITY_MODEL_ALIASES, id) ? ANTIGRAVITY_MODEL_ALIASES[id] : undefined;
|
|
92
|
+
return alias !== undefined && roster.ids.has(alias);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Forget rosters (one account, or all). For account removal and tests. */
|
|
96
|
+
export function clearAntigravityAccountRosters(providerName?: string, accountId?: string): void {
|
|
97
|
+
if (providerName && accountId) {
|
|
98
|
+
rosters.delete(rosterKey(providerName, accountId));
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
rosters.clear();
|
|
102
|
+
}
|
|
@@ -17,6 +17,21 @@ import { readAntigravityWireSnapshot, writeAntigravityWireSnapshot, type Antigra
|
|
|
17
17
|
/** Current Antigravity Flash generation. */
|
|
18
18
|
const GEMINI_FLASH_CURRENT = "gemini-3.8-flash";
|
|
19
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Gemini 4 Argon. Like 3.8 it publishes one wire id per tier (`-low/-medium/-high`) and no
|
|
22
|
+
* `-tiered` row, verified 2026-10-14 against live CCA. CCA serves these ids only to the CLI
|
|
23
|
+
* User-Agent (see antigravityUserAgentForWireModel), and only to AI Ultra / Enterprise accounts.
|
|
24
|
+
*/
|
|
25
|
+
const GEMINI_ARGON = "gemini-4-argon";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Claude 5.5 on Antigravity: one wire id per tier (`-low/-medium/-high`), each 1,000,000 tokens
|
|
29
|
+
* with image input, observed in live CCA discovery on 2026-10-03
|
|
30
|
+
* (devlog/_fin/261003_release_lane_c). They ride the IDE User-Agent like every non-Argon model.
|
|
31
|
+
*/
|
|
32
|
+
const CLAUDE_OPUS_55 = "claude-opus-5-5";
|
|
33
|
+
const CLAUDE_SONNET_55 = "claude-sonnet-5-5";
|
|
34
|
+
|
|
20
35
|
/**
|
|
21
36
|
* Previous Flash generation — still served, still picker-visible.
|
|
22
37
|
*
|
|
@@ -75,6 +90,15 @@ const ANTIGRAVITY_WIRE_MODELS = [
|
|
|
75
90
|
];
|
|
76
91
|
|
|
77
92
|
const ANTIGRAVITY_PICKER_MODEL_BY_WIRE_ID: Record<string, string> = {
|
|
93
|
+
"claude-opus-5-5-low": CLAUDE_OPUS_55,
|
|
94
|
+
"claude-opus-5-5-medium": CLAUDE_OPUS_55,
|
|
95
|
+
"claude-opus-5-5-high": CLAUDE_OPUS_55,
|
|
96
|
+
"claude-sonnet-5-5-low": CLAUDE_SONNET_55,
|
|
97
|
+
"claude-sonnet-5-5-medium": CLAUDE_SONNET_55,
|
|
98
|
+
"claude-sonnet-5-5-high": CLAUDE_SONNET_55,
|
|
99
|
+
"gemini-4-argon-low": GEMINI_ARGON,
|
|
100
|
+
"gemini-4-argon-medium": GEMINI_ARGON,
|
|
101
|
+
"gemini-4-argon-high": GEMINI_ARGON,
|
|
78
102
|
"gemini-3.8-flash-low": "gemini-3.8-flash",
|
|
79
103
|
"gemini-3.8-flash-medium": "gemini-3.8-flash",
|
|
80
104
|
"gemini-3.8-flash-high": "gemini-3.8-flash",
|
|
@@ -164,8 +188,11 @@ function collapsesIntoKnownPickerModel(candidateId: string): boolean {
|
|
|
164
188
|
// Claude Opus: effort → thinkingConfig.thinkingLevel (CLIProxyAPI proven pattern).
|
|
165
189
|
export const ANTIGRAVITY_MODEL_EFFORTS: Record<string, string[]> = {
|
|
166
190
|
// No `minimal`: Google documents it as an error for this generation, and CCA exposes only
|
|
167
|
-
// the three tiers.
|
|
191
|
+
// the three tiers. Argon documents the same ladder (`minimal` is an HTTP 400).
|
|
192
|
+
[GEMINI_ARGON]: ["low", "medium", "high"],
|
|
168
193
|
"gemini-3.8-flash": ["low", "medium", "high"],
|
|
194
|
+
[CLAUDE_OPUS_55]: ["low", "medium", "high"],
|
|
195
|
+
[CLAUDE_SONNET_55]: ["low", "medium", "high"],
|
|
169
196
|
"gemini-3.7-flash": ["low", "medium", "high"],
|
|
170
197
|
"gemini-3.1-pro": ["low", "high"],
|
|
171
198
|
"claude-sonnet-4-6": ["low", "medium", "high", "max"],
|
|
@@ -175,7 +202,23 @@ export const ANTIGRAVITY_MODEL_EFFORTS: Record<string, string[]> = {
|
|
|
175
202
|
// ── Effort → wire model map for Gemini base models ──
|
|
176
203
|
const ANTIGRAVITY_EFFORT_WIRE_MAP: Record<string, Record<string, string>> = {
|
|
177
204
|
// 3.8 publishes one wire id per tier and no `-tiered` row, so its efforts ride the suffix.
|
|
178
|
-
// This is the 3.6 shape, not the 3.7 one.
|
|
205
|
+
// This is the 3.6 shape, not the 3.7 one. Argon has the same shape.
|
|
206
|
+
[GEMINI_ARGON]: {
|
|
207
|
+
low: "gemini-4-argon-low",
|
|
208
|
+
medium: "gemini-4-argon-medium",
|
|
209
|
+
high: "gemini-4-argon-high",
|
|
210
|
+
},
|
|
211
|
+
// Claude 5.5 publishes the same per-tier suffix family.
|
|
212
|
+
[CLAUDE_OPUS_55]: {
|
|
213
|
+
low: "claude-opus-5-5-low",
|
|
214
|
+
medium: "claude-opus-5-5-medium",
|
|
215
|
+
high: "claude-opus-5-5-high",
|
|
216
|
+
},
|
|
217
|
+
[CLAUDE_SONNET_55]: {
|
|
218
|
+
low: "claude-sonnet-5-5-low",
|
|
219
|
+
medium: "claude-sonnet-5-5-medium",
|
|
220
|
+
high: "claude-sonnet-5-5-high",
|
|
221
|
+
},
|
|
179
222
|
"gemini-3.8-flash": {
|
|
180
223
|
low: "gemini-3.8-flash-low",
|
|
181
224
|
medium: "gemini-3.8-flash-medium",
|
|
@@ -198,7 +241,7 @@ const ANTIGRAVITY_EFFORT_WIRE_MAP: Record<string, Record<string, string>> = {
|
|
|
198
241
|
* `gemini-3.1-pro` is deliberately absent: its `high` rung is `gemini-pro-agent`, which carries
|
|
199
242
|
* no tier suffix, so there the level is the only thing naming the effort.
|
|
200
243
|
*/
|
|
201
|
-
const ANTIGRAVITY_SUFFIX_TIER_MODELS = new Set(["gemini-3.8-flash"]);
|
|
244
|
+
const ANTIGRAVITY_SUFFIX_TIER_MODELS = new Set([GEMINI_ARGON, "gemini-3.8-flash", CLAUDE_OPUS_55, CLAUDE_SONNET_55]);
|
|
202
245
|
|
|
203
246
|
function completeDiscoveredEffortWireModelIds(
|
|
204
247
|
pickerId: string,
|
|
@@ -223,7 +266,11 @@ function completeDiscoveredEffortWireModelIds(
|
|
|
223
266
|
// ── Default effort per Gemini base model ──
|
|
224
267
|
const ANTIGRAVITY_DEFAULT_EFFORT: Record<string, string> = {
|
|
225
268
|
// Google's documented thinking_level default, and the tier CCA marks `recommended`.
|
|
269
|
+
[GEMINI_ARGON]: "medium",
|
|
226
270
|
"gemini-3.8-flash": "medium",
|
|
271
|
+
// Matches the discovered-family default (medium), so static and live resolution agree.
|
|
272
|
+
[CLAUDE_OPUS_55]: "medium",
|
|
273
|
+
[CLAUDE_SONNET_55]: "medium",
|
|
227
274
|
"gemini-3.1-pro": "high",
|
|
228
275
|
};
|
|
229
276
|
|
|
@@ -285,9 +332,25 @@ export const ANTIGRAVITY_MODEL_ALIASES: Record<string, string> = {
|
|
|
285
332
|
...ANTIGRAVITY_COMPATIBILITY_MODEL_ALIASES,
|
|
286
333
|
};
|
|
287
334
|
|
|
288
|
-
//
|
|
335
|
+
// Static picker lineup: collapsed base models only, exactly "3.8 / 4 / 5.5 / 5.5" (2026-10-14).
|
|
336
|
+
// Anything else reaches the picker only when live discovery returns it.
|
|
289
337
|
export const ANTIGRAVITY_MODELS = [
|
|
290
338
|
GEMINI_FLASH_CURRENT,
|
|
339
|
+
GEMINI_ARGON,
|
|
340
|
+
CLAUDE_OPUS_55,
|
|
341
|
+
CLAUDE_SONNET_55,
|
|
342
|
+
];
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Every base id the routing and discovery rules recognise: the static lineup plus models that
|
|
346
|
+
* left it. Removing a model from the picker must not change how a saved selection routes or how
|
|
347
|
+
* a discovered row collapses, so these legacy bases stay known here. Live discovery on
|
|
348
|
+
* 2026-10-14 no longer returns gemini-3.7-flash(-tiered) or the gemini-3.1-pro wire ids (both
|
|
349
|
+
* 404), but still returns the Claude 4.6 rows and gpt-oss-120b-medium, which therefore keep
|
|
350
|
+
* appearing through discovery.
|
|
351
|
+
*/
|
|
352
|
+
export const ANTIGRAVITY_KNOWN_PICKER_MODELS: readonly string[] = [
|
|
353
|
+
...ANTIGRAVITY_MODELS,
|
|
291
354
|
GEMINI_FLASH_PREVIOUS,
|
|
292
355
|
"gemini-3.1-pro",
|
|
293
356
|
"gemini-3.1-flash-image",
|
|
@@ -297,11 +360,20 @@ export const ANTIGRAVITY_MODELS = [
|
|
|
297
360
|
];
|
|
298
361
|
|
|
299
362
|
function isKnownAntigravityPickerModelId(value: string): boolean {
|
|
300
|
-
return isValidModelDiscoveryModelId(value) &&
|
|
363
|
+
return isValidModelDiscoveryModelId(value) && ANTIGRAVITY_KNOWN_PICKER_MODELS.includes(value);
|
|
301
364
|
}
|
|
302
365
|
|
|
303
366
|
// Context windows from the upstream `:fetchAvailableModels` maxTokens per model.
|
|
304
367
|
const ANTIGRAVITY_WIRE_MODEL_CONTEXT_WINDOWS: Record<string, number> = {
|
|
368
|
+
"claude-opus-5-5-low": 1_000_000,
|
|
369
|
+
"claude-opus-5-5-medium": 1_000_000,
|
|
370
|
+
"claude-opus-5-5-high": 1_000_000,
|
|
371
|
+
"claude-sonnet-5-5-low": 1_000_000,
|
|
372
|
+
"claude-sonnet-5-5-medium": 1_000_000,
|
|
373
|
+
"claude-sonnet-5-5-high": 1_000_000,
|
|
374
|
+
"gemini-4-argon-low": 1_048_576,
|
|
375
|
+
"gemini-4-argon-medium": 1_048_576,
|
|
376
|
+
"gemini-4-argon-high": 1_048_576,
|
|
305
377
|
"gemini-3.8-flash-low": 1_048_576,
|
|
306
378
|
"gemini-3.8-flash-medium": 1_048_576,
|
|
307
379
|
"gemini-3.8-flash-high": 1_048_576,
|
|
@@ -316,7 +388,10 @@ const ANTIGRAVITY_WIRE_MODEL_CONTEXT_WINDOWS: Record<string, number> = {
|
|
|
316
388
|
|
|
317
389
|
export const ANTIGRAVITY_MODEL_CONTEXT_WINDOWS: Record<string, number> = {
|
|
318
390
|
// Collapsed base IDs — explicit entries for the picker.
|
|
391
|
+
[GEMINI_ARGON]: 1_048_576,
|
|
319
392
|
"gemini-3.8-flash": 1_048_576,
|
|
393
|
+
[CLAUDE_OPUS_55]: 1_000_000,
|
|
394
|
+
[CLAUDE_SONNET_55]: 1_000_000,
|
|
320
395
|
"gemini-3.7-flash": 1_048_576,
|
|
321
396
|
"gemini-3.1-pro": 1_048_576,
|
|
322
397
|
// Wire IDs and aliases via derivation.
|
|
@@ -330,6 +405,9 @@ export const ANTIGRAVITY_MODEL_CONTEXT_WINDOWS: Record<string, number> = {
|
|
|
330
405
|
};
|
|
331
406
|
|
|
332
407
|
export const ANTIGRAVITY_MODEL_INPUT_MODALITIES: Record<string, string[]> = {
|
|
408
|
+
[GEMINI_ARGON]: ["text", "image"],
|
|
409
|
+
[CLAUDE_OPUS_55]: ["text", "image"],
|
|
410
|
+
[CLAUDE_SONNET_55]: ["text", "image"],
|
|
333
411
|
// Google documents 3.7 Flash as also accepting video, audio and PDF, but this proxy
|
|
334
412
|
// carries only text and image parts (`OcxImageContent`, src/types.ts) and the Codex
|
|
335
413
|
// catalog normalizes `input_modalities` against a closed enum. Advertising a modality
|
|
@@ -641,7 +719,7 @@ export function resolveAntigravityWireModelId(modelId: string, baseUrl?: string)
|
|
|
641
719
|
* IS the effort, and sending both creates a contradictory request.
|
|
642
720
|
*/
|
|
643
721
|
export function isAntigravitySuffixModelId(modelId: string): boolean {
|
|
644
|
-
return !
|
|
722
|
+
return !ANTIGRAVITY_KNOWN_PICKER_MODELS.includes(modelId);
|
|
645
723
|
}
|
|
646
724
|
|
|
647
725
|
/** The reasoning tier a retired Flash id used to encode, if it is one. */
|
|
@@ -753,7 +831,7 @@ export function resolveAntigravityEffortWireModel(
|
|
|
753
831
|
// summary aggregation consults this reverse map so one call model = one usage row.
|
|
754
832
|
const ANTIGRAVITY_USAGE_BASE_BY_ID: Record<string, string> = (() => {
|
|
755
833
|
const rev: Record<string, string> = {};
|
|
756
|
-
for (const base of
|
|
834
|
+
for (const base of ANTIGRAVITY_KNOWN_PICKER_MODELS) rev[base] = base;
|
|
757
835
|
for (const [base, effortMap] of Object.entries(ANTIGRAVITY_EFFORT_WIRE_MAP)) {
|
|
758
836
|
rev[base] = base;
|
|
759
837
|
for (const wire of Object.values(effortMap)) rev[wire] = base;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/** Process-local cooldown state shared by selection validation and key failover. */
|
|
2
|
+
const keyCooldowns = new Map<string, { cooldownUntil: number }>();
|
|
3
|
+
|
|
4
|
+
function cooldownKey(providerName: string, keyId: string): string {
|
|
5
|
+
return `${providerName}\0${keyId}`;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function setKeyCooldown(providerName: string, keyId: string, cooldownUntil: number): void {
|
|
9
|
+
const key = cooldownKey(providerName, keyId);
|
|
10
|
+
keyCooldowns.set(key, { cooldownUntil: Math.max(keyCooldowns.get(key)?.cooldownUntil ?? 0, cooldownUntil) });
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function isKeyInCooldown(providerName: string, keyId: string, now = Date.now()): boolean {
|
|
14
|
+
const entry = keyCooldowns.get(cooldownKey(providerName, keyId));
|
|
15
|
+
if (!entry) return false;
|
|
16
|
+
if (entry.cooldownUntil <= now) {
|
|
17
|
+
keyCooldowns.delete(cooldownKey(providerName, keyId));
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function getKeyCooldownUntil(providerName: string, keyId: string, now = Date.now()): number | null {
|
|
24
|
+
const entry = keyCooldowns.get(cooldownKey(providerName, keyId));
|
|
25
|
+
if (!entry) return null;
|
|
26
|
+
return entry.cooldownUntil > now ? entry.cooldownUntil : null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function sweepExpiredApiKeyCooldowns(now = Date.now()): number {
|
|
30
|
+
let removed = 0;
|
|
31
|
+
for (const [key, cooldown] of keyCooldowns) {
|
|
32
|
+
if (cooldown.cooldownUntil > now) continue;
|
|
33
|
+
keyCooldowns.delete(key);
|
|
34
|
+
removed += 1;
|
|
35
|
+
}
|
|
36
|
+
return removed;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Manual key management clears this provider's cooldowns. */
|
|
40
|
+
export function clearKeyCooldowns(providerName?: string): void {
|
|
41
|
+
if (!providerName) {
|
|
42
|
+
keyCooldowns.clear();
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const prefix = `${providerName}\0`;
|
|
46
|
+
for (const key of keyCooldowns.keys()) {
|
|
47
|
+
if (key.startsWith(prefix)) keyCooldowns.delete(key);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -18,9 +18,12 @@
|
|
|
18
18
|
*/
|
|
19
19
|
import { resolveEnvValue } from "../config/proxy-env";
|
|
20
20
|
import { loadKeyringBinding } from "../lib/keyring-native";
|
|
21
|
+
import { isGcpCredentialMaterial } from "../lib/gcp-credential-input";
|
|
21
22
|
import type { OcxProviderConfig } from "../types";
|
|
22
23
|
|
|
23
24
|
export const KEYCHAIN_REFERENCE_PREFIX = "keychain:";
|
|
25
|
+
/** Stored GCP JSON uses the same OS service, but must never be restored as a literal API key. */
|
|
26
|
+
export const GCP_CREDENTIAL_MARKER_PREFIX = "gcp-sa:";
|
|
24
27
|
export const PROVIDER_KEYCHAIN_SERVICE = "opencodex.provider-api-key.v1";
|
|
25
28
|
|
|
26
29
|
export interface ProviderKeychainEntry {
|
|
@@ -87,15 +90,17 @@ export function providerKeychainEntry(account: string): ProviderKeychainEntry {
|
|
|
87
90
|
function readKeychain(account: string, quiet = false): string | undefined {
|
|
88
91
|
const cached = resolvedCache.get(account);
|
|
89
92
|
if (cached !== undefined) return cached;
|
|
93
|
+
let value: string | null = null;
|
|
90
94
|
try {
|
|
91
|
-
|
|
92
|
-
if (typeof value === "string" && value.trim()) {
|
|
93
|
-
resolvedCache.set(account, value);
|
|
94
|
-
return value;
|
|
95
|
-
}
|
|
95
|
+
value = providerKeychainEntry(account).getPassword();
|
|
96
96
|
} catch {
|
|
97
97
|
// fall through to the single warning below
|
|
98
98
|
}
|
|
99
|
+
if (typeof value === "string" && value.trim()) {
|
|
100
|
+
if (isGcpCredentialMaterial(value)) throw new Error("GCP credential JSON cannot be used as a literal API key; select a gcp-sa reference");
|
|
101
|
+
resolvedCache.set(account, value);
|
|
102
|
+
return value;
|
|
103
|
+
}
|
|
99
104
|
if (!quiet && !warnedAccounts.has(account)) {
|
|
100
105
|
warnedAccounts.add(account);
|
|
101
106
|
console.warn(`[opencodex] provider key reference keychain:${account} could not be read from the OS keychain; requests for this provider have no credential until the keychain is available (no plaintext fallback)`);
|
|
@@ -125,7 +130,7 @@ export type ProviderKeyStoreKind = "keychain" | "env" | "file" | "none";
|
|
|
125
130
|
export function providerKeyStoreKind(provider: Pick<OcxProviderConfig, "apiKey"> | undefined): ProviderKeyStoreKind {
|
|
126
131
|
const key = provider?.apiKey;
|
|
127
132
|
if (!key) return "none";
|
|
128
|
-
if (isKeychainReference(key)) return "keychain";
|
|
133
|
+
if (key.startsWith(GCP_CREDENTIAL_MARKER_PREFIX) || isKeychainReference(key)) return "keychain";
|
|
129
134
|
if (/^\$\{?\w+\}?$/.test(key)) return "env";
|
|
130
135
|
return "file";
|
|
131
136
|
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { OcxConfig, OcxProviderConfig } from "../types";
|
|
2
|
+
import { isEgressTransparentExecutor, markEgressTransparentExecutor } from "../lib/provider-egress";
|
|
3
|
+
import { isNonReplayableResponse } from "../lib/upstream-retry";
|
|
4
|
+
import { providerApiKeySelectionOwnsFailure } from "./api-key-selection";
|
|
5
|
+
import { recordDistributedKeyFailure } from "./key-failover";
|
|
6
|
+
import { registerProviderResponseProvenance } from "./request-pacing";
|
|
7
|
+
|
|
8
|
+
interface KeyResponseOwner {
|
|
9
|
+
readonly providerName: string;
|
|
10
|
+
readonly provider: OcxProviderConfig;
|
|
11
|
+
}
|
|
12
|
+
const owners = new WeakMap<Response, KeyResponseOwner | null>();
|
|
13
|
+
registerProviderResponseProvenance(owners);
|
|
14
|
+
|
|
15
|
+
/** Capture the credential after connection-policy rewrites, immediately before fetch. */
|
|
16
|
+
export function distributedKeyPhysicalFetch(
|
|
17
|
+
physicalFetch: typeof globalThis.fetch, providerName: string, provider: OcxProviderConfig, target: string,
|
|
18
|
+
authHeader: "authorization" | "x-api-key" = "authorization",
|
|
19
|
+
): typeof globalThis.fetch {
|
|
20
|
+
if (!provider._apiKeyAttempt?.distributed) return physicalFetch;
|
|
21
|
+
const wrapped = (async (input, init) => {
|
|
22
|
+
const headers = new Headers(init?.headers ?? (input instanceof Request ? input.headers : undefined));
|
|
23
|
+
const destination = input instanceof Request ? input.url : String(input);
|
|
24
|
+
const ownsCredential = destination === target && !headers.has("api-key")
|
|
25
|
+
&& (authHeader === "authorization"
|
|
26
|
+
? headers.get("authorization") === `Bearer ${provider.apiKey}` && !headers.has("x-api-key")
|
|
27
|
+
: headers.get("x-api-key") === provider.apiKey && !headers.has("authorization"));
|
|
28
|
+
const owner: KeyResponseOwner | null = provider._apiKeyAttempt?.distributed && ownsCredential
|
|
29
|
+
? Object.freeze({ providerName, provider: Object.freeze({ ...provider,
|
|
30
|
+
_apiKeyAttempt: Object.freeze({ ...provider._apiKeyAttempt }) }) }) : null;
|
|
31
|
+
const response = await physicalFetch(input, init);
|
|
32
|
+
// Reused response identities cannot safely describe two physical sends.
|
|
33
|
+
owners.set(response, owners.has(response) ? null : owner);
|
|
34
|
+
return response;
|
|
35
|
+
}) as typeof globalThis.fetch;
|
|
36
|
+
return isEgressTransparentExecutor(physicalFetch) ? markEgressTransparentExecutor(wrapped) : wrapped;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Return only a still-configured credential captured at the physical send. */
|
|
40
|
+
export function distributedKeyResponseOwner(
|
|
41
|
+
response: Response, config: OcxConfig, providerName: string,
|
|
42
|
+
): OcxProviderConfig | null {
|
|
43
|
+
const owner = owners.get(response);
|
|
44
|
+
return owner?.providerName === providerName
|
|
45
|
+
&& providerApiKeySelectionOwnsFailure(config, providerName, owner.provider) ? owner.provider : null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Settle only the terminal refusal; configured same-key 429 retries retain their key. */
|
|
49
|
+
export function recordDistributedResponseFailure(
|
|
50
|
+
response: Response, config: OcxConfig, providerName: string, signal?: AbortSignal,
|
|
51
|
+
): void {
|
|
52
|
+
if (signal?.aborted || isNonReplayableResponse(response) || (response.status !== 401 && response.status !== 429)) return;
|
|
53
|
+
const owner = distributedKeyResponseOwner(response, config, providerName);
|
|
54
|
+
if (!owner) return;
|
|
55
|
+
recordDistributedKeyFailure(config, providerName, owner._apiKeyAttempt,
|
|
56
|
+
response.status, Date.now(), response.headers.get("retry-after"));
|
|
57
|
+
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import { isKeyInCooldown } from "./api-key-cooldowns";
|
|
1
2
|
import { randomUUID } from "node:crypto";
|
|
2
3
|
import { mutatePersistedConfig } from "../config";
|
|
3
4
|
import { publishAccountSelection } from "../lib/account-selection-events";
|
|
4
5
|
import { clearModelCache } from "../codex/model-cache";
|
|
5
6
|
import type { OcxConfig, OcxProviderConfig } from "../types";
|
|
6
7
|
import type { ProviderApiKeySelection } from "../types/provider";
|
|
7
|
-
import { routedProviderConfig } from "
|
|
8
|
+
import { routedProviderConfig } from "./routed-config";
|
|
8
9
|
import { OPENCODE_GO_SESSION_HEADER } from "./opencode-go-transport";
|
|
9
10
|
import { resolveProviderTransport, XAI_GROK_COMPATIBILITY, type OcxProviderTransport } from "./xai-transport";
|
|
10
11
|
import { captureProviderApiKeySelection } from "./api-key-selection-capture";
|
|
@@ -17,10 +18,17 @@ function matchesSelection(provider: OcxProviderConfig, expected: ProviderApiKeyS
|
|
|
17
18
|
&& current.revision === expected.revision;
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
function currentKeyProvider(config: OcxConfig, name: string): OcxProviderConfig | null {
|
|
21
|
+
function currentKeyProvider(config: OcxConfig, name: string, selection?: ProviderApiKeySelection, allowCooling = false): OcxProviderConfig | null {
|
|
21
22
|
const configured = config.providers[name];
|
|
22
23
|
if (!configured || configured.disabled) return null;
|
|
23
|
-
|
|
24
|
+
let row = configured;
|
|
25
|
+
if (selection?.distributed) {
|
|
26
|
+
const entry = configured.apiKeyPool?.find(key => key.id === selection.entryId && key.key === selection.reference);
|
|
27
|
+
if (!entry || (!allowCooling && isKeyInCooldown(name, entry.id)) || configured.apiKeySelectionRevision !== selection.revision
|
|
28
|
+
|| !configured.requestPacing?.enabled || configured.requestPacing.keySelection !== "round-robin") return null;
|
|
29
|
+
row = { ...configured, apiKey: entry.key, _apiKeyAttempt: selection };
|
|
30
|
+
}
|
|
31
|
+
const current = routedProviderConfig(name, { ...row, _apiKeyAttempt: selection?.distributed ? selection : undefined });
|
|
24
32
|
if (current.authMode === "oauth" || current.authMode === "forward") return null;
|
|
25
33
|
if (current.authMode === "key" && !current.keyOptional && !current.apiKey?.trim()) return null;
|
|
26
34
|
return current;
|
|
@@ -32,10 +40,23 @@ export function providerApiKeySelectionIsCurrent(
|
|
|
32
40
|
name: string,
|
|
33
41
|
routedProvider: OcxProviderConfig,
|
|
34
42
|
): boolean {
|
|
35
|
-
|
|
43
|
+
return matchesConfiguredSelection(config, name, routedProvider, false);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** An already sent failure can extend cooldown, but cannot authorize another dispatch. */
|
|
47
|
+
export function providerApiKeySelectionOwnsFailure(
|
|
48
|
+
config: OcxConfig, name: string, routedProvider: OcxProviderConfig,
|
|
49
|
+
): boolean {
|
|
50
|
+
return matchesConfiguredSelection(config, name, routedProvider, true);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function matchesConfiguredSelection(
|
|
54
|
+
config: OcxConfig, name: string, routedProvider: OcxProviderConfig, allowCooling: boolean,
|
|
55
|
+
): boolean {
|
|
36
56
|
const expected = routedProvider._apiKeyAttempt;
|
|
57
|
+
const current = currentKeyProvider(config, name, expected, allowCooling);
|
|
37
58
|
return current !== null && expected !== undefined
|
|
38
|
-
&& matchesSelection(config.providers[name]!, expected)
|
|
59
|
+
&& (expected.distributed === true || matchesSelection(config.providers[name]!, expected))
|
|
39
60
|
&& current.apiKey === routedProvider.apiKey
|
|
40
61
|
&& current.authMode === routedProvider.authMode
|
|
41
62
|
&& current.baseUrl === routedProvider.baseUrl;
|
|
@@ -47,7 +68,11 @@ export function resolveCurrentProviderApiKeyTransport(
|
|
|
47
68
|
name: string,
|
|
48
69
|
routedProvider: OcxProviderConfig,
|
|
49
70
|
): OcxProviderConfig | null {
|
|
50
|
-
|
|
71
|
+
// A permit acquired for one key cannot authorize a different key after a manual edit.
|
|
72
|
+
if (routedProvider.requestPacing?.enabled && routedProvider.requestPacing.keys
|
|
73
|
+
&& routedProvider._apiKeyAttempt && config.providers[name]
|
|
74
|
+
&& !routedProvider._apiKeyAttempt.distributed && !matchesSelection(config.providers[name]!, routedProvider._apiKeyAttempt)) return null;
|
|
75
|
+
const current = currentKeyProvider(config, name, routedProvider._apiKeyAttempt);
|
|
51
76
|
if (!current) return null;
|
|
52
77
|
const runtime = routedProvider as OcxProviderTransport;
|
|
53
78
|
const headers = { ...current.headers };
|