/** * Model resolution, scoping, and initial selection */ import { ThinkingLevel } from "@gajae-code/agent-core"; import { type Api, type KnownProvider, type Model } from "@gajae-code/ai/core"; import { type ModelRegistry, type ModelRole } from "./model-registry"; import { type ModelSelectorValue } from "./model-selector-value"; import type { Settings } from "./settings"; /** Default model IDs for each known provider */ export declare const defaultModelPerProvider: Record; /** * Cursor's current RPC transport executes its exec protocol while streaming and * has no client-side tool-call mode. Managed fallback attempts must not enable * that irreversible path. */ export declare function managedCursorFallbackUnavailableReason(model: Model, selector: string): string | undefined; export interface ScopedModelSelection { model: Model; thinkingLevel?: ThinkingLevel; explicitThinkingLevel?: boolean; } export interface ScopedModel extends ScopedModelSelection { explicitThinkingLevel: boolean; } export type { SelectorThinkingSuffix } from "../thinking"; export { splitSelectorThinkingSuffix } from "../thinking"; /** * Parse a model string in "provider/modelId" format. * Returns undefined if the format is invalid. */ export declare function parseModelString(modelStr: string): { provider: string; id: string; thinkingLevel?: ThinkingLevel; } | undefined; /** * Format a model as "provider/modelId" string. */ export declare function formatModelString(model: Model): string; export declare function formatModelSelectorValue(selector: string, thinkingLevel: ThinkingLevel | undefined): string; export declare function resolveProviderModelReference(provider: string, modelId: string, availableModels: readonly Model[]): Model | undefined; export interface ModelMatchPreferences { /** Most-recently-used model keys (provider/modelId) to prefer when ambiguous. */ usageOrder?: string[]; /** Providers to deprioritize when no recent usage is available. */ deprioritizeProviders?: string[]; } export type CanonicalModelRegistry = Partial>; export type ModelLookupRegistry = Pick & Partial; type CliModelRegistry = Pick & Partial; type InitialModelRegistry = Pick; type RestorableModelRegistry = Pick; /** * Find an exact explicit provider/model match. * Bare model ids are handled separately so canonical ids can coalesce variants. */ export declare function findExactModelReferenceMatch(modelReference: string, availableModels: Model[]): Model | undefined; export interface ParsedModelResult { model: Model | undefined; /** Thinking level if explicitly specified in pattern, undefined otherwise */ thinkingLevel?: ThinkingLevel; warning: string | undefined; explicitThinkingLevel: boolean; } export interface ResolveSelectorOptions { allowInvalidThinkingSelectorFallback?: boolean; modelRegistry?: CanonicalModelRegistry; preferences?: ModelMatchPreferences; sessionId?: string; credentialSessionId?: string; aliasIntent?: "preset-equivalent" | "reject"; } /** * Resolve one selector through ordered exact, canonical, bare-id, provider-fuzzy, * substring/alias, and glob stages. A preset-equivalent alias intent adds a * final-slash-segment alias stage between strict-exact and substring/fuzzy. * Thinking is split only after a full selector cannot resolve, which preserves * OpenRouter route suffixes in concrete IDs. */ export declare function resolveSelector(selector: string, candidates: Model[], options?: ResolveSelectorOptions): ParsedModelResult; /** @internal Exported for testing and legacy adapters. */ export declare function parseModelPattern(pattern: string, availableModels: Model[], preferences?: ModelMatchPreferences, options?: { allowInvalidThinkingSelectorFallback?: boolean; modelRegistry?: CanonicalModelRegistry; sessionId?: string; credentialSessionId?: string; aliasIntent?: "preset-equivalent" | "reject"; }): ParsedModelResult; export type ModelRoleSettings = Pick; /** * Expand a role alias like "pi/default" to the configured model string. */ export declare function expandRoleAlias(value: string, settings?: ModelRoleSettings): string; export declare function resolveConfiguredModelPatterns(value: ModelSelectorValue | undefined, settings?: ModelRoleSettings): string[]; export interface AgentModelPatternResolutionOptions { settingsOverride?: ModelSelectorValue; agentModel?: ModelSelectorValue; settings?: Settings; activeModelPattern?: string; fallbackModelPattern?: string; } export declare function resolveAgentModelPatterns(options: AgentModelPatternResolutionOptions): string[]; /** * Resolve a model role value into a concrete model and thinking metadata. */ export interface ResolvedModelRoleValue { model: Model | undefined; thinkingLevel?: ThinkingLevel; explicitThinkingLevel: boolean; warning: string | undefined; } export declare function resolveModelRoleValue(roleValue: ModelSelectorValue | undefined, availableModels: Model[], options?: { settings?: ModelRoleSettings; matchPreferences?: ModelMatchPreferences; modelRegistry?: CanonicalModelRegistry; sessionId?: string; credentialSessionId?: string; aliasIntent?: "preset-equivalent" | "reject"; }): ResolvedModelRoleValue; export declare function extractExplicitThinkingSelector(value: ModelSelectorValue | undefined, settings?: Settings): ThinkingLevel | undefined; /** * Resolve a model identifier or pattern to a Model instance. */ export declare function resolveModelFromString(value: string, available: Model[], matchPreferences?: ModelMatchPreferences, modelRegistry?: CanonicalModelRegistry): Model | undefined; /** * Resolve a model from configured roles, honoring order and overrides. */ export declare function resolveModelFromSettings(options: { settings: Settings; availableModels: Model[]; matchPreferences?: ModelMatchPreferences; roleOrder?: readonly ModelRole[]; modelRegistry?: CanonicalModelRegistry; }): Model | undefined; /** * Resolve a list of override patterns to the first matching model. */ export declare function resolveModelOverride(modelPatterns: string[], modelRegistry: ModelLookupRegistry, settings?: Settings, sessionId?: string, aliasIntent?: "preset-equivalent" | "reject"): { model?: Model; thinkingLevel?: ThinkingLevel; explicitThinkingLevel: boolean; }; /** * Resolve a configured fallback chain to its first callable entry without * charging requests. For retryable chains, consumers MUST pass * `{ managedFallback: true }` so unsuitable entries (including Cursor's * provider-side tool mode) fail closed during resolution before any request * is attempted. Single-entry chains remain non-managed selections. */ export interface ModelChainResolutionOptions { managedFallback?: boolean; aliasIntent?: "preset-equivalent" | "reject"; canonicalSessionId?: string | null; credentialSessionId?: string; } export declare function resolveModelChainWithAuth(modelPatterns: readonly string[], modelRegistry: ModelLookupRegistry & Pick, settings?: Settings, sessionId?: string, options?: ModelChainResolutionOptions): Promise<{ model?: Model; thinkingLevel?: ThinkingLevel; explicitThinkingLevel: boolean; activeIndex: number; skips: Array<{ selector: string; reason: string; }>; }>; /** * Resolve a list of override patterns to the first matching model, with an * auth-aware fallback to the parent session's active model. * * If the resolved subagent model has no working credentials (provider has no * usable auth), and the parent's active model resolves with working auth, * use the parent's model instead. This prevents subagent dispatch from * silently routing to a provider the user can't actually call (e.g. * `modelRoles.task` pointing at an unqualified id whose only available * provider variant has no configured credentials — see #985). * * Keyless-by-design providers (llama.cpp, ollama, lm-studio) advertise the * `kNoAuth` sentinel from `getApiKey` to signal that they do not require * credentials. Those are treated as authenticated here so an explicitly * configured local model is never silently rerouted to the parent's remote * provider (see #1008). * * If neither the subagent nor the parent has working auth, returns the * primary resolution unchanged so the existing error path still surfaces * a meaningful failure downstream. */ export declare function resolveModelOverrideWithAuthFallback(modelPatterns: string[], parentActiveModelPattern: string | undefined, modelRegistry: ModelLookupRegistry & Pick, settings?: Settings, authSessionId?: string, options?: ModelChainResolutionOptions, canonicalSessionId?: string): Promise<{ model?: Model; thinkingLevel?: ThinkingLevel; explicitThinkingLevel: boolean; authFallbackUsed: boolean; requestedModel?: Model; fallbackReason?: "auth_unavailable"; activeIndex?: number; parentFallbackSelector?: string; skips: Array<{ selector: string; reason: string; }>; }>; /** * Resolve a list of role patterns to the first matching model. */ export declare function resolveRoleSelection(roles: readonly string[], settings: Settings, availableModels: Model[], modelRegistry?: CanonicalModelRegistry): { model: Model; thinkingLevel?: ThinkingLevel; } | undefined; /** * Resolve model patterns to actual Model objects with optional thinking levels. * A `:level` suffix is interpreted only after the complete selector fails to * resolve, preserving concrete model IDs that contain colon-bearing route suffixes. * For each non-glob pattern, alias IDs are preferred over dated versions; otherwise * the latest dated version is selected. */ export declare function resolveModelScope(patterns: string[], modelRegistry: Pick, preferences?: ModelMatchPreferences): Promise; /** * Resolve the set of models a session is allowed to use, given the active * settings. Starts from `modelRegistry.getAvailable()` (so disabled providers * and providers without credentials are already filtered out) and, when * `enabledModels` is configured for the current path scope, further restricts * the result to models matching those patterns. * * Returns the unfiltered available list when `enabledModels` is empty. * Returns an empty list when `enabledModels` is configured but no available * model matches any pattern — callers MUST treat this as "no usable model" * rather than falling back to the global default (see issue #1022). */ export declare function resolveAllowedModels(modelRegistry: Pick, settings: Settings | undefined, preferences?: ModelMatchPreferences): Promise[]>; export interface ResolveCliModelResult { model: Model | undefined; selector?: string; thinkingLevel?: ThinkingLevel; warning: string | undefined; error: string | undefined; } /** Resolve a single model from CLI flags through the staged selector resolver. */ export declare function resolveCliModel(options: { cliProvider?: string; cliModel?: string; modelRegistry: CliModelRegistry; preferences?: ModelMatchPreferences; }): ResolveCliModelResult; export interface InitialModelResult { model: Model | undefined; thinkingLevel?: ThinkingLevel; fallbackMessage: string | undefined; } /** * Find the initial model to use based on priority: * 1. CLI args (provider + model) * 2. First model from scoped models (if not continuing/resuming) * 3. Restored from session (if continuing/resuming) * 4. Saved default from settings * 5. First available model with valid API key */ export declare function findInitialModel(options: { cliProvider?: string; cliModel?: string; scopedModels: ScopedModel[]; isContinuing: boolean; defaultProvider?: string; defaultModelId?: string; defaultThinkingSelector?: ThinkingLevel; modelRegistry: InitialModelRegistry; }): Promise; /** * Restore model from session, with fallback to available models */ export declare function restoreModelFromSession(savedProvider: string, savedModelId: string, currentModel: Model | undefined, shouldPrintMessages: boolean, modelRegistry: RestorableModelRegistry): Promise<{ model: Model | undefined; fallbackMessage: string | undefined; }>;