import type { ApiKeyResolver, ResolvedApiKey } from "../auth-retry.js"; import type { AuthStorage } from "../auth-storage.js"; import { type GatewayErrorClassification } from "../error/gateway.js"; import type { Api, FetchImpl, Model, Usage } from "../types.js"; import type { ClientUsageIdentity } from "../usage.js"; import type { AuthGatewayServerOptions } from "./types.js"; export type ModelResolver = (modelId: string) => Model | undefined; /** What the gateway's routes need, whatever transport carries the requests. */ export interface AuthGatewayRouteOptions { /** Source of credentials: broker-backed for `serve`, the CLI's own for `stdio`. */ storage: AuthStorage; /** * Resolve a client-requested model id to a pi-ai Model. Caller supplies * this from a ModelRegistry (lives in `coding-agent` to avoid an inverse * dependency in `pi-ai`). */ resolveModel: ModelResolver; /** Optional supplier for `/v1/models` listing. Returns the full model array. */ listModels?: () => Iterable>; /** Upstream transport for every provider call; defaults to global `fetch`. Test seam. */ fetch?: FetchImpl; } /** The HTTP server's options: the routes' plus its listener and inbound auth. */ export interface AuthGatewayBootOptions extends AuthGatewayServerOptions, AuthGatewayRouteOptions { } /** * The client's own session key, or `undefined` when it sent none. A blank key * counts as none: honouring it would collapse every caller that sends an empty * key into one shared credential-sticky, prefix-cache and provider-session * bucket. */ export declare function normalizeClientSessionKey(clientKey: string | undefined): string | undefined; /** * Stable identity of the account a request's credential belongs to. * * `markUsageLimitReached` and the auth-retry resolver switch a session to a * sibling credential, so the provider state retained for that session can * outlive the account that taught it. OAuth rows expose an account id / email * that survives token refresh — fingerprinting the bearer instead would look * like a rotation every time a token refreshes and discard the retained * lessons for nothing. Key-based rows fall back to a hash of the key, never * the key itself: this value is held for the lifetime of the entry. */ export declare function resolveGatewayAccount(storage: AuthStorage, provider: string, sessionId: string, apiKey: string): string; /** * Resolve the credential for one request from broker-backed storage. * * pi-ai clients never consult `AuthStorage`; the gateway resolves the bearer * and its OAuth identity together (refreshing through the broker when needed). * Keep this selection snapshot intact even if another request replaces the * stored token before dispatch. Storage failures map through * {@link classifyGatewayError}; a provider without any credential is a 401. */ export declare function resolveGatewayApiKey(storage: AuthStorage, model: Model, sessionId: string, signal: AbortSignal, peer: string): Promise; /** * Build the {@link ApiKeyResolver} handed to a pi-ai client for a gateway * request. Drives the central a/b/c auth-retry policy server-side: * * - initial resolve → the credential already resolved for this request. * - step (b) `!lastChance` → force-refresh the SAME session-sticky credential * (a peer/broker may have rotated its token out from under our cached copy). * - step (c) `lastChance` → {@link refreshGatewayApiKeyAfterAuthError} switches * to a sibling (usage-limit block vs credential invalidation by error class). * * `lastKey` tracks the most recent bearer so the switch step invalidates the * credential that actually failed. `onResolvedKey` observes every rotation; * routes that retain provider session state use it to re-key the account * lease, one-shot routes pass `undefined`. */ export declare function buildGatewayApiKeyResolver(storage: AuthStorage, model: Model, sessionId: string, initialKey: ResolvedApiKey, requestSignal: AbortSignal, format: string, peer: string, onResolvedKey?: (apiKey: string) => void): ApiKeyResolver; /** * Attribute one settled upstream request to the originating client via the * broker's observed-usage channel (`AuthStorage.usage.observe`, batched * by the remote store). Error/aborted turns still record — the provider * billed whatever tokens the partial turn consumed; zero-usage results * (pre-flight failures) are skipped. `at` defaults to now. */ export declare function recordGatewayUsage(storage: AuthStorage, model: Model, client: ClientUsageIdentity, usage: Usage, at?: number): void; /** * An `AbortController` that follows the inbound request's abort signal. Routes * abort it themselves when the response body is cancelled mid-stream, which * `req.signal` alone does not observe. */ export declare function mirrorRequestAbort(req: Request): AbortController;