/** * Cross-provider fallback model extension. * * Lives in core so EVERY agent surface can reuse it: the CLI leader, the CLI * director/host subagent factory, and the runtime light subagent factory (used * by standalone SDD runs). It wraps the provider runner and, when the active * model 429s / overloads / stream-hangs, rotates through a fallback chain. The * chain is recomputed from live config every turn, so changes take effect * without a restart; an empty chain makes the wrapper a no-op. * * Moved here from `@wrongstack/cli` (it only ever depended on core types) so the * runtime light factory can wire fallbacks for SDD worker subagents. */ import type { ProviderModelStatusTracker } from '../coordination/provider-status-tracker.js'; import type { AgentExtension } from '../extension/extension-points.js'; import type { EventBus } from '../kernel/events.js'; import type { Config } from '../types/config.js'; import type { Logger } from '../types/logger.js'; import { type Provider } from '../types/provider.js'; import { FallbackProfileManager } from './fallback-profile-manager.js'; export type { ModelRef } from './model-ref.js'; export { formatModelRef, normalizeModelRef, parseModelRef } from './model-ref.js'; export interface FallbackModelDeps { /** Returns the live config (re-read each turn so `/model` switches are honored). */ getConfig: () => Config; /** Shared live manager from the runtime container. */ fallbackProfileManager?: FallbackProfileManager | undefined; /** Live named profile selected for this worker (for example by `/setmodel`). */ getFallbackProfile?: (() => string | undefined) | undefined; /** Live task/role-specific chain. Explicit task fallbacks may return a stable list. */ getFallbackModels?: (() => readonly string[] | undefined) | undefined; /** Worker-local primary; prevents a subagent fallback from restoring the leader model. */ getPrimaryTarget?: (() => { providerId: string; model: string; } | undefined) | undefined; /** When true, only the explicitly supplied fallback chain may be attempted. */ isClosedWorld?: (() => boolean) | undefined; /** * Builds a credential-resolved Provider for a provider id (alias-resolved), * WITHOUT persisting anything to config/configStore. Supplied by the boot * path, which shares this with the `/model` switch logic. May be async — the * subagent host resolves a provider's real context window asynchronously. */ buildProvider: (providerId: string, modelId?: string | undefined) => Provider | Promise; /** * Called after the active model changes (a fallback hop or the primary * restore) so the host can refresh the auto-compaction / context-window * denominator — important when a fallback crosses to a smaller-window model. */ onModelSwitch?: (providerId: string, modelId: string) => void | Promise; events: EventBus; /** Optional — warnings about un-buildable fallback providers. */ logger?: Logger | undefined; /** * Base cooldown after the configured primary fails with a fallback-worthy * error. While active, `beforeRun` leaves the context on the working fallback * instead of retrying the primary at the start of every turn. Default: 60s. * Set 0 to preserve the legacy "probe primary every turn" behavior. */ primaryCooldownMs?: number | undefined; /** * Maximum exponential cooldown for repeated failed primary probes. Default: * 10 minutes. Ignored when `primaryCooldownMs` is 0. */ primaryCooldownMaxMs?: number | undefined; /** Test hook for deterministic cooldown assertions. */ now?: (() => number) | undefined; /** * Shared provider/model status tracker. When set, the extension records * failures and successes in the tracker, and skips blocked entries in * the fallback chain. */ statusTracker?: ProviderModelStatusTracker | undefined; } export declare function fallbackProfileChain(config: Config, profileName: string | undefined): string[]; export declare function smartDefaultFallbackChain(config: Config): string[]; /** * The effective fallback chain for a turn: the explicit `fallbackModels` list * when non-empty, otherwise the smart default (unless `fallbackAuto` is off). */ export declare function effectiveFallbackChain(config: Config): string[]; /** * Build the cross-provider fallback extension. Always returns an extension — * the effective chain (`effectiveFallbackChain`) is recomputed every turn from * the live config, so a chain that is empty at boot but populated later (via * `/fallback add` or the smart default kicking in once a key is added) takes * effect WITHOUT a restart. An empty chain makes the wrapper a no-op (it just * rethrows the original error). * * Mechanism (see plan): wraps the provider runner. The inner runner already * applies the per-model retry policy (backoff, up to 5 tries for 429), so the * fallback only engages AFTER the active model's own retries are exhausted. * Because the wrapper resolves within a single provider call, it does not * consume the agent loop's `recoveryRetries` budget — chains longer than two * entries work. `beforeRun` keeps the last working fallback while the primary * is cooling down, then restores the configured primary for a half-open probe. */ export declare function createFallbackModelExtension(deps: FallbackModelDeps): AgentExtension; //# sourceMappingURL=fallback-model.d.ts.map