/** * MODULE CHARTER: Candidate Walk & Hedged Execution Engine (candidate-runner.ts) * * 1. Domain Boundary & Responsibilities: * - Executes failover candidate walks across prioritized models, provider deployments, and credential slots. * - Manages speculative hedging races to minimize tail latency across unreliable or slow free upstream tiers. * - Enforces protocol dialect translation, response streaming, and error classification across heterogeneous endpoints. * * 2. Candidate Walk Semantics & Ranking Order: * - Invariant ordering: Preserves dynamic pool benchmark rank and operator-configured primary/fallback lists. * - Runtime filters: Excludes circuit-broken deployments, cooling credentials, and cost-blocked candidates. * - Failover: Progresses sequentially across candidates upon 429 rate limits, 5xx server errors, or auth failures, * terminating early on non-retryable client errors (e.g., 400 Bad Request with invalid schema). * * 3. Hedging Race Semantics: * - Speculatively launches a secondary attempt if the primary attempt exceeds the configured hedge latency delay. * - The first attempt to return valid response headers wins the race and streams its body directly to the client. * - The losing or trailing attempt is promptly aborted via `AbortController` to conserve upstream bandwidth and quota. * * 4. Attribution & Transparency Invariants: * - Emits rich observability headers (`x-llm-relay-credential`, `x-llm-relay-pool-attempts`, `x-llm-relay-hedged`). * - Accurately classifies failure origins (`errorOrigin`) and distinguishes pre-header vs post-header body truncation. */ import type { IncomingMessage, ServerResponse } from "node:http"; import type { Config, ResolvedTarget } from "./config.js"; import type { ProbationConfig } from "./config-types.js"; import type { ModelLimits } from "./catalog.js"; import { type ErrorOrigin, type PostHeaderBodyFailure } from "./backend.js"; import { type StickySessionManager } from "./session-pin.js"; import { type StreamCommitProbe, type StreamCommitProtocol } from "./stream-commit.js"; import { type MetadataLogger, type RequestAttemptLog, type RequestAttemptStatus, type RequestLog } from "./log.js"; import type { ModelCallRecorder, ProxyAccountingFailureKind, RequestAccountingState } from "./accounting-state.js"; import { CircuitBreaker } from "./circuit-breaker.js"; import { type ResolvedAttempt } from "./resolved-attempt.js"; import { CredentialWalk, type CredentialWalkOutcome } from "./credential-select.js"; import { type UsageAccumulator } from "./usage-observer.js"; import type { AccountingAttempt } from "./accounting.js"; import { type CostClass } from "./metadata.js"; import { type QuotaObservation } from "./quota-observation.js"; import { type FactResetBasis } from "./target-facts.js"; import { type QuotaDemotionFn } from "./quota-demotion.js"; import { type LatencyDemotionFn } from "./latency-demotion.js"; import { type PacingFn } from "./pacing.js"; import type { HedgeDelayDecision } from "./hedge-trigger.js"; import { type Settled } from "./hedge-race.js"; import { type HardCapVerdict } from "./hard-cap.js"; import { materializeScope, type Interpretation } from "./refusal-interpretation.js"; import type { AttemptFailed, AttemptHandle, OutcomeProvenance, ProviderTargetIdentity } from "./kernel/contracts.js"; export declare function toolUseIdRewriteField(source: Response): { toolUseIdRewrites?: number; toolCallIdRewrites?: number; thoughtSignatureSentinels?: number; }; /** A provider declared an authEnv whose variable is unset — a configuration error, not a passthrough. */ export declare class CredentialConfigError extends Error { constructor(provider: string, authEnv: string); } export declare function buildForwardHeaders(inbound: IncomingMessage["headers"], attempt: ResolvedAttempt): Record; export type CostClassFn = (attempt: ResolvedAttempt) => CostClass | undefined; export interface StickyRequestContext { key: string; multiCandidateRoute: boolean; /** The previously stored pin's routing evaluation; null means a new pin may be created. */ provenance: string | null; } /** * The walk's health bands, best first: `probation` → `live` → `slow` → `paced` → * `credential-fault` → `cooling`. `paced` (2026-09-15, `pacing.ts`) sits behind `slow` because a * slow member most likely answers while one at its stated ceiling most likely 429s — and ahead of * the failure bands because it is healthy, merely full for the moment. */ export type TargetUsability = "live" | "slow" | "paced" | "credential-fault" | "cooling" | "probation"; type OutcomeClass = "ok" | "retriable" | "credential" | "client"; export declare const DEFAULT_WALK_BUDGET_MS = 45000; export declare const DEFAULT_STALL_TIMEOUT_MS = 90000; export interface CandidateRunnerHandlers { breaker: CircuitBreaker; logger: MetadataLogger; hardCap: (attempt: ResolvedAttempt, now: number) => HardCapVerdict | null; hedgeDelay: (attempt: ResolvedAttempt, estimatedInputTokens: number) => HedgeDelayDecision | null; modelCallRecorder?: ModelCallRecorder; stickySessions?: StickySessionManager; /** * Untested-free-members-first probation (`routing.probation`, default ON). Optional so * hand-built handler literals in tests keep compiling — absent reads as "band unreachable", * the pre-probation behaviour. The request path always sets it (see `createProxy`); a call * site that omits it silently disables the band there. */ probation?: ProbationFn | null; /** * Evidence that a provider's own roster has moved: a 404 stating that a model THIS RELAY LISTS * does not exist. The request path wires it to `ModelCatalog.noteProviderStale`, which re-fetches * that provider's `/models` behind a per-provider cooldown. * * Optional, like `probation` and for the same reason — hand-built handler literals in tests keep * compiling — and absent reads as "no trigger", which is the pre-existing behaviour: the catalog * waits for its TTL. `createProxy` always sets it. * * ⚠ The handler is handed the attempt, not a provider name, so the containment the trigger needs * — "on a model the catalog currently lists" — is decided where the catalog is, and not by a * caller that would have to re-derive it. */ catalogStale?: ((attempt: ResolvedAttempt) => void) | null; } interface ServedAnnouncementContext { readonly target: ResolvedTarget; readonly retryAfterOverrideMs?: number | null | undefined; readonly poolSummary?: string | null | undefined; readonly tried?: readonly string[] | undefined; readonly poolUnknownRefusals?: number | null | undefined; readonly credentialHeaders?: Record | undefined; readonly degraded?: string | null | undefined; readonly quotaDemoted?: string | null | undefined; readonly latencyDemoted?: string | null | undefined; readonly paced?: string | null | undefined; readonly probation?: string | null | undefined; readonly hedged?: string | null | undefined; readonly paid?: string | null | undefined; readonly sticky?: StickyRequestContext | null | undefined; } export declare function responseHeadersForTarget(backendRes: Response, ctx: ServedAnnouncementContext): Record; export declare function handleMidStreamError(res: ServerResponse, e: unknown, started: number, path: string, hadTools: boolean, streamed: boolean, backendStatus: number, target: ResolvedTarget, attempt: HealthAttempt | undefined, h: { logger: MetadataLogger; breaker: CircuitBreaker; }, errorFrameBuilder: (msg: string) => string | null, reportedModelSource?: Response, deadlineAborted?: boolean, committed?: boolean, signal?: AbortSignal): void; export declare function paidLabel(cfg: Config, h: { catalog: { cachedLimits: (p: string, m: string) => ModelLimits | null | undefined; }; }, target: ResolvedTarget): string | null; export declare function degradedLabel(pool: string | null, degraded: Set | null, target: ResolvedTarget): string | null; /** * Minimum SERVED-REQUEST samples before a free deployment counts as measured. * * Five is the smallest count for which a sample window is not simply "the last couple of * requests" — the same standing rule that keeps `latency-demotion.ts` from acting on one * request's latency. */ export declare const DEFAULT_PROBATION_MIN_SAMPLES = 5; /** One probation verdict — the smallest honest statement of "why this cell leads the walk". */ export interface ProbationVerdict { /** Served-request samples behind the verdict. Always below `minSamples`. */ readonly samples: number; /** The floor it has not reached yet. */ readonly minSamples: number; } export type ProbationFn = (attempt: ResolvedAttempt, now: number) => ProbationVerdict | null; /** * How the probation check reads its two facts. * * ⚠ A plain reader, NOT the probe cache. Keeping the seam narrow is what lets this check stay * pure and testable — the server passes `countRequestSamples` from `ping/probe-cache.ts`, the * suite passes a stub. Probe samples never reach the reader's answer: the count is * served-request samples only, which is what distinguishes "this deployment served traffic" * from "a probe found it alive". */ export interface ProbationDeps { readonly readRequestSamples: (provider: string, model: string) => number; /** * ⚠ The SHAPE is owned by `config-types.ts` (`ProbationConfig`) and imported, never * re-declared here — the same rule `latency-demotion.ts` follows for its own settings. * `config/routing-parser.ts` normalizes the boolean shorthand away, so this check never has * to decide what `false` means. */ readonly settings?: ProbationConfig | undefined; /** Free-ness comes from the caller's cost assessment (`assessCost`), never a second opinion. */ readonly costClassOf?: CostClassFn | null | undefined; } /** Resolve the knobs once. Absent, or an empty object, means every default — which is ON. */ export declare function resolveProbationSettings(settings: ProbationConfig | undefined): { enabled: boolean; minSamples: number; }; export declare function resolveProbation(deps: ProbationDeps, attempt: ResolvedAttempt): ProbationVerdict | null; /** * Build the request-path evaluator. The wrapper is the safety seam: NOTHING inside may throw * into the request path, and a failure degrades to "no opinion" — the pre-probation behaviour * — rather than to a refused request. Deliberately silent: routing hints are not log-worthy * events. */ export declare function createProbationFn(deps: ProbationDeps): ProbationFn; /** * `" (0 of 5 request samples)"` — bounded, metadata only: a spec and a count, never a * prompt, a credential or an id. */ export declare function probationLabel(spec: string, verdict: ProbationVerdict): string; /** * The probation announcement for the candidate that actually SERVED the response, or null. * * ⚠ Evaluated against the SERVING attempt at response time, not against the walk leader at * routing time: a probation leader that 429s fails over to a live member, and that response * must NOT carry the header — its serving candidate was never placed by the band. Both fronts * call this with their serving attempt and hand the string to `ServedAnnouncementContext`; * the header itself is written only by `responseHeadersForTarget`, the one owner. */ export declare function probationLabelForAttempt(h: { probation?: ProbationFn | null; }, attempt: ResolvedAttempt, now: number): string | null; export declare function targetUsability(attempt: ResolvedAttempt, breaker: CircuitBreaker, now: number, quotaDemotion?: QuotaDemotionFn | null, costClassOf?: CostClassFn | null, latencyDemotion?: LatencyDemotionFn | null, probation?: ProbationFn | null, pacing?: PacingFn | null): TargetUsability; export declare function expandCredentialAttempts(targets: readonly ResolvedTarget[]): ResolvedAttempt[]; export declare function credentialEvidence(attempt: ResolvedAttempt, cfg: Config, breaker: CircuitBreaker, now: number): { facts: { kind: import("./target-facts.js").FactKind; scope: import("./target-facts.js").FactScope; until: number; untilBasis?: FactResetBasis; value?: number; }[]; health: "unhealthy" | "unknown"; credentialFault: boolean; cooling: boolean; saturated: boolean; quota: QuotaObservation[]; cost: "free" | "paid" | "unknown"; }; export declare function orderDeploymentGroupsByUsability(attempts: readonly ResolvedAttempt[], breaker: CircuitBreaker, now: number, quotaDemotion?: QuotaDemotionFn | null, costClassOf?: CostClassFn | null, latencyDemotion?: LatencyDemotionFn | null, probation?: ProbationFn | null, pacing?: PacingFn | null): { ordered: ResolvedAttempt[]; quotaDemotedFirst: string | null; latencyDemotedFirst: string | null; pacedFirst: string | null; }; export declare class CredentialAttemptTrace { private readonly cfg; private readonly entries; constructor(cfg: Config); recordStarted(attempt: ResolvedAttempt): void; record(attempt: ResolvedAttempt, outcome: CredentialWalkOutcome): void; headers(servedAttempt?: ResolvedAttempt): Record; } export declare function recordCredentialStarted(walk: CredentialWalk, trace: CredentialAttemptTrace, attempt: ResolvedAttempt): void; export declare function recordCredentialOutcome(walk: CredentialWalk, trace: CredentialAttemptTrace, attempt: ResolvedAttempt, outcome: CredentialWalkOutcome): void; export declare function nextUncappedAttempt(h: { hardCap: (attempt: ResolvedAttempt, now: number) => HardCapVerdict | null; }, walk: CredentialWalk, attemptTrace: RequestAttemptTrace, tracker: Pool429Tracker): ResolvedAttempt | undefined; export interface AttemptRun { readonly resolvedAttempt: ResolvedAttempt; readonly target: ResolvedTarget; readonly controller: AbortController; readonly callerController: AbortController; readonly timer: ReturnType; readonly onResClose: () => void; readonly usage: UsageAccumulator; attempt: HealthAttempt | undefined; egressCallbackCalled: boolean; /** * The `fetch` this attempt's egress MUST use — both fronts' `startAttempt` pass this as * `fetchBackend`'s / `fetchOpenAiFront`'s third argument. Plain `fetch` unless a first-byte * deadline is armed below, in which case it wraps `fetch` to clear `firstByteTimer` the instant * the underlying HTTP call resolves — headers arrived — which is BEFORE `fetchBackend()` / * `fetchOpenAiFront()` finish reading a buffered body or preflighting a stream. That earlier * moment is the only place "first byte" can be observed; the OUTER promise those two functions * return already reflects the full non-streamed body read. */ fetchFn: typeof fetch; /** * Armed only for a non-streamed attempt whose target resolved a `firstByteTimeoutMs` * (`ResolvedTarget.firstByteTimeoutMs`) — never for a streamed one, which already has * `stallTimeoutMs`'s inter-byte watchdog once its own head is being served. Cleared by * `fetchFn` above the moment the raw `fetch()` resolves; the total `timer` above then governs * the body read exactly as before this existed. `undefined` when no first-byte deadline applies. */ firstByteTimer?: ReturnType | undefined; } export declare function beginAttemptRun(res: ServerResponse, offer: ResolvedAttempt, wantsStream: boolean, trace: RequestAttemptTrace): AttemptRun; export declare function releaseAttemptRun(res: ServerResponse, run: AttemptRun): void; interface StartedAttempt { readonly run: AttemptRun; readonly promise: Promise; } interface HedgedAttemptDeps { readonly h: CandidateRunnerHandlers; readonly res: ServerResponse; readonly walk: CredentialWalk; readonly credentialTrace: CredentialAttemptTrace; readonly attemptTrace: RequestAttemptTrace; readonly tracker: Pool429Tracker; /** The relay's own chars/4 estimate of THIS request's input size — see `hedge-trigger.ts`. */ readonly estimatedInputTokens: number; startRun(offer: ResolvedAttempt): StartedAttempt | undefined; } interface HedgedAttemptResult { readonly run: AttemptRun; readonly settled: Settled; readonly hedged: string | null; } interface CommitProbeOptions { readonly protocol: StreamCommitProtocol; /** Client cancellation wins races with EOF/read failures and must never start another target. */ readonly isCancelled: () => boolean; /** Malformed final wire produced by a response mapper is a local defect, not target health. */ readonly malformedProvenance: "upstream" | "local"; } /** * Run the stream-commit probe INSIDE the attempt's own promise, so the hedge race settles at * COMMIT — the first meaningful content — rather than at header arrival (2026-09-04, owner * direction: the hedge exists for wedged requests, and a provider that answers 200 + headers at * once and then produces nothing is the common wedge; a race decided at RESPONSE RESOLUTION had * already called that primary the winner, and `hedge-race.ts` recorded the gap in as many words). * A non-streamed or non-2xx response passes through untouched — the walk's own status * classification decides those. The verdict rides beside the response for the route to consume * through `takeCommitProbe`; the route keeps its inline probe only as the fallback for a response * that did not come through this wrapper. * * ⚠ The probe reads the body up to the first meaningful event and the `ready` verdict replays * every byte it consumed, so a committed winner is served byte-exact as before. A LOSER's probe is * left to settle on its own after `retireHedgeLoser` aborts its run — the race ignores a late * settlement — and that abort is what cancels its body. */ export declare function withCommitProbe(promise: Promise, options: CommitProbeOptions): Promise; /** The verdict `withCommitProbe` attached, removed on read so it is consumed exactly once. */ export declare function takeCommitProbe(response: Response): StreamCommitProbe | undefined; /** * Has this settlement WON the race? Two questions, one policy. The walk would not fail over from * the status — a 429 has not won, it was going to be walked past anyway — AND a streamed response * has COMMITTED: a probe that found the stream dead, or the client gone, has not won either, and * the walk moves on from it exactly as it does from a failing status. A response the probe never * touched — buffered, or not a stream — is judged on its status alone, as before. */ export declare function attemptWon(settled: Settled): boolean; /** * ⚠ **Carries the input-token count only when `basis` is `"input-size"`** — the case the flat * `floor` label used to cover alone. A `per-token`/`absolute` decision keeps its bare basis: the * DEPLOYMENT's own evidence set that bar, not the request's size, and stating a token count beside * it would claim size decided a number the deployment actually did. */ export declare function hedgedLabel(primary: AttemptRun, hedge: AttemptRun, winner: "primary" | "hedge", decision: HedgeDelayDecision): string; export declare function retireHedgeLoser(deps: HedgedAttemptDeps, loser: AttemptRun): void; export declare function runAttemptWithHedge(primary: StartedAttempt, deps: HedgedAttemptDeps): Promise; type WalkExitKind = "transport" | "post-header-body-failure" | "dead-stream"; interface WalkExitData { kind: WalkExitKind; message: string; errorType?: string | undefined; errorOrigin?: ErrorOrigin; servedBy?: string; shouldTryNext?: boolean; /** * The dead stream's classified cause, when the backend stated one — see `stream-commit.ts` * `stopCauseToken`. The WIRE half is already carried by `message`; this is the half that reaches * the metadata log, and it is an enum-like token rather than any part of the backend's words. */ streamStopCause?: string | undefined; } export declare function walkExitHeaders(tracker: Pool429Tracker, sticky: StickyRequestContext | null | undefined, credentialTrace: CredentialAttemptTrace, { errorOrigin, errorType, servedBy, }?: Pick): Record; export declare function endWalk(h: { hardCap: (attempt: ResolvedAttempt, now: number) => HardCapVerdict | null; logger: MetadataLogger; }, res: ServerResponse, front: "anthropic" | "openai", walk: CredentialWalk, attemptTrace: RequestAttemptTrace, tracker: Pool429Tracker, status: number, sticky: StickyRequestContext | null | undefined, credentialTrace: CredentialAttemptTrace, log: () => RequestLog, exit: WalkExitData): boolean; export declare function respondAllCapped(res: ServerResponse, h: { logger: MetadataLogger; }, ctx: { started: number; path: string; hadTools: boolean; streamed: boolean; }, front: "anthropic" | "openai", tracker: Pool429Tracker, attempts: readonly RequestAttemptLog[]): void; export declare function applyStickyOrdering(ordered: ResolvedAttempt[], pinnedSpec: string, breaker: CircuitBreaker, degraded: Set | null, now: number, quotaDemotion?: QuotaDemotionFn | null, costClassOf?: CostClassFn | null, latencyDemotion?: LatencyDemotionFn | null, probation?: ProbationFn | null, pacing?: PacingFn | null): { targets: ResolvedAttempt[]; status: string; }; export declare function stickyProvenanceHeaders(sticky: StickyRequestContext | null | undefined): Record | undefined; export declare function recordStickySuccess(h: { stickySessions?: StickySessionManager; }, sticky: StickyRequestContext | null | undefined, target: ResolvedTarget, status: number): void; /** * Demote unusable candidates — and do NOTHING else to the order. * * ⚠ Deliberately NOT `getHealthyTargets()`: that filters AND re-sorts by measured stability, which * is a second ranking pass competing with the deployment-fitness ranking `resolveTargets` already * applied. Two ranking passes means neither decides the order, and live health then PROMOTES on * evidence that is often a single request's latency. Health is used here only to demote, never to * promote: a target the breaker is cooling steps aside, everything else keeps its fitness order. * (The re-sort was invisible for as long as an untracked target scored a flat 100 and * `Array.prototype.sort` is stable — INV-TS-7.) * * ⚠⚠ **AMENDED BY OWNER DECISION 2026-08-30, and this is NOT drift — do not "restore" it.** * Latency now DOES demote, via `latency-demotion.ts` folded into `targetUsability` beside the quota * term. The paragraph above stays because every word of it is still the constraint: what was * rejected is a second ranking PASS that re-sorts and can PROMOTE on one request's latency, and * that remains rejected. A one-way demotion term is a different thing — it never re-sorts, never * promotes, and cannot fire on a single sample (p95 over a minimum count of MEASURABLE samples, * unmeasured having no effect at all). * * What forced the amendment: measured 2026-08-30, banding on breaker state ALONE walked a * breaker-CLOSED member with a p95 of 70364 ms ahead of every cooling one, and single requests cost * 120-123 s across 2-6 attempts. * * ⚠⚠ **AMENDED AGAIN BY OWNER DIRECTION 2026-09-09, same standing — do not "restore" it.** A * free deployment with fewer than `minSamples` served-request samples now LEADS, in a * `probation` band AHEAD of `live` (config order within the band), so one untested member at a * time gathers data and leaves the band by itself as its request samples accumulate. Like the * 2026-08-30 term this is a one-way placement, not a second ranking pass: fitness still decides * the order everywhere else, nothing is dropped, and an unmeasured free primary is already * hedged (`hedge-trigger.ts`: unmeasured IS hedged), so a probation member that hangs costs one * hedge, not a timeout — no second mechanism is added here. */ export declare function orderByUsability(attempts: ResolvedAttempt[], breaker?: CircuitBreaker, now?: number, quotaDemotion?: QuotaDemotionFn | null, costClassOf?: CostClassFn | null, latencyDemotion?: LatencyDemotionFn | null, probation?: ProbationFn | null, pacing?: PacingFn | null): ResolvedAttempt[]; export declare function orderByUsabilityTracked(attempts: ResolvedAttempt[], breaker: CircuitBreaker, now: number, quotaDemotion?: QuotaDemotionFn | null, costClassOf?: CostClassFn | null, latencyDemotion?: LatencyDemotionFn | null, probation?: ProbationFn | null, pacing?: PacingFn | null): { ordered: ResolvedAttempt[]; quotaDemotedFirst: string | null; latencyDemotedFirst: string | null; pacedFirst: string | null; }; /** What one HTTP status means to the walk: how to classify the outcome, and whether the body may * carry an eligibility fact worth interpreting. */ interface StatusVerdict { readonly outcome: OutcomeClass; readonly carriesEligibilityFact: boolean; } /** * The statuses this relay has a specific opinion about — SEM-04 in the 2026-09-05 duplication * catalog, REFINE ("outcome-class table only") in the adversarial verification. * * `classifyStatus` and `carriesEligibilityFact` each carried their own membership list, and the * two lists were the same seven statuses written twice. They read this table now, so a status * cannot be retriable in one and eligibility-bearing in neither. * * ⚠ It is deliberately NOT the whole classification. HTTP status is an unbounded integer domain, * not a closed union, so the two RANGE rules — under 400, and 500 and above — stay in * `statusVerdict` below where a table cannot express them. * * ⚠ Membership here is policy. Do not add or move a row as part of a mechanical change; every * entry is a decision about failover, and `CLAUDE.md` records why 402 sits with the retriable * statuses rather than the client ones (on the free providers this proxy fronts it means * depleted credits, not a malformed request). */ export declare const STATUS_VERDICT_TABLE: Readonly>; /** * The one reading of an HTTP status both classifiers now share. * * ⚠ An unlisted 4xx falls to `client` — the WEAKER claim, meaning the walk does NOT fail over. * That direction is the safe one for a status nobody has reasoned about, and it is what both * hand-written chains already did. */ export declare function statusVerdict(status: number): StatusVerdict; export declare function classifyStatus(status: number): OutcomeClass; export declare function shouldTryNext(cls: OutcomeClass): boolean; export declare class Pool429Tracker { private minRetryAfterMs; private only429; private readonly counts; private readonly firstSeenAt; private order; private inFlight; private deferredCapped; private readonly cappedLabels; private soonestCapResetAt; private egressed; noteEgress(): void; noteOffered(): void; private stamp; private stampCapped; recordFailover(status: number, retryAfterMs: number | null): void; recordFinal(status: number): void; recordCapped(label: string, resetsAt: number): void; cappedResetAt(): number | null; allCapped(): boolean; cappedSummary(): string | null; recordDeadTurn(): void; private unknownRefusals; noteUnknownRefusal(): void; unknownCount(): number | null; private count; summary(): string | null; overrideMs(finalStatus: number, finalRetryAfterMs: number | null): number | undefined; } export declare class RequestAttemptTrace { private readonly entries; private readonly diagnosticKinds; recordFirstByteTimeout(): void; withDiagnostics(record: RequestLog): RequestLog; record(target: ResolvedTarget, status: RequestAttemptStatus, started: number, completedAt: number): void; recordCapped(target: ResolvedTarget, at: number): void; snapshot(): RequestAttemptLog[]; } export interface HealthAttempt { readonly handle: AttemptHandle; readonly identity: ProviderTargetIdentity; readonly resolvedAttempt: ResolvedAttempt; readonly target: ResolvedTarget; readonly started: number; readonly trace: RequestAttemptTrace; readonly usage: UsageAccumulator; readonly accounting: RequestAccountingState | null; accountingAttempt: AccountingAttempt | null; completed: boolean; committed: boolean; terminal?: "succeeded" | "failed" | "cancelled"; } export declare function targetIdentity(attempt: ResolvedAttempt): ProviderTargetIdentity; /** * Begin the breaker attempt for one egress. `estimatedInputTokens` is REQUIRED, not optional, so * the compiler enumerates both fronts' call sites: it feeds the cell's attempt-start log that * `pacing.ts` counts token windows over, and an optional parameter would let one front silently * pace on requests alone — the one-front gap this repo's history warns about most. */ export declare function beginHealthAttempt(h: { breaker: CircuitBreaker; }, resolvedAttempt: ResolvedAttempt, started: number, trace: RequestAttemptTrace, usage: UsageAccumulator, accounting: RequestAccountingState | null, estimatedInputTokens: number): HealthAttempt | null; type EligibilityObservation = { readonly unknown: boolean; readonly scope?: ReturnType; }; export declare function observeEligibility(attempt: ResolvedAttempt, status: number, retryAfterMs: number | null, body: string, h?: Pick): EligibilityObservation; export declare function freeOnlyApplies(rule: { freeOnly?: boolean; }, rerouted: boolean): boolean; export declare function resolveReset(interpretation: Interpretation, headerMs: number | null, body: string): { ms: number; basis: FactResetBasis; } | null; export declare function carriesEligibilityFact(status: number): boolean; type InspectedCandidateResponse = { kind: "response"; response: Response; eligibility: EligibilityObservation; } | PostHeaderBodyFailure; export declare function inspectCandidateResponse(res: Response, attempt: ResolvedAttempt, retryAfterMs: number | null, h?: Pick): Promise; export declare function walkOutcomeForResponse(status: number, localFailure: boolean, scope?: EligibilityObservation["scope"]): CredentialWalkOutcome; export declare function observeAttemptHeaders(h: { breaker: CircuitBreaker; }, attempt: HealthAttempt, status: number, retryAfterMs: number | null, headers?: Headers): void; export declare function accountingFailureForAttempt(options: { readonly failure: AttemptFailed["failure"]; readonly provenance: OutcomeProvenance; readonly status: number | null; }): ProxyAccountingFailureKind; export declare function markAttemptCommitted(attempt: HealthAttempt | undefined): void; export declare function completeAttemptSuccess(h: { breaker: CircuitBreaker; modelCallRecorder?: ModelCallRecorder; costClassOf?: CostClassFn | null; }, attempt: HealthAttempt, status: number): void; export declare function completeAttemptFailure(h: { breaker: CircuitBreaker; modelCallRecorder?: ModelCallRecorder; }, attempt: HealthAttempt, options: { failure: AttemptFailed["failure"]; provenance: OutcomeProvenance; status: number | null; retryAfterMs?: number | null; logStatus?: RequestAttemptStatus; }): void; export declare function completeAttemptCancelled(h: { breaker: CircuitBreaker; modelCallRecorder?: ModelCallRecorder; }, attempt: HealthAttempt, reason: string | null): void; type PostHeaderBodyDisposition = "cancelled" | "timeout" | "protocol"; export declare function completePostHeaderBodyFailure(h: { breaker: CircuitBreaker; modelCallRecorder?: ModelCallRecorder; }, downstream: ServerResponse, signal: AbortSignal, attempt: HealthAttempt, credentialWalk: CredentialWalk, credentialTrace: CredentialAttemptTrace, resolvedAttempt: ResolvedAttempt): PostHeaderBodyDisposition; export {};