// ultra settings: validates the `ultra` block from Pi's user and trusted project settings. import type { ExtensionAPI, ExtensionContext, } from "@earendil-works/pi-coding-agent"; import { resolveSetting, type SettingDeclaration, } from "./src/pi-ext-settings.ts"; export const THINKING_LEVELS = [ "off", "minimal", "low", "medium", "high", "xhigh", "max", ] as const satisfies readonly Parameters[0][]; export const DEFAULT_FANOUT_TOOLS = ["read", "grep", "find", "ls", "bash"]; export interface ModelTier { /** null selects the session model. */ model: string | null; thinkingLevel: (typeof THINKING_LEVELS)[number]; /** Selection guidance for the orchestrator, not instructions for the child. */ instructions: string; } export type ModelTiers = Record; /** Provider name → tier name → per-field overrides for when that provider is active. */ export type ProviderModelTiers = Record< string, Record> >; export const DEFAULT_MODEL_TIERS: ModelTiers = { tiny: { model: null, thinkingLevel: "low", instructions: "No unresolved judgement.", }, small: { model: null, thinkingLevel: "low", instructions: "Few local decisions; low consequence.", }, medium: { model: null, thinkingLevel: "medium", instructions: "Several related decisions; moderate ambiguity or consequence.", }, large: { model: null, thinkingLevel: "high", instructions: "Many dependent decisions; substantial ambiguity or consequence.", }, huge: { model: null, thinkingLevel: "max", instructions: "Exceptional residual burden after decomposition, context, and verification.", }, }; export interface UltraSettings { /** When false, the tool + command refuse to run. */ enabled: boolean; /** Activate `run_workflow` when the session starts. */ autoEnable: boolean; /** Max concurrent sub-agents in a `fanout` phase. */ concurrency: number; /** Result-submission reminders after an agent stops without output. */ maxRetries: number; /** Extensions loaded into every sub-agent: all discovered extensions (`"all"`, * default), none (`"none"`), or one static list of sibling pi-ext names. * `step.tools` selects the initial tools; loaded extensions may change them. */ subagentExtensions: "none" | "all" | string[]; /** Additional initial tools permitted in fanout without writeIsolation, merged with defaults. */ fanoutToolAllowlist: string[]; /** Load host skills into sub-agents (`"all"`, default) or omit them (`"none"`). */ subagentSkills: "none" | "all"; /** Retain parent-linked sub-agent sessions, or run them entirely in memory. */ subagentSessionRetention: "all" | "none"; /** Settings-defined model, thinking, and selection policy used by step.model. */ modelTiers: ModelTiers; /** Per-provider tier overrides folded over `modelTiers` when the session model's * provider matches; providers without a block keep the base tiers. */ providerModelTiers: ProviderModelTiers; /** Fast model used to turn raw tool args into board action phrases; null disables it. */ actionSummarizerModel: string | null; } /** Documented defaults (design §Settings table). */ export const ULTRA_DEFAULTS: UltraSettings = { enabled: true, autoEnable: false, concurrency: 5, maxRetries: 2, subagentExtensions: "all", fanoutToolAllowlist: [...DEFAULT_FANOUT_TOOLS], subagentSkills: "all", subagentSessionRetention: "all", modelTiers: DEFAULT_MODEL_TIERS, providerModelTiers: {}, actionSummarizerModel: null, }; function isPlainObject(v: unknown): v is Record { return v !== null && typeof v === "object" && !Array.isArray(v); } function pickModelTiers( raw: unknown, prefix = "ultra.modelTiers", ): Record> | undefined { if (raw === undefined) return undefined; if (!isPlainObject(raw)) throw new Error(`${prefix} must be an object.`); const out: Record> = {}; for (const [name, value] of Object.entries(raw)) { const key = `${prefix}.${name}`; if (!/^[a-z][a-z0-9_-]*$/.test(name) || name in Object.prototype) throw new Error(`${key}: invalid tier name.`); if (!isPlainObject(value)) throw new Error(`${key} must be an object.`); const tier: Partial = {}; for (const field of Object.keys(value)) { if (!["model", "thinkingLevel", "instructions"].includes(field)) throw new Error(`${key}: unknown field ${field}.`); } if ("model" in value) { if ( value.model !== null && (typeof value.model !== "string" || !/^[^\s/:]+\/\S+$/.test(value.model) || /:(off|minimal|low|medium|high|xhigh|max)$/.test(value.model)) ) throw new Error( `${key}.model must be provider/model or null; set thinkingLevel separately.`, ); tier.model = value.model as string | null; } if ("thinkingLevel" in value) { if ( !THINKING_LEVELS.includes( value.thinkingLevel as ModelTier["thinkingLevel"], ) ) throw new Error( `${key}.thinkingLevel must be ${THINKING_LEVELS.join(", ")}.`, ); tier.thinkingLevel = value.thinkingLevel as ModelTier["thinkingLevel"]; } if ("instructions" in value) { if (typeof value.instructions !== "string" || !value.instructions.trim()) throw new Error(`${key}.instructions must be a non-empty string.`); tier.instructions = value.instructions; } out[name] = tier; } return out; } function pickProviderModelTiers(raw: unknown): ProviderModelTiers | undefined { if (raw === undefined) return undefined; if (!isPlainObject(raw)) throw new Error("ultra.providerModelTiers must be an object."); const out: ProviderModelTiers = {}; for (const [provider, tiers] of Object.entries(raw)) { if (!/^[^\s/:]+$/.test(provider) || provider in Object.prototype) throw new Error( `ultra.providerModelTiers.${provider}: invalid provider name.`, ); out[provider] = pickModelTiers(tiers, `ultra.providerModelTiers.${provider}`) ?? {}; } return out; } export function fanoutToolPolicyPrompt(allowed: readonly string[]): string { return `Fanout without writeIsolation: initial tools allowed: ${allowed.join(", ")}. Other initial tools require genuine disjoint ownership declared in writeIsolation. Without it, child prompts must forbid mutation. Tool availability is checked separately.`; } /** The sole tier rubric, rendered from the effective settings for each agent turn. */ export function modelTierPrompt(tiers: ModelTiers): string { return [ "# ultra model tier policy", "Orchestrator only. Rows: tier: model; default thinkingLevel; selection guidance.", "Default small; tiny only for determined outcomes. Choose lowest adequate tier; lower wins ties.", "Judge residual decisions, dependencies, ambiguity, and consequence; credit upstream findings.", "Decompose only when reduced decisions, context, or error propagation outweigh handoffs.", "Use minimum sufficient context; verify proportionately to risk, adding a check when one child is insufficient.", "Ask user for missing decisions; report missing child evidence or authority as blockers, not reasons to escalate tiers.", ...Object.entries(tiers).map( ([name, tier]) => `- ${name}: ${tier.model ?? "session model"}; ${tier.thinkingLevel}; ${tier.instructions}`, ), ].join("\n"); } function oneOf( key: string, value: unknown, options: readonly T[], ): T { if (!options.includes(value as T)) throw new Error( `${key} must be ${options.map((option) => `"${option}"`).join(" or ")}.`, ); return value as T; } /** Validate the merged `ultra` block and fold it over the defaults. * Tiers merge per field over DEFAULT_MODEL_TIERS; fanoutToolAllowlist extends the default tools. */ export function parseUltraSettings(raw: unknown): UltraSettings { if (!isPlainObject(raw)) throw new Error("ultra must be an object."); const settings: UltraSettings = { ...ULTRA_DEFAULTS }; for (const [field, value] of Object.entries(raw)) { const key = `ultra.${field}`; switch (field) { case "enabled": case "autoEnable": if (typeof value !== "boolean") throw new Error(`${key} must be a boolean.`); settings[field] = value; break; case "concurrency": case "maxRetries": if (typeof value !== "number") throw new Error(`${key} must be a number.`); settings[field] = value; break; case "subagentExtensions": if (value === "all" || value === "none") settings.subagentExtensions = value; else if ( Array.isArray(value) && value.every((name) => typeof name === "string" && name.trim()) ) settings.subagentExtensions = [ ...new Set(value.map((name: string) => name.trim())), ]; else throw new Error( `${key} must be "all", "none", or an array of extension names.`, ); break; case "fanoutToolAllowlist": if ( !Array.isArray(value) || value.some( (tool) => typeof tool !== "string" || !tool || /\s/u.test(tool), ) ) throw new Error( `${key} must be an array of non-empty tool names without whitespace.`, ); settings.fanoutToolAllowlist = [ ...new Set([...DEFAULT_FANOUT_TOOLS, ...value]), ]; break; case "subagentSkills": case "subagentSessionRetention": settings[field] = oneOf(key, value, ["all", "none"] as const); break; case "modelTiers": case "providerModelTiers": break; case "actionSummarizerModel": if (value !== null && (typeof value !== "string" || !value.trim())) throw new Error(`${key} must be a provider/model string or null.`); settings.actionSummarizerModel = value?.trim() ?? null; break; default: throw new Error(`unknown key ${key}.`); } } const modelTiers: Record> = {}; for (const tiers of [DEFAULT_MODEL_TIERS, pickModelTiers(raw.modelTiers)]) for (const [name, tier] of Object.entries(tiers ?? {})) modelTiers[name] = { ...modelTiers[name], ...tier }; for (const [name, tier] of Object.entries(modelTiers)) if ( tier.model === undefined || tier.thinkingLevel === undefined || tier.instructions === undefined ) throw new Error( `ultra.modelTiers.${name} requires model, thinkingLevel, and instructions.`, ); settings.modelTiers = modelTiers as ModelTiers; settings.providerModelTiers = pickProviderModelTiers(raw.providerModelTiers) ?? {}; for (const [provider, tiers] of Object.entries(settings.providerModelTiers)) for (const name of Object.keys(tiers)) if (!Object.hasOwn(modelTiers, name)) throw new Error( `ultra.providerModelTiers.${provider}.${name}: unknown tier.`, ); return settings; } // Precedence: trusted project, user, defaults; project and user blocks deep-merge. export const ULTRA_SETTING: SettingDeclaration = { key: "ultra", parse: parseUltraSettings, default: ULTRA_DEFAULTS, }; /** Resolve effective settings; invalid configuration throws through Pi's extension error path. */ export function loadUltraSettings( pi: Pick, ctx: Pick, ): UltraSettings { const result = resolveSetting(pi, ctx, ULTRA_SETTING); if (!result.ok) throw new Error(result.error); return result.value; } /** Fold provider-scoped overrides over the base tiers; unknown providers keep the base tiers. */ export function effectiveModelTiers( settings: UltraSettings, provider: string | undefined, ): ModelTiers { const overrides = provider === undefined ? undefined : settings.providerModelTiers[provider]; if (!overrides) return settings.modelTiers; const out: ModelTiers = {}; for (const [name, tier] of Object.entries(settings.modelTiers)) out[name] = { ...tier, ...overrides[name] }; return out; }