/** * Model routing configuration for workflow subagents. * * This module is the single source of truth for the on-disk * `~/.pi/workflows/model-tiers.json` profile. It carries TWO layers: * * 1. **Semantic roles** (issue #142): `worker`, `conductor`, `advisor`, * `security`. A role expresses WHAT JOB an agent is doing; the machine-local * config decides the concrete provider/model. Each role has a `default` * model and a small set of named `routes` (e.g. `escalation`, * `long-context`, `independent`) that are explicit, journaled specialist * selections — never an automatic fallback ladder. * * 2. **Deprecated size tiers** (small/medium/big): kept ONLY as migration * inputs for existing saved workflows and journals. They map * small→worker, medium→conductor, big→advisor (security has no size-tier * ancestor). New authoring guidance uses roles only. * * The on-disk file is read non-destructively: a profile that still uses only * the legacy `tiers` block keeps working, with roles derived from it via the * deprecation alias. A profile that adds a `roles` block wins. Migration never * destroys machine-local config. * * Augments the phase-pattern routing in model-routing.ts (phase routing maps * workflow phases → models via the script's meta); roles give scripts a * coarse, portable, responsibility-oriented knob independent of any concrete * provider/model id. */ /** * Semantic routing role. A role expresses WHAT JOB an agent is doing * (worker / conductor / advisor / security), not how big or expensive the * model is. The machine-local config decides the concrete provider/model. */ export type ModelRole = "worker" | "conductor" | "advisor" | "security"; /** Canonical ordered list of roles (used for display + validation). */ export declare const MODEL_ROLES: readonly ModelRole[]; /** * Deprecation alias map: legacy size tier → semantic role. `security` has no * size-tier ancestor. Used only to keep existing saved workflows/journals * working; new authoring guidance uses roles. */ export declare const TIER_TO_ROLE: Readonly>; /** * A single role definition: a default model plus named, explicit specialist * routes. Routes are NEVER an automatic fallback ladder — each named route is * a deliberately journaled specialist selection carrying the evidence that * justified it (failed host checks, a verifier rejection, long-context fit, * model-family diversity, etc.). */ export interface RoleRoute { /** Concrete model spec for this named route (e.g. "openai-codex/gpt-5.6-luna"). */ model: string; /** * Optional operator note carried into the authoring prompt to explain when * this route should be chosen (the evidence/trigger rule). */ note?: string; } export interface RoleDefinition { /** Default model spec for this role. */ default: string; /** Named, explicit specialist routes (e.g. escalation, long-context, independent). */ routes?: Record; } export type RoleMap = Record; /** * The provider prefix whose models are API-billed (not subscription- or * self-hosted). These models must NEVER be role defaults/routes/fallbacks; an * exact use requires a visible operator opt-in (`googleBillingOptIn: true` on * the agent() call) and is recorded as an explicit model selection. */ export declare const API_BILLED_PROVIDER_PREFIX = "google-ai-studio/"; /** * The security-only model. It is rejected outside the `security` role — after * EVERY selection source (explicit `model`, agentType model pin, role default, * role route, and deprecated tier). A quota/availability failure must surface, * never silently downgrade a required security gate. */ export declare const SECURITY_MODEL = "meridian/claude-fable-5"; /** * Role whose default model is the security-only model. Fable cannot run * outside this role. */ export declare const SECURITY_ROLE: ModelRole; /** * Full model routing configuration. Carries the semantic `roles` layer and * the deprecated `tiers` compatibility layer (kept for migration only). * * `roles` is optional on disk: when absent, roles are derived from `tiers` via * the deprecation alias. When present, `roles` is authoritative. */ export interface ModelRoleConfig { /** * Semantic role → model mapping. Authoritative when present. When absent, * roles are derived from `tiers` via {@link TIER_TO_ROLE}. May be partial on * disk; {@link resolveRoleMap} falls back to derivation when a canonical * role is missing. */ roles?: Partial; /** * Deprecated size-tier → model map (small/medium/big). Kept ONLY as migration * input. Resolved via {@link resolveTierModel}; new code should resolve * roles instead. */ tiers?: Record; /** * Optional operator guidance injected into the model-facing workflow * authoring prompt. Carries machine-specific specialization (context-window * facts, escalation evidence rules, provider availability policy). */ routingNotes?: string[]; } /** * Legacy alias for {@link ModelRoleConfig}. The on-disk shape is unchanged; * this name is retained for compatibility with existing imports/tests. * @deprecated Use {@link ModelRoleConfig}. */ export type ModelTierConfig = ModelRoleConfig; /** Path to the model routing config file (~/.pi/workflows/model-tiers.json). */ export declare function getModelTierConfigPath(): string; /** * Build a default routing config where every role AND every legacy tier points * at a single model — the user's currently active Pi model when known, else * the first available model. New users get consistent behaviour (every route * == the model they're already chatting with) and can refine via * `/workflows-models`. Both layers are populated so legacy code paths and new * role paths agree until the user customizes one. */ export declare function buildDefaultTierConfig(currentModelSpec?: string): ModelRoleConfig; /** * Return the authoritative role map for a config: the explicit `roles` block * when present, else a role map derived from the legacy `tiers` block via the * deprecation alias (small→worker, medium→conductor, big→advisor). Derivation is * STRICT — each role maps ONLY to its size-tier ancestor, with no * cross-fallback, so a tier-only profile preserves the exact legacy semantics * (e.g. a profile lacking a `medium` tier still yields an absent conductor so * an untagged agent falls through to the session default, as before). `security` * has no size-tier ancestor; on a tier-only profile it derives from `big` (the * closest frontier tier) so a security agent still resolves a model. Returns * null when neither block is usable. */ export declare function resolveRoleMap(config: ModelRoleConfig | null | undefined): RoleMap | null; /** * Load the model routing config from disk. Returns null if the file does not * exist or is unparseable (callers fall back to a default). The on-disk file is * read non-destructively — a profile with only a `tiers` block keeps working. */ export declare function loadModelTierConfig(configPath?: string): ModelRoleConfig | null; /** * Save a model routing config to disk. Creates parent directories if needed. */ export declare function saveModelTierConfig(config: ModelRoleConfig, configPath?: string): void; /** * Resolve a deprecated size-tier name to its configured model spec, or * undefined if the tier is not configured. New code should use * {@link resolveRoleModel} instead. */ export declare function resolveTierModel(tier: string, config: ModelRoleConfig): string | undefined; /** * Resolve a role (with an optional named route) to its concrete model spec. * Returns undefined when the role/route is unknown or the route is absent — * the caller decides whether to fail closed or fall back to the session model. * * Route resolution is exact, never laddered: an unknown `route` does NOT fall * back to the role default; it returns undefined so the caller can surface an * actionable error rather than silently dropping to a weaker selection. */ export declare function resolveRoleModel(role: string, route: string | undefined, config: ModelRoleConfig | null | undefined): string | undefined; /** True if the model spec belongs to the API-billed provider (never a default/route/fallback). */ export declare function isApiBilledModel(modelSpec: string | undefined): boolean; /** True if the model spec is the security-only model (Fable). */ export declare function isSecurityModel(modelSpec: string | undefined): boolean; /** Return all tier names sorted: small < medium < big, then alphabetically. */ export declare function sortedTierNames(config: ModelRoleConfig): string[]; /** Return the canonical role names in stable order (only roles that resolve in the config). */ export declare function sortedRoleNames(config: ModelRoleConfig | null | undefined): string[]; /** * Surface configuration shapes that make a small → medium → big escalation * misleading. These are warnings rather than validation errors because a fresh * install intentionally starts with all three tiers on the current Pi model. * * Also surfaces role-layer warnings: the security-only model used outside the * security role, and API-billed models used as role defaults/routes. */ export declare function modelTierConfigWarnings(config: ModelRoleConfig): string[];