/** * Per-Agent Model Selection (M1-9) + Model Fallback (M3-5, Issue #145) */ import { type GitHubModelCategory, type CostPolicyConfig, type SessionCostPolicyOverride, type CostPolicyOutcome } from '../config/models.js'; import type { EventBus } from '../runtime/event-bus.js'; /** * Task types that influence model selection. */ export type TaskType = 'code' | 'prompt' | 'docs' | 'visual' | 'planning' | 'mechanical'; /** * Model tier classification. */ export type ModelTier = 'premium' | 'standard' | 'fast'; /** * Source of the model resolution. */ export type ModelResolutionSource = 'user-override' | 'charter' | 'task-auto' | 'default'; /** * Options for model resolution. */ export interface ModelResolutionOptions { /** User-specified model override */ userOverride?: string; /** Model preference from agent's charter (## Model section) */ charterPreference?: string; /** Type of task being performed */ taskType: TaskType; /** Agent role (for context) */ agentRole?: string; /** When true, apply economy mode substitution at Layer 3/4 */ economyMode?: boolean; /** * Persistent config carrying an optional cost policy (cost-ceiling axis). * When present with a `maxCategory`, resolution is finalized against it. */ config?: { costPolicy?: CostPolicyConfig; }; /** Per-session cost policy override (wins over `config.costPolicy`). */ sessionCostPolicy?: SessionCostPolicyOverride; } /** * Result of model resolution. */ export interface ResolvedModel { /** Selected model identifier */ model: string; /** Model tier classification */ tier: ModelTier; /** Source that determined the model */ source: ModelResolutionSource; /** Fallback chain for this tier */ fallbackChain: string[]; /** Cost-policy outcome, present only when a policy was applied. */ policy?: CostPolicyOutcome; } /** * Effective (merged) cost policy after combining persistent config + session * override. Only produced when a `maxCategory` is actually set. */ export interface EffectiveCostPolicy { maxCategory: GitHubModelCategory; } /** * Resolve the appropriate model using the 4-layer priority system, then * finalize against the effective cost policy (cost-ceiling axis). * * @param options - Model resolution options * @returns Resolved model with tier, fallback chain, and optional policy outcome */ export declare function resolveModel(options: ModelResolutionOptions): ResolvedModel; export declare function inferTierFromModel(model: string): ModelTier; /** * Build the catalog id → cost-ceiling category lookup from MODEL_CATALOG. * Out-of-catalog ids are simply absent (⇒ passthrough). */ export declare function buildCatalogCategoryMap(): Map; /** * Merge a persistent cost policy with an optional per-session override. * The session override wins. Returns undefined when neither sets a ceiling * (⇒ no-op / passthrough). */ export declare function buildEffectiveCostPolicy(config?: { costPolicy?: CostPolicyConfig; }, sessionPolicy?: SessionCostPolicyOverride): EffectiveCostPolicy | undefined; /** * Prune a fallback chain to the cost ceiling: drop entries whose category is * strictly above the ceiling. Uncategorized (out-of-catalog) entries are kept * as passthrough (unknown cost, not "above"). Order is preserved. */ export declare function pruneChainToCeiling(chain: string[], maxCategory: GitHubModelCategory, catalogMap: Map): string[]; /** * Finalize a base-resolved model against an effective cost policy. * * Branches: * - uncategorized model ⇒ passthrough (no policy action). * - within ceiling ⇒ prune the fallback chain to the ceiling. * - over ceiling + EXPLICIT source (user-override/charter) ⇒ warn-and-allow; * the explicit model is kept as chain head, the rest pruned. * - over ceiling + IMPLICIT source (task-auto/default) ⇒ deterministic * downgrade to the best in-ceiling model. * - no in-ceiling model anywhere ⇒ FAIL-CLOSED + LOUD warning; the original * model is kept as a transparent last resort (action ≠ 'none'). */ export declare function finalizeResolvedModel(base: ResolvedModel, policy: EffectiveCostPolicy, catalogMap: Map): ResolvedModel; export declare function isTierFallbackAllowed(fromTier: ModelTier, toTier: ModelTier, allowCrossTier: boolean): boolean; export interface FallbackAttempt { model: string; tier: ModelTier; error: string; timestamp: Date; } export interface FallbackResult { value: T; model: string; tier: ModelTier; attempts: FallbackAttempt[]; didFallback: boolean; } export interface FallbackExecutorConfig { allowCrossTier?: boolean; eventBus?: EventBus; } export declare class ModelFallbackExecutor { private allowCrossTier; private eventBus?; private history; constructor(config?: FallbackExecutorConfig); execute(resolved: ResolvedModel, agentName: string, fn: (model: string) => Promise): Promise>; getHistory(agentName: string): FallbackAttempt[]; clearHistory(): void; private buildCandidateList; private emitEvent; } //# sourceMappingURL=model-selector.d.ts.map