UNPKG

@juspay/neurolink

Version:

The pipe layer of an AI nervous system: one interface connecting provider neurons to your application, across three inference types — generate, stream and decide. `decide` returns typed, calibrated judgements from a non-generative model (~400ms, ~$0.00002

604 lines (603 loc) • 33.3 kB
import { buildTranslationOptions } from "../../proxy/proxyTranslationEngine.js"; import { ProxyTracer } from "../../proxy/proxyTracer.js"; import { isPermanentRefreshFailure } from "../../proxy/tokenRefresh.js"; import type { ProxyAccountDirectoryOverride } from "../../types/index.js"; import type { AccountAllowlist, AccountAdmissionLease, JsonObject, AccountCooldownPlan, AccountQuota, AccountQuotaWindow, AccountUsageFetchResult, AnthropicAttemptLogger, AnthropicAuthRetryResult, AnthropicEntitlementFailure, AnthropicInvalidRequestFailure, AnthropicLoopState, AnthropicScopedExhaustion, AnthropicNonOkResult, AnthropicSuccessResult, AnthropicUpstreamBody, AnthropicUpstreamBodyBuilder, AnthropicUpstreamFetchResult, ClaudeFinalRequestLogger, ClaudeLoggedErrorBuilder, ClaudeRequest, ClaudeProxyRouteRuntimeOptions, ModelRouterInterface, ParsedClaudeError, ProxyAccountRankingPolicy, ProxyAccountRoutingAffinityEvidence, ProxyAccountRoutingDecision, ProxyAccountRoutingReason, ProxyAccountRoutingSpillEvidence, ProxyAccountSortMetrics, ProxyAffinityPrecedenceSkipReason, ProxyBodyCaptureLogger, ProxyLimitsRefreshResponse, ProxyQuotaCooldownUpdate, ProxyOveragePolicy, ProxyPassthroughAccount, ProxyRoutingPolicySnapshot, QueuedAccountAdmission, RouteGroup, RuntimeAccountState, ServerContext, StreamTerminalOutcome } from "../../types/index.js"; declare function tryAcquireAccountAdmission(accountKey: string, capacity: number | undefined, generation?: number): AccountAdmissionLease | undefined; /** Current in-flight request count for one account, unlimited or capped. */ declare function getAccountInflight(accountKey: string): number; declare function enqueueAccountAdmission(accountKey: string, capacity: number, generation?: number): QueuedAccountAdmission; declare function acquireAccountAdmission(accountKey: string, capacity: number, abortSignal?: AbortSignal, timeoutMs?: number, generation?: number): Promise<AccountAdmissionLease | undefined>; declare function acquireFirstAvailableAccountAdmission(accountKeys: string[], capacity: number, abortSignal?: AbortSignal, timeoutMs?: number, generation?: number): Promise<{ accountKey: string; lease: AccountAdmissionLease; } | undefined>; /** Resolve the configured primary's stable key to its current index in the * request's enabledAccounts list. Returns 0 (insertion-order fallback) when * no key is configured or the key cannot be matched (account disabled/ * removed). The resolution is per-request because enabledAccounts membership * can shift between requests. */ declare function resolveHomeIndex(enabledAccounts: ProxyPassthroughAccount[], primaryAccountKey: string | undefined): number; /** If the configured home primary is no longer cooling, reset * primaryAccountIndex back to its index so traffic returns to the preferred * account once its rate limit window expires. Called at the start of each * request. Home is resolved fresh per call via resolveHomeIndex. */ declare function maybeResetPrimaryToHome(enabledAccounts: ProxyPassthroughAccount[], primaryAccountKey: string | undefined): void; declare function claimTransientRateLimitRetry(accountKey: string, coolingUntil: number, now?: number): number | undefined; declare function claimTransientCooldownAdmission(accountKey: string, coolingUntil: number, now?: number): number | undefined; declare function waitForTransientAccountAvailability(orderedAccounts: ProxyPassthroughAccount[]): Promise<ProxyPassthroughAccount[]>; /** Convert an Anthropic unified-window reset (Unix epoch SECONDS, per the * `anthropic-ratelimit-unified-*-reset` headers) into epoch-ms. Tolerates a * value already expressed in ms (some intermediaries normalise it). Returns * undefined for absent/zero/past-or-garbage timestamps so callers can fall * back to retry-after. */ declare function resetEpochToMs(resetEpoch: number | undefined, now: number): number | undefined; /** * Decide how to cool an account after a genuine (non-anti-abuse) 429. * * The unified subscription limits expose per-window status + reset: * - weekly (7d) "rejected" → hard cap for the week; cool until the 7d reset. * - session (5h) "rejected" → paced out for this session; cool until the 5h reset. * Both mean "retrying this account is futile until its window resets" unless * the provider explicitly enables overage. In that case, the subscription * window is exhausted but the account remains usable for paid fallback. * * Anything else (window still "allowed" but momentarily 429'd — a per-minute * burst / acceleration limit) is transient: honor retry-after as a floor, * allow a couple of jittered same-account retries, then a short cooldown. */ declare function planCooldownFor429(quota: AccountQuota | null, retryAfterMs: number, now: number, unifiedStatus?: string | undefined, policy?: ProxyOveragePolicy, requestedModel?: string): AccountCooldownPlan; /** * Reconcile quota-backed cooldowns against a fresh provider observation. A * rejected window parks the account until its reset; an allowed observation for * that same window releases an earlier cooldown whose reset timestamp was * stale. Transient and authentication cooldowns are intentionally untouched. */ declare function reconcileCooldownFromQuota(state: RuntimeAccountState, quota: AccountQuota, now: number, policy?: ProxyOveragePolicy): ProxyQuotaCooldownUpdate; /** * Seed each account's runtime quota from the persisted snapshots in * ~/.neurolink/account-quotas.json (keyed by provider-qualified account key). * Runtime state is * in-memory only, so without this the quota-aware ordering is blind after a * proxy restart: all accounts tie, selection falls back to token-store * enumeration order, and the first account served becomes self-reinforcing * (it alone has data) — starving the others regardless of their resets. * Never overwrites fresher in-memory quota. Persisted quota cannot create or * clear a cooldown: only an existing cooldown or fresh upstream response * headers can change admission state. */ declare function seedRuntimeQuotasFromDisk(accounts: ProxyPassthroughAccount[]): Promise<void>; declare function applyAccountUsageResult(account: ProxyPassthroughAccount, fetchResult: AccountUsageFetchResult, observedAt: number, prior?: AccountQuota | null): Promise<AccountQuota | null>; /** * Fetch fresh limits from Anthropic's usage endpoint for every eligible OAuth * account and write them through the exact same chain the passive header * capture uses (runtime state → cooldown reconciliation → debounced disk * snapshot), so routing and `auth list` see the refreshed windows and the * automatic path keeps working unchanged on top of them. */ declare function refreshAccountLimits(options?: { accountAllowlist?: AccountAllowlist; accountFilter?: string; snapshotOnly?: boolean; }): Promise<ProxyLimitsRefreshResponse>; /** * Publish the operator's extra-usage policy for paths that cannot receive it * explicitly. Callers that make a routing or cooldown decision take it as a * parameter instead — see {@link isOverageUsable} — so a concurrent request or * a hot config reload cannot change the answer mid-flight. * * `undefined` means "no runtime config on this path", which is not a request to * clear an operator's setting, so the current value is kept. */ declare function setOveragePolicy(policy: ProxyOveragePolicy | undefined): void; /** * Find the model-scoped quota window that applies to the requested model. * * Two sources describe the same cap differently: response headers carry the wire * id of the model just served, while the usage API reports a DISPLAY name * ("Fable", "Claude Opus 4.6"). So an exact wire-id match is tried first and * display-name containment over alphanumeric-normalized forms is the fallback. * Among equally specific matches the freshest observation wins, which favours * the continuously-updated header window over a manually-refreshed one. * * Returns null whenever the account reports no applicable, still-fresh scoped * cap — the common case. */ declare function matchScopedQuotaWindow(quota: AccountQuota | undefined, requestedModel: string | undefined, now?: number): AccountQuotaWindow | null; /** * Split a candidate set by whether each account can still serve the requested * model under its model-scoped cap. * * An account is scoped out only on evidence strong enough to act on: a fresh * window, a reset that is actually ticking, a rejected/spent reading, and no * paid extra usage to absorb the overflow. Anything weaker leaves the account * eligible — a stale or mis-parsed window must never be able to empty the pool. * * `exhaustion` is populated only when every candidate is scoped out AND the * evidence is trustworthy, which is what lets the caller report "switch model" * instead of a generic rate limit. */ declare function evaluateScopedExhaustion(accounts: ProxyPassthroughAccount[], requestedModel: string | undefined, now?: number, policy?: ProxyOveragePolicy): { eligible: ProxyPassthroughAccount[]; exhaustion: AnthropicScopedExhaustion | null; }; declare function orderAccountsByQuotaWithMetrics(accounts: ProxyPassthroughAccount[], now: number, primaryKey: string | undefined, sessionSoftLimit: number, sessionResetToleranceMs: number, options?: { requestedModel?: string; policy?: { ranking?: ProxyAccountRankingPolicy; affinityKey?: string; preferPrimary?: boolean; }; }): { orderedAccounts: ProxyPassthroughAccount[]; metricsByKey: Map<string, ProxyAccountSortMetrics>; reason: ProxyAccountRoutingReason; affinitySkippedReason: ProxyAffinityPrecedenceSkipReason | null; }; /** * Order accounts to MAXIMIZE quota utilization (fill-first, smart order): * spend the overall weekly allowance that expires SOONEST first, so quota * cannot disappear while traffic is consuming a newer weekly window. The 5h * window remains an availability boundary: a session at its soft limit is * temporarily demoted until that session resets. * * Priority among usable accounts: * 1. known healthy quota before unknown or ambiguous stale evidence. Quota * discovery is handled by a lightweight usage GET, never a user request. * 2. session headroom before session-saturated (>= soft limit or * "throttled") — do not re-hammer an urgent weekly account while its 5h * capacity is temporarily unavailable. * 3. soonest WEEKLY (7d) reset — consume the oldest overall allowance * before it expires. * 4. soonest SESSION (5h) reset — decides equal-weekly or fresh-weekly ties, * using tolerance buckets for comparator stability. * 5. highest weekly utilization — finish off the one closest to done. * 6. configured primary account, then insertion order * Saturated pairs are ordered by soonest 5h recovery first, then weekly reset, * because neither can consume more quota until session capacity returns. * Cooling/rejected accounts sort last, soonest-back-to-service first, as * last resort. */ declare function orderAccountsByQuota(accounts: ProxyPassthroughAccount[], now: number, primaryKey: string | undefined, sessionSoftLimit?: number, sessionResetToleranceMs?: number, requestedModel?: string): ProxyPassthroughAccount[]; declare function scheduleAdaptiveQuotaRefreshes(accounts: ProxyPassthroughAccount[], orderedAccounts: ProxyPassthroughAccount[], sessionSoftLimit: number, routingMetrics?: ReadonlyMap<string, ProxyAccountSortMetrics>): void; declare function scheduleHandoffQuotaRefresh(current: ProxyPassthroughAccount, candidate: ProxyPassthroughAccount | undefined, handoffEpoch: number, sessionSoftLimit: number): void; declare function buildRoutingDecision(args: { accounts: ProxyPassthroughAccount[]; orderedAccounts: ProxyPassthroughAccount[]; metricsByKey: ReadonlyMap<string, ProxyAccountSortMetrics>; evaluatedAt: number; strategy: "round-robin" | "fill-first"; primaryKey: string | undefined; quotaRoutingEnabled: boolean; quotaOrdered: boolean; sessionSoftLimit: number; sessionResetToleranceMs: number; rotationOffset: number; policySelectionReason?: ProxyAccountRoutingReason; policy?: ProxyRoutingPolicySnapshot; affinity?: ProxyAccountRoutingAffinityEvidence; spill?: ProxyAccountRoutingSpillEvidence; }): ProxyAccountRoutingDecision | undefined; declare function selectClaudeProxyAccountOrder(args: { enabledAccounts: ProxyPassthroughAccount[]; accountStrategy: "round-robin" | "fill-first"; primaryAccountKey: string | undefined; quotaRoutingEnabled: boolean; sessionSoftLimit: number; sessionResetToleranceMs: number; requestedModel?: string; sessionId?: string; ranking: ProxyAccountRankingPolicy; preferPrimary: boolean; sessionAffinityEnabled: boolean; sessionAffinityIdleTtlMs: number; spillInflight: number; setRoutingDecision: (decision: ProxyAccountRoutingDecision) => void; }): { orderedAccounts: ProxyPassthroughAccount[]; metricsByKey: Map<string, ProxyAccountSortMetrics>; }; declare function trackUpstreamReadableStream(source: ReadableStream<Uint8Array>): { stream: ReadableStream<Uint8Array>; outcome: Promise<StreamTerminalOutcome>; }; /** * Remove what a borrower has no business seeing. * * `x-neurolink-account` carries the lender's account label, which for an OAuth * account is their email address; the pool counters describe the shape of a * pool that is not the borrower's. The borrower's own routing needs the quota * and grant headers, and nothing else here. * * A no-op for the node's own traffic, where these headers are exactly the * diagnostics the operator wants. */ declare function redactHeadersForBorrower(headers: Record<string, string>): Record<string, string>; /** * The model that actually served a fallback turn. * * One request can leave more than one leg's model in metadata: the Codex leg * records its model on the failure path before the chain moves on, and the * configured chain tries Codex first. Choosing by key precedence therefore * billed a Vertex-served turn as the Codex model — the exact misattribution * this file's pricing fix exists to prevent. The account type that reached * `logFinalRequest` is the only authoritative signal for which leg answered, * so select on that and ignore the other leg's leftovers. */ export declare function resolveServedFallbackModel(metadata: { codexFallbackModel?: unknown; vertexFallbackModel?: unknown; }, accountType: string | undefined): string | undefined; declare function clearClaudeFallbackTerminalEvidence(ctx: ServerContext): void; declare function executeClaudeFallbackTranslation(args: { ctx: ServerContext; body: ClaudeRequest; tracer?: ProxyTracer; requestStartTime: number; logProxyBody: ProxyBodyCaptureLogger; logFinalRequest: (status: number, accountLabel: string, accountType: string, errorType?: string, errorMessage?: string, extra?: { inputTokens?: number; outputTokens?: number; cacheCreationTokens?: number; cacheReadTokens?: number; }) => void; options: Parameters<ServerContext["neurolink"]["stream"]>[0]; providerLabel: string; /** * Idle timeout for the fallback stream, in ms. Defaults to * FALLBACK_STREAM_IDLE_TIMEOUT_MS. * * Injectable purely so a test can drive the timeout path in milliseconds * instead of two minutes. The alternative the coverage used before was * patching `globalThis.setTimeout` to fire every timer at 0ms for the * duration of an await — which rewrites the delay of ANY timer created in * that window, not just this one's, inside a 280-case suite sharing a single * process. That is not a hypothetical: an attempt to measure the patched * case with its own `setTimeout`-based watchdog had the watchdog rewritten * out from under it and reported an instant false hang. */ idleTimeoutMs?: number; }): Promise<unknown>; declare function executeClaudeFallbackWithRetry(args: Parameters<typeof executeClaudeFallbackTranslation>[0]): Promise<unknown>; declare function getCodexFallbackInvalidRequestFailure(error: unknown): AnthropicInvalidRequestFailure | null; declare function buildClaudeAnthropicFailureResponse(args: { tracer?: ProxyTracer; requestStartTime: number; authFailureMessage: string | null; authCooldownMessage: string | null; invalidRequestFailure: { status: number; body: string; contentType?: string; } | null; entitlementFailure: AnthropicEntitlementFailure | null; scopedExhaustion: AnthropicScopedExhaustion | null; sawNetworkError: boolean; sawTransientFailure: boolean; sawRateLimit: boolean; lastError: unknown; lastTransportErrorCode?: string; lastTransportScope?: "shared_provider_transport" | "connection_transport"; fallbackFailureMessage?: string; orderedAccounts: ProxyPassthroughAccount[]; buildLoggedClaudeError: ClaudeLoggedErrorBuilder; logProxyBody: ProxyBodyCaptureLogger; logFinalRequest: (status: number, accountLabel: string, accountType: string, errorType?: string, errorMessage?: string, extra?: { inputTokens?: number; outputTokens?: number; cacheCreationTokens?: number; cacheReadTokens?: number; }) => void; }): unknown; declare function handleAnthropicStreamingSuccessResponse(args: { ctx: ServerContext; body: ClaudeRequest; account: ProxyPassthroughAccount; accountState: RuntimeAccountState; response: Response; responseHeaders: Record<string, string>; tracer?: ProxyTracer; requestStartTime: number; fetchStartMs: number; attemptNumber: number; finalBodyStr: string; upstreamSpan?: import("@opentelemetry/api").Span; logAttempt: AnthropicAttemptLogger; logProxyBody: ProxyBodyCaptureLogger; onStreamTerminal?: () => void; logFinalRequest: (status: number, accountLabel: string, accountType: string, errorType?: string, errorMessage?: string, extra?: { inputTokens?: number; outputTokens?: number; cacheCreationTokens?: number; cacheReadTokens?: number; }) => void; }): Promise<AnthropicSuccessResult>; declare function getStreamFailureDetails(outcome: StreamTerminalOutcome): { status: number; errorType: string; message: string; } | undefined; declare function handleAnthropicAuthRetry(args: { ctx: ServerContext; body: ClaudeRequest; account: ProxyPassthroughAccount; accountState: RuntimeAccountState; headers: Record<string, string>; buildUpstreamBody: AnthropicUpstreamBodyBuilder; url: string; enabledAccounts: ProxyPassthroughAccount[]; orderedAccounts: ProxyPassthroughAccount[]; tracer?: ProxyTracer; requestStartTime: number; allocateAttemptNumber: () => number; upstreamSpan?: import("@opentelemetry/api").Span; logAttempt: AnthropicAttemptLogger; logProxyBody: ProxyBodyCaptureLogger; onStreamTerminal?: () => void; logFinalRequest: (status: number, accountLabel: string, accountType: string, errorType?: string, errorMessage?: string, extra?: { inputTokens?: number; outputTokens?: number; cacheCreationTokens?: number; cacheReadTokens?: number; }) => void; lastError: unknown; authFailureMessage: string | null; entitlementFailure: AnthropicEntitlementFailure | null; sawRateLimit: boolean; sawTransientFailure: boolean; sawNetworkError: boolean; bindServedSession?: (servingAccountKey: string) => void; }): Promise<AnthropicAuthRetryResult>; declare function finalizeAnthropicTerminalFetchError(args: { terminalError: NonNullable<AnthropicUpstreamFetchResult["terminalError"]>; account: ProxyPassthroughAccount; tracer?: ProxyTracer; requestStartTime: number; attemptNumber: number; logProxyBody: ProxyBodyCaptureLogger; logFinalRequest: ClaudeFinalRequestLogger; }): Response | unknown; declare function handleAnthropicNonOkResponse(args: { response: Response; account: ProxyPassthroughAccount; accountState: RuntimeAccountState; enabledAccounts: ProxyPassthroughAccount[]; orderedAccounts: ProxyPassthroughAccount[]; tracer?: ProxyTracer; requestStartTime: number; fetchStartMs: number; attemptNumber: number; logAttempt: AnthropicAttemptLogger; logProxyBody: ProxyBodyCaptureLogger; logFinalRequest: (status: number, accountLabel: string, accountType: string, errorType?: string, errorMessage?: string, extra?: { inputTokens?: number; outputTokens?: number; cacheCreationTokens?: number; cacheReadTokens?: number; }) => void; lastError: unknown; authFailureMessage: string | null; sawTransientFailure: boolean; invalidRequestFailure: { status: number; body: string; contentType?: string; } | null; entitlementFailure: AnthropicEntitlementFailure | null; allowConfiguredModelFallback?: boolean; }): Promise<AnthropicNonOkResult>; /** * Detect Anthropic's anti-abuse / request-construction 429. * * The subscription/OAuth path rejects requests it does not recognise as genuine * Claude Code traffic with a 429 `rate_limit_error` whose message is literally * "Error" and which carries NONE of the real rate-limit headers (no retry-after, * no anthropic-ratelimit-*). This is NOT a capacity limit — retrying or rotating * accounts cannot fix it and only burns quota, so the caller must fail fast and * return a non-retryable request error instead of "all accounts rate-limited". */ declare function isAntiAbuseConstruction429(headers: Record<string, string>, body: string): boolean; declare function fetchAnthropicAccountResponse(args: { ctx: ServerContext; url: string; headers: Record<string, string>; finalBodyStr: string; preparedContext: AnthropicUpstreamBody["preparedContext"]; requestedModel?: string; account: ProxyPassthroughAccount; accountState: RuntimeAccountState; enabledAccounts: ProxyPassthroughAccount[]; orderedAccounts: ProxyPassthroughAccount[]; tracer?: ProxyTracer; logAttempt: AnthropicAttemptLogger; logProxyBody: ProxyBodyCaptureLogger; fetchStartMs: number; attemptNumber: number; currentLastError: unknown; currentSawRateLimit: boolean; currentSawNetworkError: boolean; upstreamSpan?: import("@opentelemetry/api").Span; }): Promise<AnthropicUpstreamFetchResult>; declare function shouldAttemptClaudeFallback(loopState: AnthropicLoopState): boolean; /** * Normalise one account's quota for a dashboard consumer. * * `severity` and `isActive` are absent on header-sourced windows — a * structural property of how those rows are parsed, not a transient gap — so * every consumer would otherwise need the same fallback branch. */ declare function normalizeQuotaForAccounts(quota: unknown): JsonObject | null; export declare function createClaudeProxyRoutes(modelRouter?: ModelRouterInterface, basePath?: string, accountStrategy?: "round-robin" | "fill-first", passthroughMode?: boolean, primaryAccountKey?: string, accountAllowlistOrRuntimeOptions?: AccountAllowlist | ClaudeProxyRouteRuntimeOptions): RouteGroup; declare function reconcileEligibleAccountRuntimeState(account: ProxyPassthroughAccount): void; export declare function getTransientSameAccountRetryDelayMs(retryNumber: number): number; export declare function getOverloadRotationDelayMs(attemptNumber: number): number; declare function describeTransportError(error: unknown): string; /** * Whether a transport failure provably happened while connecting, before any * byte of the request left the process. * * Node tags its connect errors with `syscall: "connect"`; with happy-eyeballs * enabled (the default) the error is an AggregateError whose every entry is * one such attempt, and undici wraps either as `fetch failed` with the * original as `cause`. The code alone cannot make this call: an `ETIMEDOUT` * from the connect timer and an `ETIMEDOUT` from a socket that went quiet * after dispatch look identical by code, and only the first is safe to * retry. Both proxies behind one lossy Wi-Fi uplink returned 116 terminal * 502s in a day on exactly that ambiguity. */ declare function isConnectPhaseNetworkError(error: unknown): boolean; /** * Determine whether a POST can be retried without risking duplicate provider * work. Only failures that prove connection establishment did not complete are * safe; a reset, socket error, or response timeout can happen after dispatch. */ declare function isRetryableNetworkError(error: unknown): boolean; /** * Parse a Claude error payload when available. */ export declare function parseClaudeErrorBody(errBody: string): ParsedClaudeError; /** * Detect malformed request errors that should not trigger account/provider failover. */ export declare function isInvalidRequestError(status: number, errBody: string): boolean; /** * A 404 for a retired model can be served by an explicitly configured fallback; * other 404s remain terminal so a bad endpoint or resource is never disguised. */ declare function isAnthropicModelNotFound(status: number, errBody: string): boolean; /** * A subscription-specific beta rejection. Anthropic returns * `400 invalid_request_error` with a message like * "The long context beta is not yet available for this subscription." when an * account's plan tier does not grant an optional beta the proxy injected (see * CLAUDE_CODE_OAUTH_BETAS in anthropicOAuth.ts). Unlike a genuinely malformed * request, the SAME request can succeed on a different account whose tier * grants the beta — so this must be treated as retryable on the next account * rather than a terminal client-facing 400. */ export declare function isSubscriptionBetaRejection(status: number, errBody: string): boolean; /** * An entitlement rejection: Anthropic refuses THIS credential on organization * or plan policy, e.g. `403 permission_error` / * "OAuth authentication is currently not allowed for this organization." * * The taxonomy makes this safe to rotate on: `permission_error` is reserved for * credential/organization permission, distinct from `invalid_request_error` * (request shape) and `not_found_error`, both already terminal above. No * `permission_error` is caused by the request body, so the identical request * can succeed on a different account. * * Deliberately broad — rotation is cheap and reversible. Persisting the block * is gated on the narrower {@link isDurableEntitlementBlock}. */ export declare function isAccountEntitlementError(status: number, errBody: string): boolean; /** * Whether an entitlement rejection is specific enough to disable the account * until someone re-enables it. Narrower than {@link isAccountEntitlementError} * because disabling is sticky and user-visible: an unrecognised * `permission_error` should rotate through the pool and surface a 403, never * durably remove a credential on a guess. */ export declare function isDurableEntitlementBlock(status: number, errBody: string): boolean; /** * Backward-compatible alias — delegates to the shared translation engine. */ export declare const buildProxyFallbackOptions: typeof buildTranslationOptions; /** * Detect transient upstream failures that should trigger account/provider failover. * * Includes Cloudflare 52x statuses and Anthropic 400/api_error wrappers that * carry transient HTML responses (e.g. 520 pages) inside `error.message`. */ export declare function isTransientHttpFailure(status: number, errBody: string): boolean; /** * An upstream overload is already a capacity signal. Retrying it on the same * OAuth account only consumes its remaining concurrency and delays rotation. */ export declare function isUpstreamOverload(status: number, errBody: string): boolean; export declare function redactProviderErrorMessage(message: string): string; export declare const __testHooks: { clearClaudeFallbackTerminalEvidence: typeof clearClaudeFallbackTerminalEvidence; normalizeQuotaForAccounts: typeof normalizeQuotaForAccounts; resolveHomeIndex: typeof resolveHomeIndex; maybeResetPrimaryToHome: typeof maybeResetPrimaryToHome; planCooldownFor429: typeof planCooldownFor429; reconcileCooldownFromQuota: typeof reconcileCooldownFromQuota; refreshAccountLimits: typeof refreshAccountLimits; applyAccountUsageResult: typeof applyAccountUsageResult; clearLimitsRefreshStateForTests: () => void; setAccountDirectoryForTests: (override: ProxyAccountDirectoryOverride | null) => void; isRetryableNetworkError: typeof isRetryableNetworkError; isConnectPhaseNetworkError: typeof isConnectPhaseNetworkError; MAX_CONNECT_PHASE_SAME_ACCOUNT_RETRIES: number; clearProviderTransportCoordinatorForTests: () => void; isPermanentRefreshFailure: typeof isPermanentRefreshFailure; getStreamFailureDetails: typeof getStreamFailureDetails; trackUpstreamReadableStream: typeof trackUpstreamReadableStream; orderAccountsByQuota: typeof orderAccountsByQuota; orderAccountsByQuotaWithMetrics: typeof orderAccountsByQuotaWithMetrics; scheduleAdaptiveQuotaRefreshes: typeof scheduleAdaptiveQuotaRefreshes; scheduleHandoffQuotaRefresh: typeof scheduleHandoffQuotaRefresh; getQuotaRefreshState: (key: string) => import("../../index.js").ProxyQuotaRefreshRuntimeState; buildQuotaRoutingDecision: (accounts: ProxyPassthroughAccount[], now: number, primaryKey: string | undefined, sessionSoftLimit?: number, sessionResetToleranceMs?: number, options?: { requestedModel?: string; policy?: { ranking?: ProxyAccountRankingPolicy; affinityKey?: string; preferPrimary?: boolean; }; }) => ProxyAccountRoutingDecision | undefined; buildRoutingDecision: typeof buildRoutingDecision; resetEpochToMs: typeof resetEpochToMs; seedRuntimeQuotasFromDisk: typeof seedRuntimeQuotasFromDisk; reconcileEligibleAccountRuntimeState: typeof reconcileEligibleAccountRuntimeState; getAccountRuntimeState: (key: string) => RuntimeAccountState | undefined; setPrimaryAccountIndex: (index: number) => void; getPrimaryAccountIndex: () => number; setAccountRuntimeState: (key: string, state: Partial<RuntimeAccountState>) => void; resetAllRuntimeState: () => void; polyfillOAuthBody: (bodyStr: string, isClaudeClientRequest: boolean) => { bodyStr: string; sessionId?: string; }; extractSnapshotBodySessionIdForTests: (body: unknown) => string | undefined; selectClaudeProxyAccountOrderForTests: typeof selectClaudeProxyAccountOrder; setForceSpillThrowForTests: (value: boolean) => void; setForceAffinityLookupThrowForTests: (value: boolean) => void; setForceBindRecheckThrowForTests: (value: boolean) => void; isAntiAbuseConstruction429: typeof isAntiAbuseConstruction429; fetchAnthropicAccountResponse: typeof fetchAnthropicAccountResponse; finalizeAnthropicTerminalFetchError: typeof finalizeAnthropicTerminalFetchError; handleAnthropicNonOkResponse: typeof handleAnthropicNonOkResponse; handleAnthropicAuthRetry: typeof handleAnthropicAuthRetry; handleAnthropicStreamingSuccessResponse: typeof handleAnthropicStreamingSuccessResponse; claimTransientRateLimitRetry: typeof claimTransientRateLimitRetry; claimTransientCooldownAdmission: typeof claimTransientCooldownAdmission; waitForTransientAccountAvailability: typeof waitForTransientAccountAvailability; acquireAccountAdmission: typeof acquireAccountAdmission; acquireFirstAvailableAccountAdmission: typeof acquireFirstAvailableAccountAdmission; tryAcquireAccountAdmission: typeof tryAcquireAccountAdmission; enqueueAccountAdmission: typeof enqueueAccountAdmission; getAccountInflight: typeof getAccountInflight; getAccountAdmissionSnapshot: (accountKey: string) => { active: number; waiting: number; }; hasAccountAdmissionState: (accountKey: string) => boolean; describeTransportError: typeof describeTransportError; redactProviderErrorMessage: typeof redactProviderErrorMessage; isUpstreamOverload: typeof isUpstreamOverload; getOverloadRotationDelayMs: typeof getOverloadRotationDelayMs; redactHeadersForBorrower: typeof redactHeadersForBorrower; shouldAttemptClaudeFallback: typeof shouldAttemptClaudeFallback; isAnthropicModelNotFound: typeof isAnthropicModelNotFound; getCodexFallbackInvalidRequestFailure: typeof getCodexFallbackInvalidRequestFailure; executeClaudeFallbackWithRetry: typeof executeClaudeFallbackWithRetry; buildClaudeAnthropicFailureResponse: typeof buildClaudeAnthropicFailureResponse; isAccountEntitlementError: typeof isAccountEntitlementError; isDurableEntitlementBlock: typeof isDurableEntitlementBlock; evaluateScopedExhaustion: typeof evaluateScopedExhaustion; matchScopedQuotaWindow: typeof matchScopedQuotaWindow; setOveragePolicy: typeof setOveragePolicy; }; export {};