/**
 * Model failover — the answer to "one upstream hit a quota wall and the customer
 * got a Bad Gateway even though the router had abundant healthy capacity".
 *
 * This is deliberately NOT a health probe. Probing before every turn buys a
 * round-trip of latency on the happy path and still races the outage (a model
 * healthy at probe time can 502 a second later). Failover is REACTIVE: run the
 * preferred model, and on an upstream-outage signal move to the next model in
 * the chain. Zero added latency when the preferred model works.
 *
 * Two facts drive the design, both measured against a live box during the
 * 2026-07-25 Anthropic/DeepSeek outage:
 *
 * 1. An outage is NOT always a thrown error. The sandbox resolves with a
 *    payload — `{ success: false, errorCode: 'provider_inference_unavailable' }`
 *    — so a classifier that only inspects `catch` misses the customer-visible
 *    case entirely. `isUpstreamUnavailable` inspects resolved values too.
 * 2. Catalog membership is NOT liveness. The router's `/v1/models` still listed
 *    every dead model during the outage, so `validateChatModelId` admitted
 *    `claude-sonnet-4-6` while every call to it returned 502. Nothing upstream
 *    of the actual call can be trusted to tell you a model is serving.
 *
 * Substrate-free: the caller injects `run`, so this composes with a sandbox
 * turn, a router chat completion, or a test fake without importing any of them.
 */
/**
 * Error codes that mean "this model's upstream is unavailable — a different
 * model may still work". Deliberately excludes codes that would fail identically
 * on every model (bad request, auth, content filter): retrying those down a
 * chain burns latency and money to reach the same failure.
 */
declare const UPSTREAM_UNAVAILABLE_CODES: readonly string[];
/** HTTP statuses that indicate an upstream capacity/availability problem. */
declare const UPSTREAM_UNAVAILABLE_STATUSES: readonly number[];
/**
 * True when `signal` — a thrown error OR a resolved result payload — indicates
 * the model's upstream is unavailable and another model is worth trying.
 *
 * Checked in order of decreasing confidence: explicit code, HTTP status, then
 * message text. A resolved payload only counts as a failure when it carries an
 * explicit failure marker (`success: false`, or an `error`/`errorCode` field) —
 * a successful result is never misread as an outage.
 */
declare function isUpstreamUnavailable(signal: unknown): boolean;
/** One model tried, and how it went. */
interface ModelFailoverAttempt {
    model: string;
    ok: boolean;
    /** Why this model was abandoned. Absent when `ok`. */
    reason?: string;
}
/** The outcome of a failover run: the value plus the full attempt trail. */
interface ModelFailoverResult<T> {
    value: T;
    /** The model that actually produced `value`. */
    model: string;
    attempts: ModelFailoverAttempt[];
    /** True when the preferred (first) model did not serve the request. */
    usedFallback: boolean;
}
/** Every model in the chain failed; carries the trail for logging. */
declare class ModelFailoverExhaustedError extends Error {
    readonly attempts: ModelFailoverAttempt[];
    constructor(attempts: ModelFailoverAttempt[]);
}
/** Inputs to {@link runWithModelFailover}. */
interface RunWithModelFailoverInput<T> {
    /** Preferred model first, then fallbacks in descending preference. */
    models: readonly string[];
    /** Executes one turn with the given model. */
    run: (model: string) => Promise<T>;
    /**
     * Classifies a resolved result as an upstream outage. Defaults to
     * {@link isUpstreamUnavailable}, which understands the sandbox's
     * `{ success: false, errorCode }` payload.
     */
    isUnavailableResult?: (result: T) => boolean;
    /** Classifies a thrown error. Defaults to {@link isUpstreamUnavailable}. */
    isUnavailableError?: (error: unknown) => boolean;
    /** Observability hook fired each time a model is abandoned. */
    onFallback?: (attempt: ModelFailoverAttempt, nextModel: string) => void;
}
/**
 * Run `run` against the first model in `models` that does not report an upstream
 * outage, falling through the chain in order.
 *
 * A non-outage failure (bad request, auth, content filter) is re-thrown
 * immediately rather than retried down the chain — those fail identically on
 * every model, so walking the chain would only multiply latency and spend.
 *
 * @throws ModelFailoverExhaustedError when every model reports an outage.
 */
declare function runWithModelFailover<T>(input: RunWithModelFailoverInput<T>): Promise<ModelFailoverResult<T>>;
/**
 * Build a failover chain: the preferred model first, then `fallbacks`, with
 * duplicates removed so a model is never retried twice in one turn.
 */
declare function buildModelChain(preferred: string, fallbacks: readonly string[]): string[];

/**
 * Canonical chat-model resolution — identical across every agent app.
 *
 * The ONLY per-app inputs are DATA, never logic: the default model, the
 * allowlist, the env value the deployment set, and the catalog-fetch loader.
 * The logic is one precedence ladder + one fail-closed validator that every
 * product uses the same way — there is no per-product variant, no env-var name
 * baked in, and no backend dimension (router-vs-sandbox is the harness/dispatch
 * concern, not model resolution; a sandbox's provider default lives in the
 * sandbox subpath).
 *
 * - resolveChatModel: request > workspace > env > default. The product reads its
 *   own deploy env var and passes the VALUE as `envModel`; the shell knows no
 *   env-var names. Source is canonical: 'request' | 'workspace' | 'env' | 'default'.
 * - validateChatModelId: fail-closed. Admit an id that is in the allowlist, or
 *   equals the operator-set env model, or is served by the live router catalog
 *   (exact, or a bare id resolved to its canonical id when the suffix is unique).
 */
/** The router /v1/models entry shape this module reads. Minimal on purpose. */
interface ModelInfo {
    id: string;
    name?: string;
    _provider?: string;
    provider?: string;
}
/** Define possible origins for the chat model configuration values */
type ChatModelSource = 'request' | 'workspace' | 'env' | 'default';
/** Resolve a chat model with its identifier and source information */
interface ResolvedChatModel {
    model: string;
    source: ChatModelSource;
}
/** Represent successful chat model validation with a true status and a validated string value */
interface ChatModelValidationSuccess {
    succeeded: true;
    value: string;
}
/** Describe a failed chat model validation result with an error message */
interface ChatModelValidationFailure {
    succeeded: false;
    error: string;
}
/** Resolve the outcome of validating a chat model as either success or failure */
type ChatModelValidationResult = ChatModelValidationSuccess | ChatModelValidationFailure;
/** The catalog-fetch boundary: maps a router base URL to the raw model list. */
type LoadModels = (routerBaseUrl: string) => Promise<ModelInfo[]>;
/** Resolve the effective chat model input by prioritizing request, workspace, environment, and default models */
interface ResolveChatModelInput {
    /** Per-request override (highest precedence). */
    requestModel?: string;
    /** Persisted workspace-pinned model. */
    workspaceModel?: string;
    /** The value the deployment's model env var holds (the product reads its own
     *  var name and passes the value — the shell stays env-var-name agnostic). */
    envModel?: string;
    /** Final fallback (the product's default, typically profile.model.default). */
    defaultModel: string;
}
/** Resolve the chat-turn model by the one canonical precedence. Blank values are
 *  treated as absent. */
declare function resolveChatModel(input: ResolveChatModelInput): ResolvedChatModel;
/** Define input parameters for validating chat model IDs with optional allowlist and catalog access details */
interface ValidateChatModelIdInput {
    /** Ids accepted without a catalog round-trip (defaults + operator-trusted). */
    allowlist?: Iterable<string>;
    /** The operator-set env model value — always admitted (operator-trusted). */
    envModel?: string;
    /** Catalog loader; required to reach the catalog path. */
    loadModels?: LoadModels;
    /** Catalog endpoint base; required to reach the catalog path. */
    routerBaseUrl?: string;
}
/**
 * Fail-closed model-id validation. Accepts an id only when it is well-formed AND
 * (in the allowlist, or equals the operator-set env model, or served by the live
 * catalog). A bare id (no provider prefix) resolves to its canonical id only when
 * the suffix is unique across the catalog — an ambiguous suffix is rejected
 * rather than silently assigned a provider.
 */
declare function validateChatModelId(modelId: unknown, input: ValidateChatModelIdInput): Promise<ChatModelValidationResult>;
/** Resolve and return a trimmed string model ID or undefined for invalid or empty input */
declare function cleanModelId(value: unknown): string | undefined;
/** Validate if a model ID string conforms to length and character format requirements */
declare function isWellFormedModelId(modelId: string): boolean;
/** Resolve unique catalog IDs associated with a given model including its canonical form if applicable */
declare function catalogIdsForModel(model: ModelInfo): string[];

export { type ChatModelSource, type ChatModelValidationFailure, type ChatModelValidationResult, type ChatModelValidationSuccess, type LoadModels, type ModelFailoverAttempt, ModelFailoverExhaustedError, type ModelFailoverResult, type ModelInfo, type ResolveChatModelInput, type ResolvedChatModel, type RunWithModelFailoverInput, UPSTREAM_UNAVAILABLE_CODES, UPSTREAM_UNAVAILABLE_STATUSES, type ValidateChatModelIdInput, buildModelChain, catalogIdsForModel, cleanModelId, isUpstreamUnavailable, isWellFormedModelId, resolveChatModel, runWithModelFailover, validateChatModelId };
