/** * src/models/routing.ts — declarative child model resolution and honest * fallback chains (A -> B -> parent). * * Resolution priority (documented): * explicitModel -> agentModel -> class model -> parentModel -> undefined. * * The fallback chain is built honestly and traced: the resolved result exposes * the actual model chosen and a `status` ('direct' or 'fallback'), plus the * ordered list of models tried, so callers (lanes) can record a visible * 'model_fallback' without hiding the fallback behind an opaque success. * * Pure module: zero @earendil-works/* imports, zero child_process. */ import { type ModelClass } from "./classes.js"; import type { ModelScopeSource } from "./scope.js"; /** A mapping from pi-subagents class to its preferred model id. */ export type ModelByClassMap = Partial>; export type ModelRouteStatus = "direct" | "fallback"; /** * Result of a model route decision. `model` is the effective model (may be the * parent sentinel), `tried` is the ordered list of candidate models considered. * status 'direct' means the first available candidate was chosen; 'fallback' * means a later chain entry (or parent) was selected. `source` reports WHICH * input won ('explicit' | 'agent' | 'class' | 'inherited'; the parent/session * default maps to 'inherited') so downstream gates (model scope, C3) can apply * source-dependent severity. Optional because chain-selection helpers do not * track provenance. */ export interface ModelRouteResult { model?: string; status: ModelRouteStatus; source?: ModelScopeSource; tried: readonly string[]; } export interface ResolveChildModelInput { /** Explicit model override (highest priority). */ explicitModel?: string; /** Model declared on the agent definition. */ agentModel?: string; /** Requested routing class (pi-subagents or harness vocabulary). */ modelClass?: ModelClass | string; /** Preferred model per class. */ classModels?: ModelByClassMap; /** Fallback per-class map keyed by class name (normalized or harness). */ modelByClass?: Partial>; /** Parent/session default model — used as the last-resort sentinel. */ parentModel?: string; } /** * Resolve the child model with the documented priority * explicit -> agent -> class -> parent -> undefined. * Never throws; never probes availability. */ export declare function resolveChildModel(input: ResolveChildModelInput): ModelRouteResult; /** * F3 quota-safe default class models: map EVERY class (cheap/balanced/ * capable) to the parent/session model. Availability rationale: the parent * model is the one model guaranteed available for this session (the parent * runs on it), so a class-routed child never silently switches provider or * burns a different provider's quota (the F3 bug: ollama-cloud parent spawning * openai-codex children). Hosts that want per-class routing pass an explicit * `classModels` map, which REPLACES this default entirely. */ export declare function parentInheritingClassModels(parentModel: string | undefined): ModelByClassMap | undefined; /** * Build the honest fallback chain for a class: [preferred (A), ...classChain * (B), parent]. Duplicates are removed preserving order; empty/undefined * entries are skipped. The parent is always the final sentinel and is never * probed by callers. */ export declare function buildModelFallbackChain(preferred: string | undefined, classChain: readonly (string | undefined)[], parentModel: string | undefined): string[]; /** * Select the effective model from a fallback chain, marking direct vs fallback. * * An availability predicate (default: everything is available) lets callers * honestly skip candidates that were already ruled out (e.g. via the shared * availability cache), so a later chain entry is marked 'fallback'. The parent * sentinel, when reached, means "reuse the parent session / no dedicated child". * * 'direct' means the very first candidate in the chain was selected; * 'fallback' means a later candidate (or the parent sentinel) was selected. */ export declare function selectModelFromChain(chain: readonly string[], parentModel: string | undefined, isAvailable?: (model: string) => boolean): ModelRouteResult;