@bitkyc08/opencodex 2.80.0 → 2.82.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 +3 -3
- package/bin/ocx.mjs +166 -73
- package/gui/dist/assets/App-Ds_qL7o8.js +54 -0
- package/gui/dist/assets/Tray-BqHF5Trd.js +1 -0
- package/gui/dist/assets/{index-Brk6F3fn.css → index-BAbcj51J.css} +1 -1
- package/gui/dist/assets/index-D3FBCNiP.js +94 -0
- package/gui/dist/assets/quota-summary-DTBC8n7-.js +1 -0
- package/gui/dist/index.html +2 -2
- package/gui/dist/provider-icons/claude-green.svg +1 -0
- package/package.json +3 -2
- package/src/adapters/anthropic-model-contract.ts +8 -2
- package/src/adapters/anthropic.ts +8 -3
- package/src/adapters/codebuddy/mcp-server.ts +2 -25
- package/src/adapters/codebuddy/tool-bridge.ts +1 -7
- package/src/adapters/coding-agent/mcp-server.ts +33 -0
- package/src/adapters/coding-agent/protocol.ts +122 -17
- package/src/adapters/coding-agent/tool-catalog.ts +75 -0
- package/src/adapters/coding-agent/turn.ts +101 -44
- package/src/adapters/cursor/catalog.ts +8 -0
- package/src/adapters/cursor/effort-map.ts +2 -0
- package/src/adapters/devin/live-models.ts +2 -0
- package/src/adapters/kiro-retry.ts +9 -4
- package/src/adapters/openai-chat/messages.ts +7 -6
- package/src/adapters/openai-responses/compaction-search-history.ts +109 -0
- package/src/adapters/openai-responses/passthrough.ts +9 -4
- package/src/adapters/openai-responses/tool-output-recovery.ts +20 -0
- package/src/adapters/physical-send.ts +8 -5
- package/src/adapters/qoder/adapter.ts +41 -6
- package/src/adapters/qoder/scaffold-guard.ts +2 -3
- package/src/adapters/zed.ts +16 -1
- package/src/claude/agents-inject.ts +14 -3
- package/src/claude/context-windows.ts +19 -17
- package/src/claude/desktop-3p.ts +88 -12
- package/src/claude/desktop-profile.ts +2 -1
- package/src/claude/gateway-cache.ts +18 -1
- package/src/claude/inbound-cache-stabilize.ts +4 -9
- package/src/claude/inbound.ts +78 -3
- package/src/claude/intercept/cli-catalog.ts +3 -0
- package/src/claude/intercept/model-bindings.ts +1 -0
- package/src/claude/intercept/picker-ca-persistence.ts +7 -6
- package/src/claude/intercept/picker-listener.ts +71 -4
- package/src/claude/intercept/picker-models.ts +14 -6
- package/src/claude/intercept/settings.ts +3 -0
- package/src/claude/long-context.ts +66 -0
- package/src/claude/model-info.ts +19 -16
- package/src/claude/outbound.ts +94 -9
- package/src/claude/subagent-model.ts +40 -11
- package/src/cli/account-anthropic-threshold.ts +14 -5
- package/src/cli/account-api.ts +3 -1
- package/src/cli/account-extended.ts +13 -9
- package/src/cli/account-policy-dto.ts +1 -1
- package/src/cli/account-policy.ts +13 -4
- package/src/cli/account.ts +3 -3
- package/src/cli/agent.ts +9 -5
- package/src/cli/aside-profile-recovery.ts +33 -0
- package/src/cli/capabilities-accounts.ts +2 -2
- package/src/cli/capabilities-agents-routing.ts +24 -0
- package/src/cli/capabilities-base.ts +5 -5
- package/src/cli/capabilities-integrations.ts +18 -2
- package/src/cli/cli-path-diagnostics.ts +132 -0
- package/src/cli/codex-shim-autorestore.ts +3 -0
- package/src/cli/desktop-runtime-guidance.ts +65 -0
- package/src/cli/dispatch.ts +15 -1
- package/src/cli/doctor.ts +16 -3
- package/src/cli/help.ts +4 -1
- package/src/cli/index.ts +25 -14
- package/src/cli/integration-aside-sync.ts +2 -1
- package/src/cli/integration-plan-dto.ts +1 -0
- package/src/cli/integration-preview.ts +8 -1
- package/src/cli/integrations.ts +48 -6
- package/src/cli/message-args.ts +34 -0
- package/src/cli/message-command.ts +43 -0
- package/src/cli/message-runtime.ts +6 -0
- package/src/cli/observe.ts +3 -1
- package/src/cli/provider.ts +29 -6
- package/src/cli/registry.ts +24 -2
- package/src/cli/resolve.ts +25 -2
- package/src/cli/root.ts +4 -2
- package/src/cli/status.ts +64 -12
- package/src/cli/update-restart-child.ts +4 -2
- package/src/cli/update-restart-home.ts +28 -10
- package/src/cli/update-restart-service-record.ts +116 -0
- package/src/cli/update-restart-supervision.ts +96 -0
- package/src/cli/update-restart.ts +59 -13
- package/src/cli/v2.ts +27 -20
- package/src/cli/version-skew-notice.ts +77 -0
- package/src/clients/config-export/commandcode.ts +131 -0
- package/src/clients/config-export/contracts.ts +12 -0
- package/src/clients/config-export.ts +64 -1
- package/src/clients/lazycodex.ts +1 -1
- package/src/clients/omo-role-models.ts +32 -7
- package/src/codex/auth-api/account-list.ts +11 -3
- package/src/codex/auth-api/account-selection.ts +107 -0
- package/src/codex/auth-api/main-account-probe.ts +25 -8
- package/src/codex/auth-api/pool-mode-gate.ts +33 -9
- package/src/codex/auth-api/pool-quota-probe.ts +15 -1
- package/src/codex/auth-api/routes.ts +11 -49
- package/src/codex/auth-context.ts +20 -1
- package/src/codex/autostart-health.ts +12 -3
- package/src/codex/catalog/auto-review.ts +13 -6
- package/src/codex/catalog/build-entries.ts +33 -7
- package/src/codex/catalog/bundled.ts +155 -26
- package/src/codex/catalog/combo-member.ts +3 -2
- package/src/codex/catalog/control-plane.ts +45 -0
- package/src/codex/catalog/effort.ts +37 -6
- package/src/codex/catalog/gather-capture.ts +10 -3
- package/src/codex/catalog/model-hints.ts +15 -8
- package/src/codex/catalog/model-visibility.ts +3 -1
- package/src/codex/catalog/operator-tiers.ts +38 -0
- package/src/codex/catalog/parsing.ts +2 -1
- package/src/codex/catalog/provider-models.ts +58 -44
- package/src/codex/catalog/retained-sync.ts +7 -5
- package/src/codex/catalog/routed-gather.ts +15 -1
- package/src/codex/catalog-auto-refresh-sources.ts +3 -4
- package/src/codex/catalog-auto-refresh.ts +19 -0
- package/src/codex/catalog.ts +1 -1
- package/src/codex/codex-home-owner.ts +3 -0
- package/src/codex/config-write-lock.ts +333 -0
- package/src/codex/control-plane-models.ts +6 -0
- package/src/codex/convergence.ts +2 -1
- package/src/codex/features.ts +257 -200
- package/src/codex/inject/config-write-section.ts +34 -0
- package/src/codex/inject/multi-agent-v2.ts +14 -6
- package/src/codex/inject/remove.ts +36 -11
- package/src/codex/inject/restore.ts +85 -20
- package/src/codex/inject-coordination.ts +15 -6
- package/src/codex/inject.ts +62 -20
- package/src/codex/journal.ts +62 -17
- package/src/codex/main-account-cache.ts +43 -0
- package/src/codex/paths.ts +2 -0
- package/src/codex/prepared-home.ts +18 -0
- package/src/codex/prompt-journal.ts +25 -12
- package/src/codex/prompt-layers.ts +49 -26
- package/src/codex/prompt-lock-claim.ts +134 -0
- package/src/codex/prompt-lock-evidence.ts +26 -0
- package/src/codex/prompt-lock-io.ts +16 -0
- package/src/codex/prompt-lock-owner.ts +130 -0
- package/src/codex/prompt-lock.ts +84 -53
- package/src/codex/routing/failure-window.ts +146 -0
- package/src/codex/routing/selection.ts +4 -1
- package/src/codex/routing.ts +13 -17
- package/src/codex/runtime.ts +106 -18
- package/src/codex/shim.ts +45 -19
- package/src/combos/jev-decision-contract.ts +25 -2
- package/src/combos/jev-service-exchange.ts +281 -0
- package/src/combos/jev.ts +34 -219
- package/src/combos/types.ts +1 -1
- package/src/config/atomic-write.ts +1 -1
- package/src/config/codex-ws-reuse-setting.ts +10 -0
- package/src/config/diagnostics.ts +27 -16
- package/src/config/live-reconcile.ts +17 -0
- package/src/config/load-degrade.ts +42 -0
- package/src/config/rebase-provenance.ts +11 -0
- package/src/config/schema/anthropic-account-pool.ts +143 -0
- package/src/config/schema/config-schema.ts +11 -1
- package/src/config/schema/leaf-validators.ts +4 -0
- package/src/config.ts +2 -0
- package/src/generated/compatibility-version.json +566 -298
- package/src/generated/model-metadata.ts +7 -6
- package/src/grok/inject.ts +50 -4
- package/src/grok/sync.ts +2 -1
- package/src/images/loop.ts +30 -11
- package/src/integrations/aside-profiles.ts +10 -3
- package/src/integrations/catalog-refresh.ts +19 -3
- package/src/integrations/mutation-plan.ts +7 -0
- package/src/integrations/registry.ts +8 -0
- package/src/integrations/state.ts +9 -2
- package/src/integrations/writer.ts +17 -5
- package/src/lib/bun-path-runtime.d.mts +32 -0
- package/src/lib/bun-path-runtime.mjs +95 -0
- package/src/lib/bun-runtime-preflight.ts +140 -0
- package/src/lib/desktop-cli-handoff.d.mts +20 -0
- package/src/lib/desktop-cli-handoff.mjs +92 -0
- package/src/lib/desktop-cli-record.d.mts +29 -0
- package/src/lib/desktop-cli-record.mjs +102 -0
- package/src/lib/lab-live-route-production.ts +9 -1
- package/src/lib/request-execution-budget.ts +244 -58
- package/src/lib/spend-pool-alias-validation.ts +79 -0
- package/src/lib/spend-pool-continuity.ts +34 -0
- package/src/lib/spend-reservation-ledger.ts +412 -50
- package/src/lib/state-store-registrations.ts +20 -3
- package/src/lib/synced-state-location.ts +46 -6
- package/src/lib/test-home-guard.ts +87 -1
- package/src/lib/translator-budget.ts +17 -4
- package/src/lib/upstream-retry.ts +17 -5
- package/src/lib/windows-atomic-replace.ts +38 -16
- package/src/lib/windows-elevation.ts +6 -0
- package/src/lib/windows-secret-acl.ts +92 -25
- package/src/lib/winsw.ts +2 -3
- package/src/lib/workflow-budget.ts +53 -11
- package/src/messaging/budget.ts +45 -0
- package/src/messaging/discovery.ts +57 -0
- package/src/messaging/envelope.ts +49 -0
- package/src/messaging/input.ts +37 -0
- package/src/messaging/rpc.ts +196 -0
- package/src/messaging/send.ts +58 -0
- package/src/messaging/socket.ts +72 -0
- package/src/messaging/types.ts +29 -0
- package/src/oauth/anthropic-account-refusal.ts +90 -18
- package/src/oauth/anthropic-account-threshold.ts +8 -2
- package/src/oauth/anthropic-continuity.ts +15 -1
- package/src/oauth/anthropic-model-quota.ts +120 -48
- package/src/oauth/anthropic-model-routes.ts +11 -3
- package/src/oauth/anthropic-oauth-definitions.ts +98 -0
- package/src/oauth/anthropic-pool-config.ts +30 -0
- package/src/oauth/anthropic-rate-limit-policy.ts +64 -9
- package/src/oauth/anthropic-routing.ts +1202 -866
- package/src/oauth/anthropic-send-ownership.ts +41 -0
- package/src/oauth/anthropic.ts +4 -1
- package/src/oauth/chatgpt.ts +113 -41
- package/src/oauth/generic-account-failover.ts +3 -1
- package/src/oauth/health.ts +8 -6
- package/src/oauth/index.ts +96 -175
- package/src/oauth/kiro.ts +22 -9
- package/src/oauth/login-cli.ts +2 -0
- package/src/oauth/model-discovery-auth.ts +43 -0
- package/src/oauth/pool-kernel.ts +11 -5
- package/src/oauth/pool-settings-capability.ts +5 -3
- package/src/oauth/store-anthropic-instance.ts +87 -0
- package/src/oauth/store.ts +68 -10
- package/src/oauth/token-guardian.ts +2 -0
- package/src/oauth/types.ts +2 -0
- package/src/protocols/plan-snapshot.ts +5 -0
- package/src/protocols/settings.ts +20 -5
- package/src/providers/anthropic-instance-id.ts +47 -0
- package/src/providers/anthropic-instance.ts +48 -0
- package/src/providers/anthropic-reset-grant-ledger.ts +11 -1
- package/src/providers/azure-model-metadata.ts +115 -0
- package/src/providers/derive.ts +23 -0
- package/src/providers/kiro-models.ts +3 -0
- package/src/providers/label.ts +1 -1
- package/src/providers/model-rename-fields.ts +2 -0
- package/src/providers/quota/account-cache.ts +37 -11
- package/src/providers/quota/anthropic-account-quota.ts +93 -0
- package/src/providers/quota/anthropic-cooldown-recovery.ts +222 -111
- package/src/providers/quota/report-cache.ts +24 -1
- package/src/providers/quota/vendor-probes-key.ts +127 -17
- package/src/providers/quota/vendor-probes-oauth.ts +46 -14
- package/src/providers/quota.ts +23 -33
- package/src/providers/registry/entries-core.ts +35 -30
- package/src/providers/registry/model-ids.ts +1 -0
- package/src/providers/registry/model-seeds.ts +7 -4
- package/src/providers/registry/types.ts +3 -0
- package/src/providers/registry.ts +3 -3
- package/src/responses/reasoning-envelope.ts +29 -1
- package/src/responses/reasoning-replay-cache.ts +17 -1
- package/src/responses/spill-store.ts +20 -3
- package/src/responses/state/metrics.ts +95 -0
- package/src/responses/state/snapshot-codec.ts +6 -0
- package/src/responses/state/spill-failure.ts +12 -0
- package/src/responses/state/spill-queue.ts +13 -5
- package/src/responses/state/unforced-store-false.ts +55 -0
- package/src/responses/state.ts +68 -76
- package/src/responses/task-input.ts +17 -0
- package/src/router.ts +28 -4
- package/src/routing/compatibility/assemble.ts +1 -1
- package/src/routing/quota.ts +7 -3
- package/src/server/auth-cors.ts +5 -0
- package/src/server/background-lifecycle.ts +27 -0
- package/src/server/chat-completions.ts +7 -1
- package/src/server/chat-native.ts +64 -28
- package/src/server/claude-messages.ts +59 -8
- package/src/server/gui-static.ts +38 -4
- package/src/server/inbound-body-admission.ts +1 -0
- package/src/server/index/claude-intercept-lifecycle.ts +3 -1
- package/src/server/index/serve-options.ts +10 -2
- package/src/server/index/spend-ledger-lifecycle.ts +12 -5
- package/src/server/index/startup-warnings.ts +19 -0
- package/src/server/index.ts +5 -2
- package/src/server/inference/context.ts +1 -1
- package/src/server/lifecycle.ts +1 -1
- package/src/server/management/agent-settings-routes.ts +123 -38
- package/src/server/management/anthropic-account-threshold.ts +12 -9
- package/src/server/management/anthropic-pool-settings.ts +72 -0
- package/src/server/management/anthropic-reset-grant-routes.ts +98 -39
- package/src/server/management/codex-agent-role-routes.ts +4 -2
- package/src/server/management/codex-prompt-routes.ts +1 -0
- package/src/server/management/codex-role-auto-assign.ts +13 -2
- package/src/server/management/config-routes.ts +85 -14
- package/src/server/management/context.ts +2 -2
- package/src/server/management/decision-routes.ts +2 -1
- package/src/server/management/integration-routes.ts +25 -2
- package/src/server/management/oauth-account-routes.ts +79 -98
- package/src/server/management/provider-instance-ownership.ts +44 -0
- package/src/server/management/provider-routes.ts +21 -28
- package/src/server/management/route-registry.ts +3 -1
- package/src/server/management/routing-profile-routes.ts +4 -4
- package/src/server/management/shared.ts +3 -3
- package/src/server/management/system-restart.ts +44 -14
- package/src/server/management/system-routes.ts +24 -2
- package/src/server/management/vision-sidecar-options.ts +9 -6
- package/src/server/management/web-search-sidecar-options.ts +51 -2
- package/src/server/management-auth.ts +6 -0
- package/src/server/messages-native-eligibility.ts +3 -2
- package/src/server/messages-native-oauth.ts +91 -45
- package/src/server/messages-native-selector.ts +39 -0
- package/src/server/messages-native.ts +133 -38
- package/src/server/messages-response-headers.ts +14 -0
- package/src/server/request-log-conversation.ts +48 -5
- package/src/server/request-log.ts +36 -2
- package/src/server/responses/adapter-continuation.ts +31 -19
- package/src/server/responses/adapter-dispatch.ts +75 -24
- package/src/server/responses/antigravity-validation-refusal.ts +20 -6
- package/src/server/responses/codex-ws-exchange.ts +17 -2
- package/src/server/responses/codex-ws-pool.ts +100 -13
- package/src/server/responses/codex-ws-wire.ts +9 -0
- package/src/server/responses/combo-requested-effort.ts +3 -1
- package/src/server/responses/compact.ts +25 -8
- package/src/server/responses/compaction-recovery.ts +1 -1
- package/src/server/responses/core-codex-account.ts +22 -1
- package/src/server/responses/core-combo-native.ts +24 -4
- package/src/server/responses/core-combo.ts +38 -25
- package/src/server/responses/core-lifetime.ts +3 -36
- package/src/server/responses/core-normalize.ts +1 -0
- package/src/server/responses/core-options.ts +8 -1
- package/src/server/responses/core-replay.ts +78 -0
- package/src/server/responses/core.ts +3 -3
- package/src/server/responses/fetch-helpers.ts +26 -3
- package/src/server/responses/passthrough-delivery.ts +30 -4
- package/src/server/responses/passthrough-dispatch.ts +39 -9
- package/src/server/responses/request-prepare.ts +41 -5
- package/src/server/responses/request-send-budget.ts +150 -50
- package/src/server/responses/request-spend.ts +325 -33
- package/src/server/responses/request-transport.ts +132 -52
- package/src/server/responses/response-effects.ts +1 -0
- package/src/server/responses/run-turn-execution.ts +13 -3
- package/src/server/responses/sidecar-execution.ts +113 -22
- package/src/server/responses/sidecar-send-budget.ts +58 -0
- package/src/server/responses/ws-upstream.ts +19 -6
- package/src/server/responses-undeclared-tool-guard.ts +5 -1
- package/src/server/startup-health-cache.ts +2 -1
- package/src/server/workflow-refusal.ts +33 -0
- package/src/service/cli.ts +20 -4
- package/src/service/desktop-command-guard.ts +32 -0
- package/src/service/desktop-startup.ts +66 -40
- package/src/service/desktop-supervision.d.mts +26 -0
- package/src/service/desktop-supervision.mjs +140 -0
- package/src/service/managing-cli.ts +1 -0
- package/src/service/orchestration.ts +46 -20
- package/src/service/ownership-mutation-lease.d.mts +1 -1
- package/src/service/ownership-mutation-lease.mjs +5 -7
- package/src/service/repair.ts +25 -9
- package/src/service/state.ts +4 -3
- package/src/service/systemd.ts +17 -8
- package/src/service/windows-ops.ts +61 -22
- package/src/sidecar/anthropic-binding.ts +79 -0
- package/src/sidecar/auth.ts +76 -2
- package/src/storage/policy-job.ts +5 -1
- package/src/storage/policy-worker.ts +3 -1
- package/src/storage/policy.ts +9 -0
- package/src/types/anthropic-account-pool.ts +33 -0
- package/src/types/config.ts +33 -28
- package/src/types/provider.ts +5 -0
- package/src/types/request.ts +5 -0
- package/src/types/tools.ts +7 -0
- package/src/types/wire.ts +1 -0
- package/src/types.ts +4 -0
- package/src/update/index.ts +57 -9
- package/src/update/restart-ownership.ts +10 -3
- package/src/update/runtime-ownership.d.mts +6 -0
- package/src/update/runtime-ownership.mjs +23 -7
- package/src/usage/cost.ts +3 -2
- package/src/usage/expected-prices.ts +66 -14
- package/src/vision/anthropic-describe.ts +16 -3
- package/src/vision/index.ts +3 -0
- package/src/vision/plan.ts +19 -6
- package/src/web-search/alpha-search.ts +7 -1
- package/src/web-search/anthropic-executor.ts +16 -4
- package/src/web-search/index.ts +7 -3
- package/src/web-search/loop.ts +32 -7
- package/src/web-search/passthrough-bridge.ts +4 -1
- package/src/web-search/sidecar-providers.ts +4 -3
- package/gui/dist/assets/App-MbDVfUeC.js +0 -54
- package/gui/dist/assets/Tray-CzkxK82A.js +0 -1
- package/gui/dist/assets/index-UJt6npXk.js +0 -94
- package/gui/dist/assets/quota-summary-DU3sD6Z-.js +0 -1
|
@@ -2,7 +2,9 @@ import { markAnthropicFamilyEnumeration } from "./anthropic-family-headers";
|
|
|
2
2
|
import { effectiveCodexAuthAccountId, fetchMainAccountInfoSnapshot, listCodexAuthAccountsSnapshot } from "../../codex/auth-api";
|
|
3
3
|
import { MAIN_CODEX_ACCOUNT_ID } from "../../codex/main-account";
|
|
4
4
|
import { getValidAccessToken } from "../../oauth";
|
|
5
|
-
import {
|
|
5
|
+
import { loadConfig } from "../../config";
|
|
6
|
+
import { configuredAnthropicInstance, type AnthropicInstanceId } from "../anthropic-instance";
|
|
7
|
+
import { captureOAuthAccountSelection, credentialGeneration, getAccountCredential, getAccountSet } from "../../oauth/store";
|
|
6
8
|
import { hydrateKiroAccountState, persistKiroAccountState } from "../kiro-account-state-disk";
|
|
7
9
|
import { kiroProbeCurrent, kiroProbeIdentity } from "./kiro-account-probe";
|
|
8
10
|
import { fetchMuseKeyQuotaSnapshot } from "../muse-key-quota";
|
|
@@ -33,6 +35,7 @@ import {
|
|
|
33
35
|
} from "./report-cache";
|
|
34
36
|
import {
|
|
35
37
|
accountCacheKey,
|
|
38
|
+
captureProviderAccountQuotaEpoch,
|
|
36
39
|
accountQuotaCache,
|
|
37
40
|
hydrateAccountQuotaCache,
|
|
38
41
|
mayCommitAccountQuotaKey,
|
|
@@ -40,7 +43,7 @@ import {
|
|
|
40
43
|
} from "./account-cache";
|
|
41
44
|
import type { OcxConfig, OcxProviderConfig } from "../../types";
|
|
42
45
|
import type { ProviderQuota, ProviderQuotaWindow } from "../quota-types";
|
|
43
|
-
import { AnthropicQuotaProbeOwnershipError,
|
|
46
|
+
import { AnthropicQuotaProbeOwnershipError, anthropicCooldownRecoveryFor } from "./anthropic-cooldown-recovery";
|
|
44
47
|
|
|
45
48
|
const XAI_BILLING_URL = "https://cli-chat-proxy.grok.com/v1/billing";
|
|
46
49
|
const XAI_CREDITS_URL = `${XAI_BILLING_URL}?format=credits`;
|
|
@@ -336,39 +339,68 @@ async function readAnthropicUsageQuota(accessToken: string): Promise<ProviderQuo
|
|
|
336
339
|
export async function fetchAnthropicUsageQuota(
|
|
337
340
|
accessToken: string,
|
|
338
341
|
requireFreshDispatch = false,
|
|
342
|
+
): Promise<ProviderQuota | null> {
|
|
343
|
+
return fetchAnthropicUsageQuotaForInstance("anthropic", accessToken, requireFreshDispatch);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
export async function fetchAnthropicUsageQuotaForInstance(
|
|
347
|
+
instance: AnthropicInstanceId,
|
|
348
|
+
accessToken: string,
|
|
349
|
+
requireFreshDispatch = false,
|
|
339
350
|
): Promise<ProviderQuota | null> {
|
|
340
351
|
// Recovery evidence must be requested after the claimed cooldown. Joining an older request
|
|
341
352
|
// can return after the 429 while still describing provider state from before that refusal.
|
|
342
|
-
const
|
|
353
|
+
const credentialIdentity = getAccountSet(instance)?.accounts.filter(row => row.credential.access === accessToken)
|
|
354
|
+
.map(row => [row.id, row.loginId, row.addedAt, credentialGeneration(row.credential)]);
|
|
355
|
+
const key = `${instance}\0${captureProviderAccountQuotaEpoch(instance)}\0${JSON.stringify(credentialIdentity)}\0${accessToken}`;
|
|
356
|
+
const joinable = requireFreshDispatch ? undefined : anthropicUsageInflight.get(key);
|
|
343
357
|
if (joinable) return joinable;
|
|
344
358
|
|
|
345
359
|
const probe = readAnthropicUsageQuota(accessToken).finally(() => {
|
|
346
|
-
if (anthropicUsageInflight.get(
|
|
360
|
+
if (anthropicUsageInflight.get(key) === probe) anthropicUsageInflight.delete(key);
|
|
347
361
|
});
|
|
348
|
-
anthropicUsageInflight.set(
|
|
362
|
+
anthropicUsageInflight.set(key, probe);
|
|
349
363
|
return probe;
|
|
350
364
|
}
|
|
351
365
|
|
|
352
|
-
|
|
366
|
+
/** `config` is the caller's live object; currency re-reads it instead of reloading the file. */
|
|
367
|
+
export async function fetchAnthropicQuota(provider: string, config: OcxConfig = loadConfig()): Promise<ProviderQuotaReport | null> {
|
|
368
|
+
const instance = configuredAnthropicInstance(config, provider);
|
|
369
|
+
const target = config.providers[provider];
|
|
370
|
+
if (!instance || target?.disabled || target && target.authMode !== "oauth") return null;
|
|
371
|
+
const targetIdentity = JSON.stringify(target);
|
|
372
|
+
const targetCurrent = () => configuredAnthropicInstance(config, provider) === instance
|
|
373
|
+
&& JSON.stringify(config.providers[provider]) === targetIdentity;
|
|
374
|
+
const recovery = anthropicCooldownRecoveryFor(instance);
|
|
353
375
|
// Capture the account we intend to probe before awaiting — a mid-flight active
|
|
354
376
|
// switch must not seed the wrong account's cache with this response.
|
|
355
|
-
const selection = captureOAuthAccountSelection(
|
|
377
|
+
const selection = captureOAuthAccountSelection(instance);
|
|
356
378
|
const probedAccountId = selection?.accountId;
|
|
357
|
-
const
|
|
379
|
+
const initialRow = getAccountSet(instance)?.accounts.find(row => row.id === probedAccountId);
|
|
380
|
+
const epoch = captureProviderAccountQuotaEpoch(instance);
|
|
381
|
+
const incarnation = probedAccountId ? recovery.reserveAnthropicAccountIncarnation(probedAccountId) : undefined;
|
|
382
|
+
const probedAccountKey = probedAccountId ? accountCacheKey(instance, probedAccountId) : null;
|
|
358
383
|
const writerGeneration = captureConfigGeneration();
|
|
359
384
|
let accessToken: string;
|
|
360
385
|
try {
|
|
361
|
-
accessToken = await getValidAccessToken(
|
|
386
|
+
accessToken = await getValidAccessToken(instance);
|
|
362
387
|
} catch {
|
|
363
388
|
return null;
|
|
364
389
|
}
|
|
365
390
|
if (!probedAccountId || !probedAccountKey) return null;
|
|
391
|
+
const currentRow = getAccountSet(instance)?.accounts.find(row => row.id === probedAccountId);
|
|
392
|
+
if (!initialRow || !currentRow || initialRow.loginId !== currentRow.loginId || initialRow.addedAt !== currentRow.addedAt
|
|
393
|
+
|| epoch !== captureProviderAccountQuotaEpoch(instance) || recovery.anthropicAccountIncarnation(probedAccountId) !== incarnation) return null;
|
|
366
394
|
let quota: ProviderQuota | null;
|
|
367
395
|
let anthropicCurrent: (() => boolean) | undefined;
|
|
368
396
|
try {
|
|
369
|
-
const result = await probeAnthropicQuotaWithRecovery(probedAccountId, accessToken,
|
|
370
|
-
fresh => {
|
|
371
|
-
|
|
397
|
+
const result = await recovery.probeAnthropicQuotaWithRecovery(probedAccountId, accessToken,
|
|
398
|
+
fresh => {
|
|
399
|
+
if (!targetCurrent()) throw new AnthropicQuotaProbeOwnershipError("quota target changed");
|
|
400
|
+
recovery.assertAnthropicQuotaSendAllowed(probedAccountId, accessToken);
|
|
401
|
+
return fetchAnthropicUsageQuotaForInstance(instance, accessToken, fresh);
|
|
402
|
+
},
|
|
403
|
+
() => targetCurrent() && mayCommitAccountQuotaKey(probedAccountKey, writerGeneration));
|
|
372
404
|
if (result && !result.isCurrent()) return null;
|
|
373
405
|
quota = result?.quota ?? null;
|
|
374
406
|
anthropicCurrent = result?.isCurrent;
|
|
@@ -380,14 +412,14 @@ export async function fetchAnthropicQuota(provider: string): Promise<ProviderQuo
|
|
|
380
412
|
// Share the active-account probe with the per-account cache so Providers-page
|
|
381
413
|
// loads do not double-hit Anthropic's rate-limited usage endpoint.
|
|
382
414
|
if (probedAccountId && probedAccountKey) {
|
|
383
|
-
const stillOwnsToken = getAccountCredential(
|
|
415
|
+
const stillOwnsToken = getAccountCredential(instance, probedAccountId)?.access === accessToken;
|
|
384
416
|
if (stillOwnsToken && anthropicCurrent?.() && mayCommitAccountQuotaKey(probedAccountKey, writerGeneration)) {
|
|
385
417
|
accountQuotaCache.set(probedAccountKey, { ts: Date.now(), quota, isCurrent: anthropicCurrent });
|
|
386
418
|
}
|
|
387
419
|
}
|
|
388
420
|
const quotaReport = report(provider, "anthropic:oauth-usage", quota);
|
|
389
421
|
if (quotaReport && anthropicCurrent) {
|
|
390
|
-
accountReportCurrent.set(quotaReport, () => anthropicCurrent() && getAccountSet(
|
|
422
|
+
accountReportCurrent.set(quotaReport, () => anthropicCurrent() && getAccountSet(instance)?.activeAccountId === probedAccountId);
|
|
391
423
|
}
|
|
392
424
|
return quotaReport;
|
|
393
425
|
}
|
package/src/providers/quota.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { listCodexAuthAccountsSnapshot } from "../codex/auth-api";
|
|
2
|
+
import { isAnthropicInstanceId, configuredAnthropicInstance } from "./anthropic-instance";
|
|
3
|
+
import { fetchAnthropicAccountQuota } from "./quota/anthropic-account-quota";
|
|
2
4
|
import { resolveEnvValue } from "../config";
|
|
3
5
|
import { getAccountCredential, getAccountSet } from "../oauth/store";
|
|
4
6
|
import { apiKeyPoolEntryId } from "./api-keys";
|
|
@@ -18,6 +20,7 @@ import {
|
|
|
18
20
|
accountReportCurrent,
|
|
19
21
|
AUTHORITATIVE_EMPTY_QUOTA,
|
|
20
22
|
bumpProviderQuotaInvalidationEpoch,
|
|
23
|
+
captureProviderReportEpochs, providerReportEpochChanged,
|
|
21
24
|
cacheKeyWithAggregationState,
|
|
22
25
|
getProviderQuotaReportCache,
|
|
23
26
|
hasCodexPoolProvider,
|
|
@@ -56,7 +59,6 @@ import {
|
|
|
56
59
|
} from "./quota/account-cache";
|
|
57
60
|
import {
|
|
58
61
|
fetchAnthropicQuota,
|
|
59
|
-
fetchAnthropicUsageQuota,
|
|
60
62
|
fetchChatGptForwardQuota,
|
|
61
63
|
fetchCursorQuota,
|
|
62
64
|
fetchKiroQuota,
|
|
@@ -68,11 +70,10 @@ import { fetchCommandCodeQuota, fetchKimiQuota, keyQuotaReaderForProvider } from
|
|
|
68
70
|
import { antigravityQuotaDiagnosticIdentity, fetchAntigravityQuota, probeAntigravityUsageQuota } from "./quota/antigravity";
|
|
69
71
|
import { persistKiroAccountState } from "./kiro-account-state-disk";
|
|
70
72
|
import { kiroProbeCurrent, kiroProbeIdentity } from "./quota/kiro-account-probe";
|
|
71
|
-
import { AnthropicQuotaProbeOwnershipError, anthropicCooldownFlightKey, assertAnthropicQuotaSendAllowed, probeAnthropicQuotaWithRecovery } from "./quota/anthropic-cooldown-recovery";
|
|
72
73
|
export type { ProviderQuota, ProviderQuotaCreditsUsd, ProviderQuotaWindow } from "./quota-types";
|
|
73
74
|
export { QUOTA_RESPONSE_MAX_BYTES } from "./quota-wire";
|
|
74
75
|
export {
|
|
75
|
-
clearProviderQuotaCache,
|
|
76
|
+
clearProviderQuotaCache, clearProviderQuotaCacheFor,
|
|
76
77
|
publishKeyReportForTests,
|
|
77
78
|
readProviderQuotaJsonForTests,
|
|
78
79
|
setProviderQuotaBeforePublishForTests,
|
|
@@ -86,7 +87,7 @@ export {
|
|
|
86
87
|
parseAnthropicRateLimitHeaders,
|
|
87
88
|
providerOAuthAccountQuotaMode,
|
|
88
89
|
readPassiveProviderAccountQuotas,
|
|
89
|
-
recordAnthropicAccountQuotaFromHeaders,
|
|
90
|
+
recordAnthropicAccountQuotaFromHeaders, recordAnthropicAccountQuotaFromHeadersForInstance,
|
|
90
91
|
recordPassiveAccountQuota,
|
|
91
92
|
reconcileProviderAccountQuotaRows,
|
|
92
93
|
resetProviderQuotaReconcileStateForTests,
|
|
@@ -96,7 +97,7 @@ export {
|
|
|
96
97
|
type ProviderAccountQuota,
|
|
97
98
|
} from "./quota/account-cache";
|
|
98
99
|
export { fetchAntigravityUsageQuota, isCanonicalAntigravityQuotaUrl, setAntigravityAccountQuotaTransportForTests } from "./quota/antigravity";
|
|
99
|
-
export { parseOllamaCloudQuota, parseZaiQuotaLimits, providerApiKeyQuotaMode } from "./quota/vendor-probes-key";
|
|
100
|
+
export { parseOllamaCloudBalance, parseOllamaCloudQuota, parseZaiQuotaLimits, providerApiKeyQuotaMode } from "./quota/vendor-probes-key";
|
|
100
101
|
export { parseXaiCreditsResponse } from "./quota/vendor-probes-oauth";
|
|
101
102
|
export async function fetchProviderApiKeyQuotas(config: OcxConfig, name: string, forceRefresh = false): Promise<ProviderApiKeyQuota[]> {
|
|
102
103
|
const provider = config.providers[name];
|
|
@@ -121,7 +122,7 @@ async function maybeFetchProviderQuota(
|
|
|
121
122
|
return fetchChatGptForwardQuota(config, name, provider, forceRefresh, prefetchedCodexSnapshot);
|
|
122
123
|
}
|
|
123
124
|
if (provider.authMode === "oauth" && explicitAccountReader(name)) return await fetchExplicitCurrentQuota(name, provider, config);
|
|
124
|
-
if (provider.authMode === "oauth" && name
|
|
125
|
+
if (provider.authMode === "oauth" && configuredAnthropicInstance(config, name)) return fetchAnthropicQuota(name, config);
|
|
125
126
|
if (provider.authMode === "oauth" && name === "google-antigravity") return await fetchAntigravityQuota(name);
|
|
126
127
|
if (provider.authMode === "oauth" && name === "kiro") return fetchKiroQuota(name);
|
|
127
128
|
// meta-muse: a device-logged-in account can be probed at the key endpoint; an
|
|
@@ -236,6 +237,7 @@ export async function fetchProviderQuotaReports(config: OcxConfig, forceRefresh
|
|
|
236
237
|
const keyCandidate = cacheKeyWithAggregationState(config, prefetchedCodexSnapshot);
|
|
237
238
|
const key = typeof keyCandidate === "string" ? keyCandidate : await keyCandidate;
|
|
238
239
|
const writerGeneration = captureConfigGeneration();
|
|
240
|
+
const providerEpochs = captureProviderReportEpochs();
|
|
239
241
|
const now = Date.now();
|
|
240
242
|
// The cache fast path must not extend a preserved last-good row past its 30-minute bound:
|
|
241
243
|
// a row preserved at age 29:59 plus a full 5-minute TTL would otherwise serve until ~35min.
|
|
@@ -312,6 +314,8 @@ export async function fetchProviderQuotaReports(config: OcxConfig, forceRefresh
|
|
|
312
314
|
byProvider.delete(provider);
|
|
313
315
|
generationMismatchedProviders.delete(provider);
|
|
314
316
|
}
|
|
317
|
+
// A provider-scoped clear during this probe retires only that provider's row.
|
|
318
|
+
for (const provider of [...byProvider.keys()]) if (providerReportEpochChanged(providerEpochs, provider)) byProvider.delete(provider);
|
|
315
319
|
|
|
316
320
|
const response = { generatedAt: Date.now(), reports: [...byProvider.values()] };
|
|
317
321
|
// Commit only when this probe still holds authority (no clear/force superseded it).
|
|
@@ -415,27 +419,27 @@ async function fetchAccountQuota(
|
|
|
415
419
|
accountId: string,
|
|
416
420
|
forceRefresh: boolean,
|
|
417
421
|
providerConfig?: OcxProviderConfig,
|
|
422
|
+
liveConfig?: OcxConfig,
|
|
418
423
|
): Promise<AccountQuotaCacheEntry> {
|
|
419
424
|
if (accountQuotaProbeSkip(provider, accountId)) return accountQuotaProbeSkip(provider, accountId)!;
|
|
425
|
+
if (isAnthropicInstanceId(provider)) return fetchAnthropicAccountQuota(provider, accountId, forceRefresh, liveConfig);
|
|
420
426
|
if (explicitAccountReader(provider)) return fetchExplicitAccountQuota(provider, accountId, forceRefresh, providerConfig);
|
|
421
|
-
if (provider === "
|
|
427
|
+
if (provider === "kiro") hydrateAccountQuotaCache();
|
|
422
428
|
const key = accountCacheKey(provider, accountId);
|
|
423
429
|
const writerGeneration = captureConfigGeneration();
|
|
424
430
|
const kiroIdentity = provider === "kiro" ? kiroProbeIdentity(accountId) : undefined;
|
|
425
431
|
const cachedCandidate = accountQuotaCache.get(key);
|
|
426
|
-
const cached =
|
|
427
|
-
&& (provider !== "anthropic" || cachedCandidate?.isCurrent?.() !== false) ? cachedCandidate : undefined;
|
|
432
|
+
const cached = provider !== "kiro" || cachedCandidate?.identity === kiroIdentity ? cachedCandidate : undefined;
|
|
428
433
|
if (!forceRefresh && cached && Date.now() - cached.ts < ACCOUNT_QUOTA_TTL_MS) {
|
|
429
434
|
if (provider === "google-antigravity" && cached.quotaFailure && cached.quotaFailureIsCurrent?.() !== true) return { ...cached, quotaFailure: undefined };
|
|
430
|
-
return
|
|
435
|
+
return cached;
|
|
431
436
|
}
|
|
432
|
-
const flightKey =
|
|
433
|
-
const flightCurrent = () => provider !== "anthropic" || anthropicCooldownFlightKey(key, accountId) === flightKey;
|
|
437
|
+
const flightKey = key;
|
|
434
438
|
const joinable = accountQuotaInflight.get(flightKey);
|
|
435
439
|
if (joinable) {
|
|
436
440
|
const joined = await joinable;
|
|
437
441
|
const joinedCurrent = joined.isCurrent?.();
|
|
438
|
-
if (joinedCurrent === false
|
|
442
|
+
if (joinedCurrent === false) return { ts: Date.now(), quota: null, unavailable: true };
|
|
439
443
|
return provider !== "kiro" || joined.identity === kiroIdentity
|
|
440
444
|
? joined : fetchAccountQuota(provider, accountId, true, providerConfig);
|
|
441
445
|
}
|
|
@@ -449,14 +453,10 @@ async function fetchAccountQuota(
|
|
|
449
453
|
};
|
|
450
454
|
const diagnosticFields = () => quotaFailure && quotaFailureIsCurrent() ? { quotaFailure, quotaFailureIsCurrent } : {};
|
|
451
455
|
const unavailable = (): AccountQuotaCacheEntry => {
|
|
452
|
-
if (!flightCurrent()) return { ts: Date.now(), quota: null, unavailable: true };
|
|
453
|
-
const previous = accountQuotaCache.get(key);
|
|
454
|
-
const retained = provider === "anthropic" && previous?.isCurrent?.() === false ? undefined : previous;
|
|
455
456
|
const entry: AccountQuotaCacheEntry = {
|
|
456
457
|
ts: Date.now(),
|
|
457
|
-
quota:
|
|
458
|
+
quota: cached?.quota ?? null,
|
|
458
459
|
unavailable: true,
|
|
459
|
-
...(provider === "anthropic" && retained?.isCurrent ? { isCurrent: retained.isCurrent } : {}),
|
|
460
460
|
...(provider === "kiro" ? { identity: kiroIdentity } : {}),
|
|
461
461
|
...diagnosticFields(),
|
|
462
462
|
};
|
|
@@ -470,14 +470,12 @@ async function fetchAccountQuota(
|
|
|
470
470
|
try {
|
|
471
471
|
if (provider === "google-antigravity") diagnosticIdentity = antigravityQuotaDiagnosticIdentity(accountId);
|
|
472
472
|
let quota: ProviderQuota | null;
|
|
473
|
-
let anthropicCurrent: (() => boolean) | undefined;
|
|
474
473
|
let kiroSnapshot: KiroUsageSnapshot | null = null;
|
|
475
474
|
if (provider === "kiro") {
|
|
476
475
|
kiroSnapshot = await fetchKiroUsageSnapshot(await kiroUsageContextForAccount(accountId));
|
|
477
476
|
quota = kiroSnapshot?.quota ?? null;
|
|
478
477
|
} else {
|
|
479
478
|
const token = await getTokenForAccountQuotaProbe(provider, accountId);
|
|
480
|
-
if (!flightCurrent()) throw new AnthropicQuotaProbeOwnershipError("anthropic quota flight is stale");
|
|
481
479
|
if (provider === "google-antigravity") {
|
|
482
480
|
// Per-account Gem/Cla windows (#1082). The project id is part of the stored
|
|
483
481
|
// credential; without it the probe cannot be made, and that is "unavailable",
|
|
@@ -488,22 +486,14 @@ async function fetchAccountQuota(
|
|
|
488
486
|
const result = await probeAntigravityUsageQuota(token, credential.projectId);
|
|
489
487
|
quota = result.kind === "available" ? result.quota : null;
|
|
490
488
|
if (result.kind === "unavailable") quotaFailure = result.failure;
|
|
491
|
-
} else if (provider === "anthropic") {
|
|
492
|
-
const result = await probeAnthropicQuotaWithRecovery(accountId, token,
|
|
493
|
-
fresh => { assertAnthropicQuotaSendAllowed(accountId, token); return fetchAnthropicUsageQuota(token, fresh); },
|
|
494
|
-
() => mayCommitAccountQuotaKey(key, writerGeneration));
|
|
495
|
-
if (result && !result.isCurrent()) throw new AnthropicQuotaProbeOwnershipError("anthropic quota probe lost publication ownership");
|
|
496
|
-
quota = result?.quota ?? null;
|
|
497
|
-
anthropicCurrent = result?.isCurrent;
|
|
498
489
|
} else {
|
|
499
490
|
return { ts: Date.now(), quota: null, unavailable: true };
|
|
500
491
|
}
|
|
501
492
|
}
|
|
502
493
|
if (!quota) return unavailable();
|
|
503
494
|
const entry: AccountQuotaCacheEntry = {
|
|
504
|
-
ts: Date.now(), quota
|
|
495
|
+
ts: Date.now(), quota,
|
|
505
496
|
...(provider === "kiro" ? { identity: kiroIdentity } : {}),
|
|
506
|
-
...(anthropicCurrent ? { isCurrent: anthropicCurrent } : {}),
|
|
507
497
|
};
|
|
508
498
|
if (mayCommitAccountQuotaKey(key, writerGeneration)
|
|
509
499
|
&& (provider !== "kiro" || kiroProbeCurrent(accountId, kiroIdentity))) {
|
|
@@ -513,7 +503,6 @@ async function fetchAccountQuota(
|
|
|
513
503
|
}
|
|
514
504
|
return entry;
|
|
515
505
|
} catch (error) {
|
|
516
|
-
if (provider === "anthropic" && error instanceof AnthropicQuotaProbeOwnershipError) return { ts: Date.now(), quota: null, unavailable: true };
|
|
517
506
|
if (provider === "google-antigravity") quotaFailure = "account_unavailable";
|
|
518
507
|
return unavailable();
|
|
519
508
|
}
|
|
@@ -531,20 +520,21 @@ export async function fetchProviderAccountQuotas(
|
|
|
531
520
|
provider: string,
|
|
532
521
|
forceRefresh = false,
|
|
533
522
|
providerConfig?: OcxProviderConfig,
|
|
523
|
+
liveConfig?: OcxConfig,
|
|
534
524
|
): Promise<ProviderAccountQuota[]> {
|
|
535
525
|
if (!supportsPerAccountQuota(provider)) return [];
|
|
536
526
|
const set = getAccountSet(provider);
|
|
537
527
|
if (!set) return [];
|
|
538
528
|
return mapQuotaRoster(set.accounts, async account => {
|
|
539
|
-
const entry = await fetchAccountQuota(provider, account.id, forceRefresh, providerConfig);
|
|
529
|
+
const entry = await fetchAccountQuota(provider, account.id, forceRefresh, providerConfig, liveConfig);
|
|
540
530
|
const result: ProviderAccountQuota = {
|
|
541
531
|
accountId: account.id,
|
|
542
|
-
quota: provider
|
|
532
|
+
quota: isAnthropicInstanceId(provider) ? normalizeAnthropicQuota(entry.quota, Date.now()) : entry.quota,
|
|
543
533
|
...(entry.unavailable ? { unavailable: true as const } : {}),
|
|
544
534
|
...(entry.unavailable && entry.quotaFailure && entry.quotaFailureIsCurrent?.() === true ? { quotaFailure: entry.quotaFailure } : {}),
|
|
545
535
|
};
|
|
546
536
|
if (entry.quotaFailureIsCurrent) Object.defineProperty(result, "quotaFailureIsCurrent", { value: entry.quotaFailureIsCurrent });
|
|
547
|
-
if (provider
|
|
537
|
+
if (isAnthropicInstanceId(provider) && entry.isCurrent) Object.defineProperty(result, "isCurrent", { value: entry.isCurrent });
|
|
548
538
|
if (!explicitAccountReader(provider)) return result;
|
|
549
539
|
const identity = entry.identity;
|
|
550
540
|
Object.defineProperty(result, "isCurrent", { value: () => {
|
|
@@ -14,6 +14,7 @@ import { cursorFastCapableBases } from "../../adapters/cursor/catalog";
|
|
|
14
14
|
import { COMMAND_CODE_MODEL_REASONING_EFFORTS } from "../command-code-efforts";
|
|
15
15
|
import { isCanonicalOpenRouterTarget } from "../openrouter-routing";
|
|
16
16
|
import type { ProviderRegistryEntry } from "./types";
|
|
17
|
+
import type { AnthropicInstanceId } from "../anthropic-instance-id";
|
|
17
18
|
import { ANTHROPIC_FAST_MODE_BETA } from "../anthropic-fast";
|
|
18
19
|
import {
|
|
19
20
|
ANTHROPIC_MODELS,
|
|
@@ -109,6 +110,37 @@ const ANTHROPIC_FAST_MODELS: Readonly<Record<string, boolean>> = Object.freeze({
|
|
|
109
110
|
const ANTHROPIC_FAST_TIER_DESCRIPTION =
|
|
110
111
|
"Claude fast mode: faster output at 2x price; needs usage credits (subscription) or fast-mode access (API)";
|
|
111
112
|
|
|
113
|
+
/** Each instance owns its entire seed, including nested model lists and Fast wire maps. */
|
|
114
|
+
function anthropicOAuthEntry(id: AnthropicInstanceId, label: string): ProviderRegistryEntry {
|
|
115
|
+
return structuredClone<ProviderRegistryEntry>({
|
|
116
|
+
id,
|
|
117
|
+
label,
|
|
118
|
+
adapter: "anthropic",
|
|
119
|
+
baseUrl: "https://api.anthropic.com",
|
|
120
|
+
authKind: "oauth",
|
|
121
|
+
allowBaseUrlOverride: true,
|
|
122
|
+
featured: true,
|
|
123
|
+
oauthId: id,
|
|
124
|
+
oauthFamily: "anthropic",
|
|
125
|
+
jawcodeBundle: "anthropic",
|
|
126
|
+
note: id === "anthropic" ? "Log in with your Claude account"
|
|
127
|
+
: "Independent Claude account pool — log in with a separate Claude account",
|
|
128
|
+
models: [...ANTHROPIC_MODELS],
|
|
129
|
+
modelContextWindows: { ...ANTHROPIC_MODEL_CONTEXT_WINDOWS },
|
|
130
|
+
modelInputModalities: { ...ANTHROPIC_MODEL_INPUT_MODALITIES },
|
|
131
|
+
modelReasoningEfforts: { ...ANTHROPIC_MODEL_REASONING_EFFORTS },
|
|
132
|
+
// Codex omits max_output_tokens; the fallback 8192 truncates long answers.
|
|
133
|
+
defaultMaxOutputTokens: ANTHROPIC_DEFAULT_MAX_OUTPUT_TOKENS,
|
|
134
|
+
modelMaxOutputTokens: { ...ANTHROPIC_MODEL_MAX_OUTPUT_TOKENS },
|
|
135
|
+
defaultModel: "claude-sonnet-5",
|
|
136
|
+
// Subscription /fast remains opt-in: it draws usage credits at 2x price.
|
|
137
|
+
fastWire: ANTHROPIC_FAST_WIRE,
|
|
138
|
+
modelSupportsServiceTier: { ...ANTHROPIC_FAST_MODELS },
|
|
139
|
+
fastTierDescription: ANTHROPIC_FAST_TIER_DESCRIPTION,
|
|
140
|
+
fastOptIn: true,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
112
144
|
export const PROVIDER_REGISTRY_CORE: readonly ProviderRegistryEntry[] = [
|
|
113
145
|
{
|
|
114
146
|
id: "openai",
|
|
@@ -209,7 +241,7 @@ export const PROVIDER_REGISTRY_CORE: readonly ProviderRegistryEntry[] = [
|
|
|
209
241
|
// (it is the current catalog, so its default ordering wins), then the ids
|
|
210
242
|
// only the old devin entry carried. Degraded-mode seed only either way —
|
|
211
243
|
// `liveModels` discovers the account's real roster.
|
|
212
|
-
models: ["swe-2", "swe-1-7", "gpt-5-6-sol", "gpt-6-astra", "gpt-6-1-sol", "gpt-6-sol", "gpt-6-luna", "claude-opus-5-5", "claude-opus-5", "claude-fable-5-1", "claude-sonnet-5-5", "claude-sonnet-5", "glm-5-3", "kimi-k3", "gemini-3-8-flash", "grok-4-6", "grok-4-7", "swe-1-7-lightning", "gpt-5-6-luna", "gpt-5-6-terra", "claude-opus-4-8", "glm-5-2", "kimi-k2-7", "grok-4-5"],
|
|
244
|
+
models: ["swe-2", "swe-1-7", "gpt-5-6-sol", "gpt-6-astra", "gpt-6-1-sol", "gpt-6-sol", "gpt-6-luna", "claude-opus-5-5", "claude-opus-5", "claude-fable-5-1", "claude-sonnet-5-5", "claude-haiku-5-5", "claude-sonnet-5", "glm-5-3", "kimi-k3", "gemini-3-8-flash", "grok-4-6", "grok-4-7", "swe-1-7-lightning", "gpt-5-6-luna", "gpt-5-6-terra", "claude-opus-4-8", "glm-5-2", "kimi-k2-7", "grok-4-5"],
|
|
213
245
|
liveModels: true,
|
|
214
246
|
defaultModel: "swe-2",
|
|
215
247
|
modelContextWindows: DEVIN_MODEL_CONTEXT_WINDOWS,
|
|
@@ -490,35 +522,8 @@ export const PROVIDER_REGISTRY_CORE: readonly ProviderRegistryEntry[] = [
|
|
|
490
522
|
modelReasoningEfforts: ORCAROUTER_MODEL_REASONING_EFFORTS,
|
|
491
523
|
note: "Connect your OrcaRouter account with OAuth 2.0 + PKCE; the issued API key is stored in OpenCodex's existing credential store.",
|
|
492
524
|
},
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
label: "Anthropic Claude",
|
|
496
|
-
adapter: "anthropic",
|
|
497
|
-
baseUrl: "https://api.anthropic.com",
|
|
498
|
-
authKind: "oauth",
|
|
499
|
-
allowBaseUrlOverride: true,
|
|
500
|
-
featured: true,
|
|
501
|
-
oauthId: "anthropic",
|
|
502
|
-
jawcodeBundle: "anthropic",
|
|
503
|
-
note: "Log in with your Claude account",
|
|
504
|
-
models: [...ANTHROPIC_MODELS],
|
|
505
|
-
modelContextWindows: { ...ANTHROPIC_MODEL_CONTEXT_WINDOWS },
|
|
506
|
-
modelInputModalities: { ...ANTHROPIC_MODEL_INPUT_MODALITIES },
|
|
507
|
-
modelReasoningEfforts: { ...ANTHROPIC_MODEL_REASONING_EFFORTS },
|
|
508
|
-
// Codex omits max_output_tokens; without a provider budget the Anthropic adapter
|
|
509
|
-
// falls back to 8192, which truncates long answers with stop_reason=max_tokens.
|
|
510
|
-
defaultMaxOutputTokens: ANTHROPIC_DEFAULT_MAX_OUTPUT_TOKENS,
|
|
511
|
-
modelMaxOutputTokens: { ...ANTHROPIC_MODEL_MAX_OUTPUT_TOKENS },
|
|
512
|
-
defaultModel: "claude-sonnet-5",
|
|
513
|
-
// Claude fast mode on the subscription lane (Claude Code `/fast`): the OAuth route accepts
|
|
514
|
-
// `speed` and gates it on account entitlement (usage credits / org enablement), probed live
|
|
515
|
-
// 2026-09-23 (devlog/_plan/260923_anthropic_fast_speed/020_probe-evidence.md).
|
|
516
|
-
// Off until the operator opts in: fast mode draws usage credits at 2x price.
|
|
517
|
-
fastWire: ANTHROPIC_FAST_WIRE,
|
|
518
|
-
modelSupportsServiceTier: { ...ANTHROPIC_FAST_MODELS },
|
|
519
|
-
fastTierDescription: ANTHROPIC_FAST_TIER_DESCRIPTION,
|
|
520
|
-
fastOptIn: true,
|
|
521
|
-
},
|
|
525
|
+
anthropicOAuthEntry("anthropic", "Anthropic Claude"),
|
|
526
|
+
anthropicOAuthEntry("anthropic2", "Anthropic · Pool 2"),
|
|
522
527
|
{
|
|
523
528
|
id: "anthropic-apikey",
|
|
524
529
|
label: "Anthropic (API key)",
|
|
@@ -12,8 +12,10 @@ import type { ProviderModelDiscoverySpec } from "./types";
|
|
|
12
12
|
// 260929 Claude Sonnet 5.5 (`claude-sonnet-5-5`, released 2026-09-28): 1M context / 128K output /
|
|
13
13
|
// adaptive thinking / effort low..max with a high default, same price as Sonnet 5, per the Sonnet 5.5
|
|
14
14
|
// overview, migration guide, effort and pricing pages (platform.claude.com).
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
// 261008 Claude Haiku 5.5: 1M / 128K, adaptive thinking, effort low..max (default medium),
|
|
16
|
+
// per platform.claude.com Haiku 5.5 overview, migration guide and pricing page.
|
|
17
|
+
export const ANTHROPIC_MODELS = ["claude-fable-5-1", "claude-fable-5", "claude-sonnet-5-5", "claude-sonnet-5", "claude-opus-5-5", "claude-opus-5", "claude-opus-4-8", "claude-opus-4-7", "claude-opus-4-6", "claude-sonnet-4-6", "claude-haiku-5-5", "claude-haiku-4-5"];
|
|
18
|
+
export const ANTHROPIC_MODEL_CONTEXT_WINDOWS: Record<string, number> = { "claude-fable-5-1": 1_000_000, "claude-sonnet-5-5": 1_000_000, "claude-sonnet-5": 1_000_000, "claude-fable-5": 1_000_000, "claude-opus-5-5": 1_000_000, "claude-opus-5": 1_000_000, "claude-opus-4-8": 1_000_000, "claude-opus-4-7": 1_000_000, "claude-opus-4-6": 1_000_000, "claude-sonnet-4-6": 1_000_000, "claude-haiku-5-5": 1_000_000, "claude-haiku-4-5": 200_000 };
|
|
17
19
|
// All seeded Claude models support vision: https://platform.claude.com/docs/en/models/overview
|
|
18
20
|
export const ANTHROPIC_MODEL_INPUT_MODALITIES: Record<string, string[]> = Object.fromEntries(
|
|
19
21
|
ANTHROPIC_MODELS.map(id => [id, ["text", "image"]]),
|
|
@@ -40,6 +42,7 @@ export const ANTHROPIC_DEFAULT_MAX_OUTPUT_TOKENS = 64_000;
|
|
|
40
42
|
* headroom arithmetic wrong.
|
|
41
43
|
*/
|
|
42
44
|
export const ANTHROPIC_MODEL_MAX_OUTPUT_TOKENS: Record<string, number> = {
|
|
45
|
+
"claude-haiku-5-5": 128_000,
|
|
43
46
|
"claude-fable-5-1": 128_000,
|
|
44
47
|
"claude-fable-5": 128_000,
|
|
45
48
|
"claude-sonnet-5-5": 128_000,
|
|
@@ -70,8 +73,8 @@ export const ANTHROPIC_MODEL_MAX_OUTPUT_TOKENS: Record<string, number> = {
|
|
|
70
73
|
* do what it says:
|
|
71
74
|
* - `minimal`: `adaptiveEffort` rewrites it to `low` (the adaptive wire 400s on it), so
|
|
72
75
|
* it is not a distinct setting.
|
|
73
|
-
* - `none`:
|
|
74
|
-
*
|
|
76
|
+
* - `none`: thinking-off support differs by family (`anthropic-model-contract.ts`);
|
|
77
|
+
* Haiku 5.5 accepts it without effort, while Fable rejects one outright.
|
|
75
78
|
* - `ultra`: not an Anthropic concept, and it is degraded to `max` at the request
|
|
76
79
|
* boundary anyway (src/responses/parser.ts).
|
|
77
80
|
*/
|
|
@@ -361,6 +361,8 @@ export interface ProviderRegistryEntry {
|
|
|
361
361
|
thinkingBudgetModels?: string[];
|
|
362
362
|
escapeBuiltinToolNames?: boolean;
|
|
363
363
|
oauthId?: string;
|
|
364
|
+
/** OAuth implementation family; instance identity remains the exact id/oauthId. */
|
|
365
|
+
oauthFamily?: "anthropic";
|
|
364
366
|
virtualModels?: Record<string, { wireModelId: string; reasoningMode: "pro" }>;
|
|
365
367
|
modelMaxInputTokens?: Record<string, number>;
|
|
366
368
|
jawcodeBundle?: string;
|
|
@@ -373,6 +375,7 @@ export interface ProviderRegistryEntry {
|
|
|
373
375
|
|
|
374
376
|
export type ProviderConfigSeed = Pick<
|
|
375
377
|
OcxProviderConfig,
|
|
378
|
+
"anthropicOAuthInstance" |
|
|
376
379
|
"adapter" | "baseUrl" | "apiKeyTransport" | "responsesPath" | "chatCompletionsPath" | "authMode" | "keyOptional" | "freeTier" | "modelSuffixBracketStrip" | "defaultModel" | "models"
|
|
377
380
|
| "liveModels" | "contextWindow" | "modelContextWindows" | "modelInputModalities"
|
|
378
381
|
| "modelDisplayNames"
|
|
@@ -7,7 +7,7 @@ import type {
|
|
|
7
7
|
} from "./registry/types";
|
|
8
8
|
import { PROVIDER_REGISTRY_CORE } from "./registry/entries-core";
|
|
9
9
|
import { PROVIDER_REGISTRY_EXTENDED } from "./registry/entries-extended";
|
|
10
|
-
|
|
10
|
+
import { anthropicInstanceRowShapeMatches } from "./anthropic-instance-id";
|
|
11
11
|
export type {
|
|
12
12
|
ProviderAuthKind,
|
|
13
13
|
MetadataModelIdNormalize,
|
|
@@ -102,10 +102,11 @@ export function normalizedProviderEndpoint(value: string): string {
|
|
|
102
102
|
*/
|
|
103
103
|
export function providerMatchesRegistryTransport(
|
|
104
104
|
id: string,
|
|
105
|
-
provider: Pick<OcxProviderConfig, "baseUrl" | "adapter"> & Partial<Pick<OcxProviderConfig, "authMode">>,
|
|
105
|
+
provider: Pick<OcxProviderConfig, "baseUrl" | "adapter"> & Partial<Pick<OcxProviderConfig, "authMode" | "anthropicOAuthInstance">>,
|
|
106
106
|
): boolean {
|
|
107
107
|
const entry = getProviderRegistryEntry(id);
|
|
108
108
|
if (!entry) return false;
|
|
109
|
+
if (id === "anthropic2") return entry.authKind === "oauth" && entry.oauthId === id && entry.oauthFamily === "anthropic" && anthropicInstanceRowShapeMatches(id, provider);
|
|
109
110
|
if (entry.authKind !== "key" || entry.preserveCustomDestination !== true) return true;
|
|
110
111
|
// The opt-in is intentionally limited to fixed key destinations. Fail closed if a future
|
|
111
112
|
// registry edit combines it with an override/template despite the registry parity tests.
|
|
@@ -115,7 +116,6 @@ export function providerMatchesRegistryTransport(
|
|
|
115
116
|
if (provider.authMode !== undefined && provider.authMode !== "key") return false;
|
|
116
117
|
return normalizedProviderEndpoint(provider.baseUrl) === normalizedProviderEndpoint(entry.baseUrl);
|
|
117
118
|
}
|
|
118
|
-
|
|
119
119
|
/**
|
|
120
120
|
* Resolve the registry entry a configured provider actually points at, by TRANSPORT
|
|
121
121
|
* rather than by name.
|
|
@@ -36,6 +36,32 @@ export interface ReasoningEnvelope {
|
|
|
36
36
|
* same way a signature does.
|
|
37
37
|
*/
|
|
38
38
|
krc?: string;
|
|
39
|
+
/**
|
|
40
|
+
* A Responses provider's own `encrypted_content` for one reasoning item, minted while serving
|
|
41
|
+
* `model` (the client-facing model the Claude route was asked for). The Claude route has no
|
|
42
|
+
* other channel to hand a provider its reasoning back: the body is `store: false` and Claude
|
|
43
|
+
* Code replays only the thinking block. `src/claude/inbound.ts` restores it only for a request
|
|
44
|
+
* naming the same model, so a model switch cannot carry it to a provider that cannot decrypt it.
|
|
45
|
+
*/
|
|
46
|
+
nat?: NativeReasoning;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface NativeReasoning {
|
|
50
|
+
/** The provider's opaque `encrypted_content`, verbatim. */
|
|
51
|
+
enc: string;
|
|
52
|
+
/** Client-facing model the item was produced for. */
|
|
53
|
+
model: string;
|
|
54
|
+
/** Route-bound HMAC for this exact ciphertext. */
|
|
55
|
+
tag: string;
|
|
56
|
+
/** The provider's reasoning item id, when it sent one. */
|
|
57
|
+
id?: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function decodeNativeReasoning(value: unknown): NativeReasoning | undefined {
|
|
61
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return undefined;
|
|
62
|
+
const { enc, model, id, tag } = value as { enc?: unknown; model?: unknown; id?: unknown; tag?: unknown };
|
|
63
|
+
if (typeof enc !== "string" || enc.length === 0 || typeof model !== "string" || model.length === 0 || typeof tag !== "string" || tag.length === 0) return undefined;
|
|
64
|
+
return typeof id === "string" && id.length > 0 ? { enc, model, tag, id } : { enc, model, tag };
|
|
39
65
|
}
|
|
40
66
|
|
|
41
67
|
export function encodeReasoningEnvelope(envelope: ReasoningEnvelope, budget?: TranslatorBudget): string {
|
|
@@ -116,7 +142,9 @@ export function decodeReasoningEnvelope(encryptedContent: string, budget?: Trans
|
|
|
116
142
|
if (hasTxt) envelope.txt = txt;
|
|
117
143
|
const krc = (parsed as { krc?: unknown }).krc;
|
|
118
144
|
if (typeof krc === "string" && krc.length > 0) envelope.krc = krc;
|
|
119
|
-
|
|
145
|
+
const nat = decodeNativeReasoning((parsed as { nat?: unknown }).nat);
|
|
146
|
+
if (nat) envelope.nat = nat;
|
|
147
|
+
return envelope.sig || envelope.red || hasTxt || envelope.krc || envelope.nat ? envelope : null;
|
|
120
148
|
} catch {
|
|
121
149
|
return null;
|
|
122
150
|
} finally {
|
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
* long-lived proxy cannot grow without limit.
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
-
import { createHash, createHmac, randomBytes } from "node:crypto";
|
|
22
|
+
import { createHash, createHmac, randomBytes, timingSafeEqual } from "node:crypto";
|
|
23
|
+
import { thoughtSignatureReplaySalt } from "./thought-signature-replay";
|
|
23
24
|
import type {
|
|
24
25
|
OcxProviderConfig,
|
|
25
26
|
OcxReasoningReplayIdentity,
|
|
@@ -27,6 +28,21 @@ import type {
|
|
|
27
28
|
} from "../types";
|
|
28
29
|
|
|
29
30
|
const MAX_ENTRIES = 64;
|
|
31
|
+
export interface NativeReasoningOwner { destination: string; credential: string }
|
|
32
|
+
|
|
33
|
+
/** Bind exact ciphertext to the durable route that accepted it. */
|
|
34
|
+
export function nativeReasoningTag(owner: NativeReasoningOwner | undefined, blob: string): string | undefined {
|
|
35
|
+
const salt = thoughtSignatureReplaySalt();
|
|
36
|
+
if (!owner || !salt || salt.length < 16 || blob.length === 0) return undefined;
|
|
37
|
+
return createHmac("sha256", salt).update("native-reasoning\0")
|
|
38
|
+
.update(JSON.stringify([owner.destination, owner.credential, createHash("sha256").update(blob).digest("hex")]))
|
|
39
|
+
.digest("hex");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function nativeReasoningTagMatches(expected: string | undefined, presented: string | undefined): boolean {
|
|
43
|
+
if (!expected || !presented || !/^[a-f0-9]{64}$/.test(expected) || !/^[a-f0-9]{64}$/.test(presented)) return false;
|
|
44
|
+
return timingSafeEqual(Buffer.from(expected, "hex"), Buffer.from(presented, "hex"));
|
|
45
|
+
}
|
|
30
46
|
const MAX_TOTAL_BYTES = 256 * 1024;
|
|
31
47
|
const TTL_MS = 60 * 60 * 1000;
|
|
32
48
|
const OPAQUE_BLOB_REJECTION_TTL_MS = 5 * 60 * 1000;
|