/** * MODULE CHARTER: Pure Credential Selector & Attempt Planner (credential-select.ts) * * 1. Domain Boundary & Responsibilities: * - Implements deterministic, side-effect-free credential selection across provider fleets. * - Encapsulates LRU tracking, credential health assessment, and quota-driven slot prioritization. * - Isolates credential secrets from selection algorithms by operating exclusively on non-secret `CredentialId`s. * * 2. `AttemptPlan` Contract & Ranking Invariants: * - `groupCredentialAttempts()` partitions candidates into discrete attempts ordered by availability and cost. * - Prioritizes healthy, non-cooling slots with available quota before falling back to degraded or untested slots. * - Applies LRU tie-breaking via `CredentialLru` to evenly distribute requests across equal-priority credentials. * * 3. Look-Ahead Re-offering & Cooldown Rules: * - Credential slots marked saturated or cooling are suppressed until their backoff or reset periods expire. * - Re-offering logic evaluates timestamps against `now` without mutating global clock state. * - Permanent authentication failures (`credentialFault`) are excluded until explicitly cleared or rotated. * * 4. Learned Target Facts & Evidence: * - Integrates observed rate limits and quota headers from `target-facts.ts` into live routing decisions. * - Evidence lookup supports fine-grained attempt-level matching (provider × model × credential). */ import type { ResolvedAttempt } from "./resolved-attempt.js"; import type { CredentialId } from "./credential-id.js"; import type { FactScope } from "./target-facts.js"; import type { QuotaObservation } from "./quota-observation.js"; /** A non-secret learned fact, already materialized for the candidate being routed. */ export interface CredentialFact { readonly kind: string; readonly scope: FactScope; } /** Raw live dimensions used by the pure selector. All fields are optional and neutral when absent. */ export interface CredentialSelectionEvidence { readonly facts?: readonly CredentialFact[]; readonly health?: "healthy" | "degraded" | "unhealthy" | "unknown"; readonly credentialFault?: boolean; readonly cooling?: boolean; readonly saturated?: boolean; readonly quota?: readonly QuotaObservation[]; readonly cost?: "free" | "paid" | "unknown"; } export interface CredentialSelectionOptions { readonly evidence?: ReadonlyMap; /** Exact credential/model-cell evidence. Takes precedence over the legacy credential map. */ readonly evidenceFor?: (attempt: ResolvedAttempt) => CredentialSelectionEvidence | undefined; readonly now?: number; /** Age after which a provider-stated quota observation is no longer fresh. */ readonly quotaFreshnessMs?: number; } /** LRU state is deliberately credential-wide, not provider/model-wide. */ export declare class CredentialLru { #private; /** Called only when a backend egress is about to start. */ touch(credentialId: CredentialId): void; lastUsed(credentialId: CredentialId): number | undefined; /** Lower values sort first: unseen, then least recently used. */ rank(credentialId: CredentialId): number; snapshot(): ReadonlyMap; } /** Rank attempts without mutating LRU state or reading/logging secret material. */ export declare function rankCredentialAttempts(attempts: readonly ResolvedAttempt[], lru?: CredentialLru, options?: CredentialSelectionOptions): ResolvedAttempt[]; export interface DeploymentCredentialGroup { readonly key: string; readonly attempts: readonly ResolvedAttempt[]; } /** Group by deployment while retaining the first-seen target order and slot order. */ export declare function groupCredentialAttempts(attempts: readonly ResolvedAttempt[]): DeploymentCredentialGroup[]; export type CredentialWalkOutcome = { readonly status?: number; readonly kind?: "success" | "credential" | "deployment" | "provider-transport" | "timeout" | "protocol" | "unknown-refusal" | "local" | "client" | "cancelled"; /** An accepted fact scope overrides the generic status scope. */ readonly scope?: FactScope; }; export interface CredentialWalkOptions extends Omit { readonly lru?: CredentialLru; readonly walkBudgetMs?: number; readonly now?: () => number; readonly selectionNow?: number; readonly suppressedFacts?: readonly CredentialFact[]; /** * How many attempts may be in flight at once. **Defaults to 1**, which is the walk's historical * behaviour exactly — one offered candidate, re-offered until it is recorded. * * ⚠ Above 1 is what makes a HEDGE possible: the walk can offer the next candidate while the * previous one is still running. It changes nothing on its own — the caller still decides whether * to ask — so a caller that never asks for a second candidate sees no difference at all. */ readonly maxInFlight?: number; } export interface CredentialWalkStats { readonly started: number; readonly skipped: number; readonly stopped: boolean; } /** * An immutable attempt execution plan held in-flight by the candidate walk. */ export interface AttemptPlan { readonly attempt: ResolvedAttempt; readonly group: DeploymentCredentialGroup; readonly started: boolean; } /** * Request-local breadth-first credential walk. `next()` only offers a candidate. The caller must * call `recordStarted()` immediately before fetch/egress; that is the sole budget/LRU mutation * boundary. Pre-egress validation can call `recordRejected()` and consumes no start budget. */ export declare class CredentialWalk { #private; constructor(attempts: readonly ResolvedAttempt[], options?: CredentialWalkOptions); get stats(): CredentialWalkStats; /** * The OLDEST attempt still in flight, or undefined. * * ⚠ Kept as a single-value getter because that is what callers already test against * (`walk.pending === resolvedAttempt`). At `maxInFlight` 1 it is exactly what it always was. Use * `isPending` when more than one may be live, or the check silently only ever asks about the * first. */ get pending(): ResolvedAttempt | undefined; /** Is this exact attempt still in flight? The multi-attempt form of the `pending` check. */ isPending(attempt: ResolvedAttempt): boolean; next(): ResolvedAttempt | undefined; /** Mark the offered candidate as crossing the real backend-start boundary. */ recordStarted(attempt: ResolvedAttempt): void; /** Discard an offered candidate after local/pre-egress validation; no LRU or budget mutation. */ recordRejected(attempt: ResolvedAttempt): void; /** * Retire a HEDGE LOSER: an attempt this relay aborted because another one won. * * ⚠ **It deliberately does NOT go through `record`, and that is the second blocker hedging hit.** * `record` treats a `cancelled` outcome as terminal and sets `#stopped`, which is right for a * client hanging up and catastrophically wrong here — it would end the walk for a request the * hedge just rescued. An abandoned attempt proved NOTHING about its deployment, so it also must * not suppress a credential, close a deployment, or record an outcome of any kind. * * ⚠ The deployment goes BACK ON THE QUEUE. Burning it would silently shrink the candidate pool * on every hedged request, which is the opposite of what hedging is for. The start budget is * NOT refunded: the attempt really was started, and pretending otherwise would let concurrency * buy more of the walk budget than a serial walk could spend. */ recordAbandoned(attempt: ResolvedAttempt): void; record(attempt: ResolvedAttempt, outcome: CredentialWalkOutcome): void; }