repositories / pi-ext
pi-ext
bugabingas pi extensions
owned by admin
extensions/ultra/settings.ts
Raw// 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<ExtensionAPI["setThinkingLevel"]>[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<string, ModelTier>;
/** Provider name → tier name → per-field overrides for when that provider is active. */
export type ProviderModelTiers = Record<
string,
Record<string, Partial<ModelTier>>
>;
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<string, unknown> {
return v !== null && typeof v === "object" && !Array.isArray(v);
}
function pickModelTiers(
raw: unknown,
prefix = "ultra.modelTiers",
): Record<string, Partial<ModelTier>> | undefined {
if (raw === undefined) return undefined;
if (!isPlainObject(raw)) throw new Error(`${prefix} must be an object.`);
const out: Record<string, Partial<ModelTier>> = {};
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<ModelTier> = {};
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<T extends string>(
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<string, Partial<ModelTier>> = {};
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<UltraSettings> = {
key: "ultra",
parse: parseUltraSettings,
default: ULTRA_DEFAULTS,
};
/** Resolve effective settings; invalid configuration throws through Pi's extension error path. */
export function loadUltraSettings(
pi: Pick<ExtensionAPI, "getFlag">,
ctx: Pick<ExtensionContext, "cwd" | "isProjectTrusted">,
): 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;
}