repositories / pi-ext
pi-ext
bugabingas pi extensions
owned by admin
extensions/ultra/__tests__/contract.test.ts
Rawimport {
mkdirSync,
mkdtempSync,
readdirSync,
readFileSync,
rmSync,
writeFileSync,
} from "node:fs";
import { tmpdir } from "node:os";
import { dirname, join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import {
type Context,
fauxAssistantMessage,
fauxToolCall,
type JsonObject,
} from "@earendil-works/pi-ai";
import {
DefaultPackageManager,
type ExtensionCommandContext,
type ExtensionContext,
type ExtensionToolContext,
initTheme,
SessionManager,
type Theme,
} from "@earendil-works/pi-coding-agent";
import { afterEach, describe, expect, it, vi } from "vitest";
import {
createPiSession,
type PiSession,
} from "../../../test/fixture/create-pi-session.ts";
import { resolveSubagentExtensions } from "../implementation.ts";
import ultraExtension from "../index.ts";
import {
DEFAULT_MODEL_TIERS,
modelTierPrompt,
parseUltraSettings,
} from "../settings.ts";
import { WorkflowSpecSchema } from "../spec.ts";
let session: PiSession | undefined;
let cwd: string | undefined;
let notifySpy: ReturnType<typeof vi.spyOn> | undefined;
afterEach(async () => {
await session?.dispose();
if (cwd) rmSync(cwd, { recursive: true, force: true });
session = undefined;
cwd = undefined;
notifySpy = undefined;
vi.unstubAllEnvs();
vi.restoreAllMocks();
});
function ultraPath(): string {
return fileURLToPath(new URL("../index.ts", import.meta.url));
}
function createToolContext(
overrides: Partial<ExtensionContext> = {},
toolCallId = "call",
): ExtensionToolContext {
if (!session) throw new Error("missing session");
const context = Object.create(
session.session.extensionRunner.createToolContext(toolCallId, undefined),
) as ExtensionToolContext;
if (overrides.ui)
Object.defineProperty(context, "ui", { value: overrides.ui });
if (overrides.sessionManager)
Object.defineProperty(context, "sessionManager", {
value: overrides.sessionManager,
});
return context;
}
// The owned fixture binds in print mode with a no-op UI context. Replace it with
// a recording UI (hasUI becomes true, so overlay-driven surfaces run) and spy on
// notify to capture the UI's notify calls.
function installUi(): void {
if (!session) throw new Error("missing session");
const runner = session.session.extensionRunner;
const ui = { ...runner.getUIContext() };
notifySpy = vi.spyOn(ui, "notify");
runner.setUIContext(ui, "print");
}
function uiCallsFor(method: "notify"): Array<{ args: unknown[] }> {
if (method !== "notify") return [];
return (notifySpy?.mock.calls ?? []).map((args: unknown[]) => ({ args }));
}
async function start(
options: {
settings?: Record<string, unknown> | null;
env?: Record<string, string | undefined>;
factory?: boolean;
extra?: string[];
} = {},
) {
cwd = mkdtempSync(join(tmpdir(), "ultra-contract-"));
if (options.settings !== null) {
mkdirSync(join(cwd, ".pi"), { recursive: true });
writeFileSync(
join(cwd, ".pi", "settings.json"),
JSON.stringify(options.settings ?? { ultra: { autoEnable: false } }),
);
}
session = await createPiSession({
cwd,
...(options.env ? { env: options.env } : {}),
...(options.factory
? { extensionFactories: [ultraExtension] }
: { extensionPaths: [ultraPath(), ...(options.extra ?? [])] }),
});
installUi();
return session.extensions[0]!;
}
function saveWorkflow(name: string, dynamicExtension?: string): void {
if (!session) throw new Error("missing session");
const directory = join(session.cwd, ".pi", "workflows");
mkdirSync(directory, { recursive: true });
writeFileSync(
join(directory, `${name}.json`),
`${JSON.stringify({
name,
phases: [
{
id: "work",
kind: "single",
step: {
summary: "Work",
prompt: "Work",
...(dynamicExtension
? { dynamicExtensions: [dynamicExtension] }
: {}),
},
},
],
})}\n`,
);
}
function driveOverlay(
ctx: ExtensionCommandContext,
interact?: (overlay: { handleInput(data: string): void }) => void,
): void {
Object.assign(ctx.ui, {
custom: vi.fn(
async (
factory: (
host: unknown,
theme: unknown,
keybindings: unknown,
done: (value: unknown) => void,
) => { handleInput(data: string): void },
) =>
new Promise((resolve) => {
const overlay = factory(
{ requestRender: vi.fn() },
{},
{ matches: () => false },
resolve,
);
interact?.(overlay);
}),
),
});
}
describe("ultra extension", () => {
it("resolves explicit Pi sources through the SDK without installing them", async () => {
const resolveSources = vi
.spyOn(DefaultPackageManager.prototype, "resolveExtensionSources")
.mockResolvedValue({
extensions: [
{ path: "/enabled/index.ts", enabled: true },
{ path: "/disabled/index.ts", enabled: false },
],
} as never);
const selection = await resolveSubagentExtensions(
parseUltraSettings({
subagentExtensions: [
"npm:@scope/pi-extension",
"https://github.com/example/pi-extension.git",
],
}).subagentExtensions,
{
cwd: process.cwd(),
agentDir: join(process.cwd(), ".agent"),
projectTrusted: false,
},
);
expect(resolveSources).toHaveBeenNthCalledWith(
1,
["npm:@scope/pi-extension"],
{ temporary: true },
);
expect(resolveSources).toHaveBeenNthCalledWith(
2,
["https://github.com/example/pi-extension.git"],
{ temporary: true },
);
expect(selection.paths).toEqual(["/enabled/index.ts"]);
const path = resolve("/enabled/index.ts").replaceAll("\\", "/");
expect(selection.pathKeys).toEqual(
new Set([process.platform === "win32" ? path.toLowerCase() : path]),
);
});
it("resolves relative sources from cwd regardless of project trust", async () => {
cwd = mkdtempSync(join(tmpdir(), "ultra-relative-source-"));
mkdirSync(join(cwd, "research"));
const entry = join(cwd, "research", "evidence.ts");
writeFileSync(entry, "export default function () {}");
writeFileSync(
join(cwd, "research", "package.json"),
JSON.stringify({ pi: { extensions: ["evidence.ts"] } }),
);
for (const projectTrusted of [false, true]) {
const options = {
cwd,
agentDir: join(cwd, "unrelated-agent"),
projectTrusted,
};
expect(
(await resolveSubagentExtensions(["./research"], options)).paths,
).toEqual([entry]);
expect(
(await resolveSubagentExtensions(["research"], options)).paths,
).toEqual([entry]);
await expect(
resolveSubagentExtensions(["./research", "./missing.ts"], options),
).rejects.toThrow("no enabled extensions resolved");
}
});
it("rejects legacy bare extension nicknames with migration guidance", async () => {
await expect(
resolveSubagentExtensions(["nushell"], {
cwd: process.cwd(),
agentDir: join(process.cwd(), ".agent"),
projectTrusted: true,
}),
).rejects.toThrow(/no longer accepts bare extension nickname/u);
});
it("loads only the command and renderer in the real Pi runtime", async () => {
const indexPath = ultraPath();
const extension = await start();
expect(extension.path).toBe(indexPath);
expect(extension.tools.has("run_workflow")).toBe(false);
expect(readFileSync(indexPath, "utf8")).toContain(
'import("./implementation.ts")',
);
expect(extension.commands.has("ultra")).toBe(true);
expect(extension.commands.has("ultra-runs")).toBe(false);
});
it("writes disabled command outcome in its debug sandbox", async () => {
const extension = await start({
settings: null,
env: { PI_ULTRA_DEBUG: "1" },
});
const runner = session?.session.extensionRunner;
if (!runner || !session) throw new Error("missing session");
const context = runner.createCommandContext();
mkdirSync(join(session.cwd, ".pi"), { recursive: true });
writeFileSync(
join(session.cwd, ".pi", "settings.json"),
JSON.stringify({ ultra: { enabled: false } }),
);
await extension.commands.get("ultra")!.handler("", context);
await runner.emit({ type: "session_shutdown", reason: "quit" });
const directory = join(
session.cwd,
".test-state",
"pi-ext",
"debug",
"ultra",
);
const records = readFileSync(
join(directory, readdirSync(directory)[0]!),
"utf8",
)
.trim()
.split("\n")
.map((line) => JSON.parse(line));
expect(
records.filter((record) => record.event === "command.handle.start"),
).toHaveLength(1);
expect(
records.filter((record) => record.event === "command.handle.finish"),
).toEqual([expect.objectContaining({ outcome: "disabled" })]);
expect(records.map((record) => record.event)).toEqual(
expect.arrayContaining(["session.start", "session.shutdown"]),
);
});
it("auto-enables the workflow tool when configured", async () => {
const extension = await start({
settings: { ultra: { autoEnable: true } },
});
if (!session) throw new Error("missing session");
expect(extension.tools.has("run_workflow")).toBe(true);
expect(session.session.getActiveToolNames()).toContain("run_workflow");
expect(session.session.systemPrompt).toContain("- run_workflow:");
});
it.each([false, true])(
"registers the authoring skill independently of tool activation (auto-enable: %s)",
async (autoEnable) => {
const extension = await start({ settings: { ultra: { autoEnable } } });
if (!session) throw new Error("missing session");
const skillPath = fileURLToPath(
new URL("../prompts/ultra-authoring/SKILL.md", import.meta.url),
);
const skill = session.session.resourceLoader
.getSkills()
.skills.find((candidate) => candidate.name === "ultra-authoring");
expect(skill?.filePath).toBe(skillPath);
expect(session.session.systemPrompt).toContain(skillPath);
expect(session.session.systemPrompt).not.toContain(
readFileSync(skillPath, "utf8").trim(),
);
expect(session.session.systemPrompt).not.toContain("## Worked example");
if (!autoEnable) {
await extension.commands
.get("ultra")!
.handler("", session.session.extensionRunner.createCommandContext());
}
expect(extension.tools.get("run_workflow")!.definition.exposure).toBe(
"model-only",
);
expect(
extension.tools.get("run_workflow")!.definition.annotations,
).toEqual({
readOnlyHint: false,
});
const rules: string[] =
extension.tools.get("run_workflow")!.definition.promptGuidelines!;
expect(rules).toEqual([
"Use run_workflow when decomposition into independent or sequential child-agent steps reduces context or verification risk.",
"Use dynamic extensions when a workflow needs a reusable, task-specific capability; read the ultra-authoring skill for how to build and load them.",
]);
for (const rule of rules)
expect(session.session.systemPrompt.split(rule)).toHaveLength(2);
session.session.setActiveToolsByName(
session.session
.getActiveToolNames()
.filter((name) => name !== "run_workflow"),
);
expect(session.session.systemPrompt).toContain(skillPath);
for (const rule of rules)
expect(session.session.systemPrompt).not.toContain(rule);
await extension.commands
.get("ultra")!
.handler("", session.session.extensionRunner.createCommandContext());
for (const rule of rules)
expect(session.session.systemPrompt.split(rule)).toHaveLength(2);
expect(session.session.messages).toHaveLength(0);
},
);
it("does not advertise the skill when Ultra is disabled, even after completion or activation attempts", async () => {
const extension = await start({
settings: { ultra: { enabled: false, autoEnable: true } },
});
if (!session) throw new Error("missing session");
const command = extension.commands.get("ultra")!;
await command.getArgumentCompletions!("");
await command.handler(
"",
session.session.extensionRunner.createCommandContext(),
);
expect(session.session.getActiveToolNames()).not.toContain("run_workflow");
expect(session.session.systemPrompt).not.toContain(
"ultra-authoring/SKILL.md",
);
});
it("injects the effective tier policy only while active and refreshes settings each turn", async () => {
const extension = await start();
if (!session) throw new Error("missing session");
const runner = session.session.extensionRunner;
const emit = () =>
runner.emitBeforeAgentStart(
{ messageType: "user", prompt: "prompt" },
{
cwd: session?.cwd ?? process.cwd(),
},
);
const policy = async () => (await emit()).systemPromptOptions.sections;
expect((await policy())["ultra_model_tier_policy"]).toBeUndefined();
expect((await policy())["ultra_fanout_tool_policy"]).toBeUndefined();
await extension.commands
.get("ultra")!
.handler("", runner.createCommandContext());
expect((await policy())["ultra_model_tier_policy"]).toBe(
modelTierPrompt(DEFAULT_MODEL_TIERS),
);
expect((await policy())["ultra_fanout_tool_policy"]).toContain(
"read, grep, find, ls, bash",
);
writeFileSync(
join(session.cwd, ".pi", "settings.json"),
JSON.stringify({
ultra: {
fanoutToolAllowlist: ["web"],
modelTiers: {
large: { instructions: "Architecture only." },
fast_extract: {
model: "custom/extractor",
thinkingLevel: "low",
instructions: "Select for literal extraction.",
},
},
},
}),
);
const updated = await policy();
expect(updated["ultra_model_tier_policy"]).toContain("Architecture only.");
expect(updated["ultra_model_tier_policy"]).not.toContain(
DEFAULT_MODEL_TIERS["large"]!.instructions,
);
expect(updated["ultra_model_tier_policy"]).toContain(
"- fast_extract: custom/extractor; low; Select for literal extraction.",
);
expect(
updated["ultra_model_tier_policy"]?.match(/# ultra model tier policy/g),
).toHaveLength(1);
expect(updated["ultra_fanout_tool_policy"]).toContain("bash, web");
writeFileSync(
join(session.cwd, ".pi", "settings.json"),
JSON.stringify({ ultra: { enabled: false } }),
);
expect((await policy())["ultra_model_tier_policy"]).toBeUndefined();
expect((await policy())["ultra_fanout_tool_policy"]).toBeUndefined();
});
it("advertises which step.flags enable sub-agent extension tools while active", async () => {
const extension = await start({
settings: {
ultra: {
subagentExtensions: [
fileURLToPath(new URL("../../nushell/index.ts", import.meta.url)),
fileURLToPath(
new URL("../../chrome-cdp/index.ts", import.meta.url),
),
fileURLToPath(new URL("../../ask/ask-tool.ts", import.meta.url)),
],
},
},
});
if (!session) throw new Error("missing session");
const runner = session.session.extensionRunner;
const sections = async () =>
(
await runner.emitBeforeAgentStart(
{ messageType: "user", prompt: "prompt" },
{
cwd: session?.cwd ?? process.cwd(),
},
)
).systemPromptOptions.sections;
expect((await sections())["ultra_subagent_flags"]).toBeUndefined();
await extension.commands
.get("ultra")!
.handler("", runner.createCommandContext());
const prompt = (await sections())["ultra_subagent_flags"] ?? "";
expect(prompt).toContain("# ultra sub-agent flags");
expect(prompt).toContain(
"- nushell: nu; --nushell (boolean): Enable Nushell tools at startup instead of bash and powershell",
);
expect(prompt).toContain("- chrome-cdp: chrome_cdp; --chrome (boolean)");
// ask registers tools but no flags, so it is not listed.
expect(prompt).not.toContain("- ask:");
});
it("rejects unknown tiers before either surface starts work", async () => {
const extension = await start();
if (!session) throw new Error("missing session");
const ctx = session.session.extensionRunner.createCommandContext();
await extension.commands.get("ultra")!.handler("", ctx);
const spec = {
name: "unknown-tier",
phases: [
{
id: "first",
kind: "single",
step: { summary: "First", prompt: "First", model: "medium" },
},
{
id: "second",
kind: "single",
step: { summary: "Second", prompt: "Second", model: "missing_tier" },
},
],
};
const tool = extension.tools.get("run_workflow")!.definition;
await expect(
tool.execute(
"call",
{ spec },
undefined,
undefined,
createToolContext({ ui: ctx.ui, sessionManager: ctx.sessionManager }),
),
).rejects.toThrow('unknown model tier "missing_tier"');
const directory = join(session.cwd, ".pi", "workflows");
mkdirSync(directory, { recursive: true });
writeFileSync(join(directory, "unknown-tier.json"), JSON.stringify(spec));
const custom = vi.fn();
Object.assign(ctx.ui, { custom });
await extension.commands.get("ultra")!.handler("run unknown-tier", ctx);
expect(custom).not.toHaveBeenCalled();
expect(uiCallsFor("notify").at(-1)?.args).toEqual([
'ultra: unknown model tier "missing_tier". Configure ultra.modelTiers or use provider/model.',
"error",
]);
});
it.each(["command", "tool"])(
"inherits the parent's session directory through the %s surface",
async (surface) => {
const extension = await start({ factory: true });
if (!session) throw new Error("missing session");
saveWorkflow("session-directory");
const sessionDir = join(session.cwd, "custom-sessions");
const parent = SessionManager.create(session.cwd, sessionDir);
const ctx = {
...session.session.extensionRunner.createCommandContext(),
sessionManager: parent,
};
const create = vi
.spyOn(SessionManager, "create")
.mockImplementation(() => {
throw new Error("Stop before provider access");
});
if (surface === "command") {
driveOverlay(ctx as unknown as ExtensionCommandContext);
await extension.commands
.get("ultra")!
.handler("run session-directory", ctx);
} else {
await extension.commands.get("ultra")!.handler("", ctx);
await extension.tools.get("run_workflow")!.definition.execute(
"call",
{ name: "session-directory" },
undefined,
undefined,
createToolContext({
ui: ctx.ui,
sessionManager: parent,
}),
);
}
expect(create).toHaveBeenCalledWith(session.cwd, parent.getSessionDir(), {
parentSession: parent.getSessionFile(),
});
},
);
it.each([false, true])(
"bounds orchestrator content while retaining UI details (oversized: %s)",
async (oversized) => {
const extension = await start();
const ctx = session?.session.extensionRunner.createCommandContext();
if (!ctx) throw new Error("missing session");
await extension.commands.get("ultra")!.handler("", ctx);
const answer = oversized ? "界".repeat(30_000) : "Final answer";
const response = await extension.tools
.get("run_workflow")!
.definition.execute(
"call",
{
spec: {
name: "context-budget",
phases: [
{
id: "work",
kind: "single",
when: "{args.run}",
step: { summary: "Unused", prompt: "Unused" },
},
],
return: "{args.answer}",
},
args: { run: false, answer },
},
undefined,
undefined,
createToolContext(),
);
const details = response.details as {
workflowResult: {
result: unknown;
phaseResults: Record<string, unknown[]>;
};
fullOutputPath?: string;
};
const body = JSON.parse((response.content[0] as { text: string }).text);
try {
expect(details.workflowResult.result).toBe(answer);
expect(details.workflowResult.phaseResults).toEqual({
work: [],
});
expect(body.phaseResults).toBeUndefined();
if (oversized) {
expect(body.truncated).toBe(true);
expect(body.fullOutputPath).toBe(details.fullOutputPath);
expect({
...JSON.parse(readFileSync(body.fullOutputPath, "utf8")),
fullOutputPath: body.fullOutputPath,
}).toEqual(details.workflowResult);
} else {
expect(body.fullOutputPath).toBe(details.fullOutputPath);
expect(
JSON.parse(readFileSync(body.fullOutputPath, "utf8")).result,
).toBe(answer);
expect(body.result).toBe(answer);
expect(body.phases).toEqual([
{ id: "work", ran: 0, ok: 0, dropped: 0 },
]);
expect(body.truncated).toBeUndefined();
}
} finally {
if (details.fullOutputPath)
rmSync(dirname(details.fullOutputPath), {
recursive: true,
force: true,
});
}
},
);
it.each(["command-output", "review", "research"])(
"keeps %s command context bounded and its evidence retrievable",
async (name) => {
const extension = await start();
if (!session) throw new Error("missing session");
saveWorkflow(name);
const ctx = session.session.extensionRunner.createCommandContext();
const result = {
workflow: name,
phases: [{ id: "work", ran: 1, ok: 1, dropped: 0 }],
phaseResults: {
work: [{ ok: true, value: "INTERMEDIATE".repeat(10000) }],
},
phaseFailures: {},
result: [{ status: "blocked", blockers: ["Missing evidence"] }],
report: { summary: "Human report" },
steered: false,
dynamicExtensions: [],
tokenUsage: {
input: 1,
output: 2,
total: 3,
cacheRead: 0,
cacheWrite: 0,
cost: 0,
},
};
Object.assign(ctx.ui, {
custom: vi.fn().mockResolvedValue({ status: "completed", result }),
});
await extension.commands.get("ultra")!.handler(`run ${name}`, ctx);
expect(
uiCallsFor("notify").some(
(call) =>
call.args[0] ===
`ultra: "${name}" complete · 1 phases · $0.0000 · 2 output.`,
),
).toBe(true);
const message = session.session.messages.find(
(message): message is Extract<typeof message, { role: "custom" }> =>
"customType" in message && message.customType === "ultra-result",
);
if (!message || typeof message.content !== "string")
throw new Error("Missing command result");
const body = JSON.parse(message.content);
try {
expect(body.result).toEqual(result.result);
expect(body.report).toEqual(result.report);
expect(body.executionStatus).toBe("finished");
expect(body.phaseResults).toBeUndefined();
expect(message.content).not.toContain("INTERMEDIATE");
expect(message.details).toEqual({
...result,
fullOutputPath: body.fullOutputPath,
});
expect(JSON.parse(readFileSync(body.fullOutputPath, "utf8"))).toEqual(
result,
);
} finally {
rmSync(dirname(body.fullOutputPath), { recursive: true, force: true });
}
},
);
it("completes subcommands first and saved workflows below run", async () => {
const extension = await start();
if (!session) throw new Error("missing session");
const complete = extension.commands.get("ultra")!.getArgumentCompletions!;
expect(extension.tools.has("run_workflow")).toBe(false);
const subcommands = (await complete(""))!;
const workflows = (await complete("run "))!;
expect(extension.tools.has("run_workflow")).toBe(true);
expect(session.session.getActiveToolNames()).not.toContain("run_workflow");
expect(subcommands.map((item: { value: string }) => item.value)).toEqual([
"exec",
"run",
]);
expect(workflows.map((item: { value: string }) => item.value)).toEqual(
expect.arrayContaining(["run research", "run review"]),
);
});
it("activates the workflow tool and notifies without starting a turn", async () => {
const extension = await start();
if (!session) throw new Error("missing session");
const beforeMessages = session.session.messages.length;
const beforeNotifications = uiCallsFor("notify").length;
expect(session.session.getActiveToolNames()).not.toContain("run_workflow");
expect(session.session.systemPrompt).not.toContain("- run_workflow:");
await extension.commands
.get("ultra")!
.handler("", session.session.extensionRunner.createCommandContext());
expect(session.session.getActiveToolNames()).toContain("run_workflow");
expect(session.session.systemPrompt).toContain(
"- run_workflow: run saved or inline multi-agent workflows.",
);
expect(session.session.systemPrompt).toContain(
"Use run_workflow when decomposition into independent or sequential child-agent steps reduces context or verification risk.",
);
expect(session.session.messages).toHaveLength(beforeMessages);
const notifications = uiCallsFor("notify");
expect(notifications).toHaveLength(beforeNotifications + 1);
expect(notifications.at(-1)?.args).toEqual([
"ultra: run_workflow tool enabled.",
"info",
]);
});
it("sends exec instructions as the visible user message that starts the turn", async () => {
const extension = await start();
if (!session) throw new Error("missing session");
const sendUserMessage = vi
.spyOn(session.session, "sendUserMessage")
.mockResolvedValue();
const beforeNotifications = uiCallsFor("notify").length;
await extension.commands
.get("ultra")!
.handler(
"exec inspect this failure once",
session.session.extensionRunner.createCommandContext(),
);
expect(session.session.getActiveToolNames()).toContain("run_workflow");
expect(sendUserMessage).toHaveBeenCalledOnce();
const prompt = sendUserMessage.mock.calls[0]?.[0];
expect(prompt).toBe(
"# ultra exec\n\n# user request\n\ninspect this failure once",
);
const guidance =
extension.tools.get("run_workflow")!.definition.promptGuidelines!;
for (const rule of guidance) {
expect(session.session.systemPrompt.split(rule)).toHaveLength(2);
expect(prompt).not.toContain(rule);
}
expect(session.session.systemPrompt).not.toContain(
"# Authoring ultra workflows",
);
expect(session.session.systemPrompt).not.toContain("## Worked example");
expect(prompt).not.toContain("The workflow tool is now available");
const injected = await session.session.extensionRunner.emitBeforeAgentStart(
{ messageType: "user", prompt: prompt as string },
{ cwd: session.cwd },
);
expect(
injected.systemPromptOptions.sections["ultra_model_tier_policy"],
).toBe(modelTierPrompt(DEFAULT_MODEL_TIERS));
expect(injected.systemPromptOptions.forceSystemPrompt).toBeUndefined();
expect(prompt).not.toContain(DEFAULT_MODEL_TIERS["large"]!.instructions);
expect(uiCallsFor("notify")).toHaveLength(beforeNotifications);
});
it("renders workflow progress as an overlay without replacing session content", async () => {
const extension = await start();
if (!session) throw new Error("missing session");
const ctx = session.session.extensionRunner.createCommandContext();
const custom = vi.fn().mockResolvedValue(null);
Object.assign(ctx.ui, { custom });
await extension.commands
.get("ultra")!
.handler("run review inspect scrolling", ctx);
expect(custom).toHaveBeenCalledOnce();
expect(custom.mock.calls[0]![1]).toEqual({
overlay: true,
overlayOptions: {
anchor: "bottom-left",
width: "100%",
maxHeight: "100%",
},
});
});
it("surfaces workflow errors instead of misreporting them as user aborts", async () => {
const extension = await start();
if (!session) throw new Error("missing session");
saveWorkflow("runtime-error", "missing-live-capability");
const ctx = session.session.extensionRunner.createCommandContext();
driveOverlay(ctx);
await extension.commands.get("ultra")!.handler("run runtime-error", ctx);
expect(uiCallsFor("notify").at(-1)?.args).toEqual([
'ultra: dynamic extension "missing-live-capability" has no canonical revision. Run a builder with all four dynamic-extension tools.',
"error",
]);
});
it("prepares dynamic extension runs on both command and tool surfaces", async () => {
const message =
'ultra: dynamic extension "missing-live-capability" has no canonical revision. Run a builder with all four dynamic-extension tools.';
const extension = await start();
if (!session) throw new Error("missing session");
saveWorkflow("runtime-error-command", "missing-live-capability");
saveWorkflow("runtime-error-tool", "missing-live-capability");
const ctx = session.session.extensionRunner.createCommandContext();
driveOverlay(ctx);
await extension.commands
.get("ultra")!
.handler("run runtime-error-command", ctx);
expect(uiCallsFor("notify").at(-1)?.args).toEqual([message, "error"]);
await extension.commands.get("ultra")!.handler("", ctx);
await expect(
extension.tools
.get("run_workflow")!
.definition.execute(
"call",
{ name: "runtime-error-tool" },
undefined,
undefined,
createToolContext({ ui: ctx.ui }),
),
).rejects.toThrow(message);
});
it("refuses project workflows on both surfaces when the project is untrusted", async () => {
const extension = await start();
if (!session) throw new Error("missing session");
saveWorkflow("repo-owned");
const path = join(session.cwd, ".pi", "workflows", "repo-owned.json");
const message = `ultra: refused workflow "repo-owned" from untrusted project file ${path}. Project workflows are repo-controlled; trust this project to run it.`;
session.session.settingsManager.setProjectTrusted(false);
vi.spyOn(process, "cwd").mockReturnValue(session.cwd);
const ctx = session.session.extensionRunner.createCommandContext();
expect(ctx.isProjectTrusted()).toBe(false);
const command = extension.commands.get("ultra")!;
const completions = await command.getArgumentCompletions!("run ");
expect(completions?.map((item) => item.value)).not.toContain(
"run repo-owned",
);
expect(completions?.map((item) => item.value)).toContain("run review");
const custom = vi.fn();
Object.assign(ctx.ui, { custom });
await command.handler("run repo-owned", ctx);
expect(custom).not.toHaveBeenCalled();
expect(uiCallsFor("notify").at(-1)?.args).toEqual([message, "error"]);
await command.handler("", ctx);
await expect(
extension.tools
.get("run_workflow")!
.definition.execute(
"call",
{ name: "repo-owned" },
undefined,
undefined,
createToolContext({ ui: ctx.ui }),
),
).rejects.toThrow(message);
});
it("offers project workflows with their source when the project is trusted", async () => {
const extension = await start();
if (!session) throw new Error("missing session");
saveWorkflow("repo-owned");
vi.spyOn(process, "cwd").mockReturnValue(session.cwd);
const ctx = session.session.extensionRunner.createCommandContext();
expect(ctx.isProjectTrusted()).toBe(true);
const completions =
await extension.commands.get("ultra")!.getArgumentCompletions!("run re");
expect(completions).toEqual(
expect.arrayContaining([
{
value: "run repo-owned",
label: "repo-owned",
description: "project",
},
{ value: "run review", label: "review", description: "bundled" },
]),
);
});
it("reports confirmed whole-run cancellation as aborted while retaining its aggregate", async () => {
const extension = await start();
if (!session) throw new Error("missing session");
saveWorkflow("cancelled-run");
const ctx = session.session.extensionRunner.createCommandContext();
driveOverlay(ctx, (overlay) => {
overlay.handleInput("\u001b");
overlay.handleInput("\u001b");
});
await extension.commands.get("ultra")!.handler("run cancelled-run", ctx);
expect(uiCallsFor("notify").at(-1)?.args).toEqual([
'ultra: "cancelled-run" aborted · 1 phases · $0.0000 · 0 output.',
"warning",
]);
expect(
uiCallsFor("notify").some((call) =>
String(call.args[0]).includes("complete"),
),
).toBe(false);
const message = session.session.messages.find(
(message): message is Extract<typeof message, { role: "custom" }> =>
"customType" in message && message.customType === "ultra-result",
);
const body = JSON.parse(String(message?.content));
try {
expect(body.executionStatus).toBe("aborted");
expect(
JSON.parse(readFileSync(body.fullOutputPath, "utf8")).aborted,
).toBe(true);
} finally {
rmSync(dirname(body.fullOutputPath), { recursive: true, force: true });
}
});
it("refuses a legacy bare sub-agent extension nickname", async () => {
const extension = await start({
settings: { ultra: { subagentExtensions: ["missing-extension"] } },
});
if (!session) throw new Error("missing session");
await extension.commands
.get("ultra")!
.handler(
"run review",
session.session.extensionRunner.createCommandContext(),
);
expect(uiCallsFor("notify").at(-1)?.args).toEqual([
'ultra: subagentExtensions no longer accepts bare extension nickname "missing-extension"; replace it with a Pi extension source path or package source.',
"error",
]);
});
it.each(["command", "tool"])(
"names excluded tool owners and startup flags through the %s surface",
async (surface) => {
const extension = await start({
settings: {
ultra: {
subagentExtensions: [
fileURLToPath(new URL("../../ask/index.ts", import.meta.url)),
],
},
},
env: { PI_NUSHELL_ENABLE: "false" },
extra: [
fileURLToPath(new URL("../../nushell/index.ts", import.meta.url)),
],
});
if (!session) throw new Error("missing session");
const spec = {
name: "excluded-nushell",
phases: [
{
id: "work",
kind: "single",
step: {
summary: "Work",
prompt: "Work",
tools: ["nu"],
flags: { nushell: true },
},
},
],
};
const ctx = session.session.extensionRunner.createCommandContext();
await extension.commands.get("ultra")!.handler("", ctx);
let message: string;
if (surface === "command") {
const directory = join(session.cwd, ".pi", "workflows");
mkdirSync(directory, { recursive: true });
writeFileSync(
join(directory, `${spec.name}.json`),
JSON.stringify(spec),
);
await extension.commands.get("ultra")!.handler(`run ${spec.name}`, ctx);
message = String(uiCallsFor("notify").at(-1)?.args[0]);
} else {
message = await extension.tools
.get("run_workflow")!
.definition.execute(
"call",
{ spec },
undefined,
undefined,
createToolContext(),
)
.then(
() => "unexpected success",
(error: Error) => error.message,
);
}
expect(message).toContain("requires unavailable tools: nu");
expect(message).toContain(
'owning extension "nushell" is excluded by ultra.subagentExtensions',
);
expect(message).toContain(
`include ${JSON.stringify(resolve("extensions/nushell/index.ts"))} in that setting before using its step.flags`,
);
expect(message).toContain("- nushell: nu; --nushell (boolean)");
expect(message).not.toContain("unexpected success");
},
);
it("diagnoses a loaded JS owner after package selection changes to TS without widening authorization", async () => {
await start();
if (!session) throw new Error("missing session");
const extensionDir = join(session.cwd, "owner-fixture");
mkdirSync(extensionDir);
const priorPath = join(extensionDir, "prior.js");
const replacementPath = join(extensionDir, "replacement.ts");
const otherPath = join(extensionDir, "other.ts");
const source = (flag: string) =>
[
'import { Type } from "typebox";',
"export default (pi) => {",
` pi.registerFlag(${JSON.stringify(flag)}, { type: "boolean" });`,
' pi.registerTool({ name: "fixture_tool", label: "fixture_tool", description: "fixture", parameters: Type.Object({}), execute: async () => ({ content: [{ type: "text", text: "ok" }] }) });',
"};",
"",
].join("\n");
writeFileSync(priorPath, source("prior-owner"));
writeFileSync(replacementPath, source("replacement-owner"));
writeFileSync(otherPath, source("other-owner"));
writeFileSync(
join(extensionDir, "package.json"),
JSON.stringify({ pi: { extensions: ["prior.js"] } }),
);
await session.dispose();
writeFileSync(
join(session.cwd, ".pi", "settings.json"),
JSON.stringify({ ultra: { subagentExtensions: [extensionDir] } }),
);
session = await createPiSession({
cwd: cwd!,
extensionPaths: [ultraPath(), priorPath],
});
installUi();
writeFileSync(
join(extensionDir, "package.json"),
JSON.stringify({ pi: { extensions: ["replacement.ts"] } }),
);
const spec = {
name: "changed-entry",
phases: [
{
id: "work",
kind: "single",
step: {
summary: "Work",
prompt: "Work",
tools: ["fixture_tool"],
},
},
],
};
const createChild = vi.spyOn(SessionManager, "inMemory");
const staleExtension = session.extensions.find((item) =>
item.commands.has("ultra"),
)!;
await staleExtension.commands
.get("ultra")!
.handler("", session.session.extensionRunner.createCommandContext());
const mismatch = await staleExtension.tools
.get("run_workflow")!
.definition.execute(
"call",
{ spec },
undefined,
undefined,
createToolContext(),
)
.then(
() => "unexpected success",
(error: Error) => error.message,
);
expect(mismatch).toContain("loaded owning extension");
expect(mismatch).toContain("does not match selected extension entries");
expect(mismatch).toContain(
"runtime may be stale or explicit selection differs",
);
expect(mismatch).toContain("Reload/restart Pi");
expect(mismatch).not.toContain("include");
expect(mismatch).toContain("replacement-owner");
expect(mismatch).not.toContain("prior-owner");
expect(createChild).not.toHaveBeenCalled();
createChild.mockRestore();
await session.dispose();
session = await createPiSession({
cwd: cwd!,
extensionPaths: [ultraPath(), replacementPath],
});
installUi();
const freshExtension = session.extensions.find((item) =>
item.commands.has("ultra"),
)!;
await freshExtension.commands
.get("ultra")!
.handler("", session.session.extensionRunner.createCommandContext());
const reachedChildCreation = vi
.spyOn(SessionManager, "inMemory")
.mockImplementation(() => {
throw new Error("Stop before provider access");
});
const freshResponse = await freshExtension.tools
.get("run_workflow")!
.definition.execute(
"call",
{ spec },
undefined,
undefined,
createToolContext(),
);
expect(reachedChildCreation).toHaveBeenCalled();
expect(freshResponse.details).toBeDefined();
reachedChildCreation.mockRestore();
await session.dispose();
writeFileSync(
join(session.cwd, ".pi", "settings.json"),
JSON.stringify({ ultra: { subagentExtensions: [otherPath] } }),
);
session = await createPiSession({
cwd: cwd!,
extensionPaths: [ultraPath(), priorPath],
});
installUi();
const excludedExtension = session.extensions.find((item) =>
item.commands.has("ultra"),
)!;
await excludedExtension.commands
.get("ultra")!
.handler("", session.session.extensionRunner.createCommandContext());
const noChild = vi.spyOn(SessionManager, "inMemory");
const excluded = await excludedExtension.tools
.get("run_workflow")!
.definition.execute(
"call",
{ spec },
undefined,
undefined,
createToolContext(),
)
.then(
() => "unexpected success",
(error: Error) => error.message,
);
expect(excluded).toContain("does not match selected extension entries");
expect(excluded).toContain(JSON.stringify(otherPath));
expect(excluded).not.toContain("include");
expect(noChild).not.toHaveBeenCalled();
});
it("reports unknown tools without inventing an owning extension or flag", async () => {
const extension = await start();
if (!session) throw new Error("missing session");
const ctx = session.session.extensionRunner.createCommandContext();
await extension.commands.get("ultra")!.handler("", ctx);
await expect(
extension.tools.get("run_workflow")!.definition.execute(
"call",
{
spec: {
name: "unknown-tool",
phases: [
{
id: "work",
kind: "single",
step: {
summary: "Work",
prompt: "Work",
tools: ["not_installed"],
},
},
],
},
},
undefined,
undefined,
createToolContext(),
),
).rejects.toThrow(
"not_installed: no registered owning extension; check step.tools and installed extensions.",
);
});
describe("inactive-tool preflight", () => {
const nushellPath = () =>
fileURLToPath(new URL("../../nushell/index.ts", import.meta.url));
function specWith(step: Record<string, unknown>) {
return {
name: "inactive-preflight",
phases: [
{
id: "work",
kind: "fanout",
over: ["a", "b", "c"],
step: { summary: "Work {item}", prompt: "Work {item}", ...step },
},
],
};
}
function deactivate(...names: string[]): void {
if (!session) throw new Error("missing session");
session.session.setActiveToolsByName(
session.session
.getActiveToolNames()
.filter((name) => !names.includes(name)),
);
}
it.each(["command", "tool"])(
"refuses a step listing a registered-but-inactive tool before creating children through the %s surface",
async (surface) => {
const extension = await start({
settings: { ultra: { subagentExtensions: [nushellPath()] } },
});
if (!session) throw new Error("missing session");
// Like `pi --nushell`: bash is registered but switched off in this Pi.
deactivate("bash");
expect(session.session.getActiveToolNames()).not.toContain("bash");
const spec = specWith({ tools: ["read", "bash"] });
const ctx = session.session.extensionRunner.createCommandContext();
const create = vi.spyOn(SessionManager, "create");
const inMemory = vi.spyOn(SessionManager, "inMemory");
let message: string;
if (surface === "command") {
const directory = join(session.cwd, ".pi", "workflows");
mkdirSync(directory, { recursive: true });
writeFileSync(
join(directory, `${spec.name}.json`),
JSON.stringify(spec),
);
const custom = vi.fn();
Object.assign(ctx.ui, { custom });
await extension.commands
.get("ultra")!
.handler(`run ${spec.name}`, ctx);
expect(custom).not.toHaveBeenCalled();
expect(uiCallsFor("notify").at(-1)?.args[1]).toBe("error");
message = String(uiCallsFor("notify").at(-1)?.args[0]);
} else {
await extension.commands.get("ultra")!.handler("", ctx);
message = await extension.tools
.get("run_workflow")!
.definition.execute(
"call",
{ spec },
undefined,
undefined,
createToolContext(),
)
.then(
() => "unexpected success",
(error: Error) => error.message,
);
}
expect(message).toBe(
"ultra: step tools are inactive in this Pi and children inherit its flags: bash. Possibly deactivated by an extension with startup flags: nushell (--nushell: Enable Nushell tools at startup instead of bash and powershell). Use an active alternative or set step.flags.",
);
expect(create).not.toHaveBeenCalled();
expect(inMemory).not.toHaveBeenCalled();
},
);
it("recommends the active nu tool when bash is inactive", async () => {
const extension = await start({
settings: { ultra: { subagentExtensions: [nushellPath()] } },
env: { PI_NUSHELL_ENABLE: "false" },
extra: [nushellPath()],
});
if (!session) throw new Error("missing session");
deactivate("bash");
session.session.setActiveToolsByName([
...session.session.getActiveToolNames(),
"nu",
]);
const ctx = session.session.extensionRunner.createCommandContext();
await extension.commands.get("ultra")!.handler("", ctx);
await expect(
extension.tools
.get("run_workflow")!
.definition.execute(
"call",
{ spec: specWith({ tools: ["bash"] }) },
undefined,
undefined,
createToolContext(),
),
).rejects.toThrow(
"Use an active alternative or set step.flags. The nu tool is active; prefer it.",
);
});
// The parent runs in memory, so children are minted through `inMemory`;
// stopping there proves the preflight let child creation begin.
async function runPastPreflight(step: Record<string, unknown>) {
const extension = await start({
settings: { ultra: { subagentExtensions: [nushellPath()] } },
});
if (!session) throw new Error("missing session");
deactivate("bash");
const ctx = session.session.extensionRunner.createCommandContext();
await extension.commands.get("ultra")!.handler("", ctx);
const inMemory = vi
.spyOn(SessionManager, "inMemory")
.mockImplementation(() => {
throw new Error("Stop before provider access");
});
const response = await extension.tools
.get("run_workflow")!
.definition.execute(
"call",
{ spec: specWith(step) },
undefined,
undefined,
createToolContext(),
);
const details = response.details as {
workflowResult: {
phaseFailures: Record<string, Array<{ message: string }>>;
};
fullOutputPath?: string;
};
if (details.fullOutputPath)
rmSync(dirname(details.fullOutputPath), {
recursive: true,
force: true,
});
expect(inMemory).toHaveBeenCalled();
const messages = (details.workflowResult.phaseFailures["work"] ?? []).map(
(failure) => failure.message,
);
expect(messages).toHaveLength(3);
for (const message of messages) {
expect(message).toContain("Stop before provider access");
expect(message).not.toContain("step tools are inactive");
}
}
it("skips the preflight for a step that declares its own flags", async () => {
await runPastPreflight({ tools: ["bash"], flags: { nushell: false } });
});
// grep/find/ls are registered but inactive in a stock Pi; children activate
// them explicitly, so they never trigger the preflight.
it("passes a step listing active tools and Pi's optional builtins", async () => {
await runPastPreflight({ tools: ["read", "grep", "find", "ls"] });
});
});
it("starts research without knowing which extension supplies research tools", async () => {
const extension = await start();
if (!session) throw new Error("missing session");
const ctx = session.session.extensionRunner.createCommandContext();
const custom = vi.fn().mockResolvedValue(null);
Object.assign(ctx.ui, { custom });
await extension.commands
.get("ultra")!
.handler("run research question", ctx);
expect(custom).toHaveBeenCalledOnce();
expect(
uiCallsFor("notify").some((call) =>
String(call.args[0]).includes("unavailable tools"),
),
).toBe(false);
});
it("discovers a workflow saved after the extension loaded without /reload", async () => {
const extension = await start();
if (!session) throw new Error("missing session");
const complete = () =>
extension.commands.get("ultra")!.getArgumentCompletions!("run ");
await complete();
const previousCwd = process.cwd();
try {
process.chdir(session.cwd);
const directory = join(session.cwd, ".pi", "workflows");
mkdirSync(directory, { recursive: true });
writeFileSync(
join(directory, "fresh.json"),
`${JSON.stringify({
name: "fresh",
phases: [
{ id: "p", kind: "single", step: { summary: "hi", prompt: "hi" } },
],
})}\n`,
);
const items = (await complete())!;
expect(items.map((item: { value: string }) => item.value)).toContain(
"run fresh",
);
} finally {
process.chdir(previousCwd);
}
});
it.each([
{
params: {},
expected: ['provide either a workflow "name" or an inline "spec"'],
},
{
params: { name: "does-not-exist" },
expected: ['unknown workflow "does-not-exist"', "Available:", "review"],
},
{ params: { args: "not-an-object" }, expected: ["args", "object"] },
{
params: {
spec: {
name: "invalid",
phases: [{ id: "work", kind: "single", step: { prompt: "Work" } }],
},
},
expected: ["summary"],
},
])(
"delivers actionable argument errors to the agent and the tool UI: $params",
async ({ params, expected }) => {
const extension = await start();
if (!session) throw new Error("missing session");
const ctx = session.session.extensionRunner.createCommandContext();
await extension.commands.get("ultra")!.handler("", ctx);
// Use the real tool boundary through the owned fixture's scripted
// provider: the agent turns a thrown execute() into an isError
// toolResult surfaced back to the model.
const followUp = vi.fn((context: Context) => {
const result = context.messages.findLast(
(message) => message.role === "toolResult",
);
expect(result?.isError).toBe(true);
const error = result?.content
.filter((part) => part.type === "text")
.map((part) => part.text)
.join("\n");
for (const text of expected) expect(error).toContain(text);
return fauxAssistantMessage("Error received.");
});
session.setResponses([
fauxAssistantMessage(
fauxToolCall("run_workflow", params as JsonObject),
{
stopReason: "toolUse",
},
),
followUp,
]);
await session.session.prompt("Exercise invalid workflow arguments");
expect(followUp).toHaveBeenCalledOnce();
const toolResults = session.session.messages.filter(
(message) => message.role === "toolResult",
);
expect(toolResults).toHaveLength(1);
const result = toolResults[0]!;
expect(result.isError).toBe(true);
const resultText = result.content
.filter((part) => part.type === "text")
.map((part) => part.text)
.join("\n");
for (const text of expected) expect(resultText).toContain(text);
const message = session.session.messages.find(
(message): message is Extract<typeof message, { role: "toolResult" }> =>
message.role === "toolResult",
);
if (!message) throw new Error("missing tool result message");
expect(message).toMatchObject({
role: "toolResult",
isError: true,
content: result.content,
});
expect(session.session.messages.at(-1)).toMatchObject({
role: "assistant",
});
initTheme();
const theme = {
fg: (_color: string, text: string) => text,
bold: (text: string) => text,
} as Theme;
const tool = extension.tools.get("run_workflow")!.definition;
const renderResult = tool.renderResult!;
const toolResult = message as Parameters<typeof renderResult>[0];
const renderContext = {
isError: true,
} as Parameters<typeof renderResult>[3];
const collapsed = renderResult(
toolResult,
{ expanded: false, isPartial: false },
theme,
renderContext,
)
.render(120)
.join("\n");
expect(collapsed).toContain("ultra · failed:");
expect(collapsed).not.toContain("0/0 done");
const expanded = renderResult(
toolResult,
{ expanded: true, isPartial: false },
theme,
renderContext,
)
.render(120)
.join("\n");
for (const text of expected) expect(expanded).toContain(text);
},
);
it("exposes the workflow schema while rejecting malformed specs before execution", async () => {
const extension = await start();
if (!session) throw new Error("missing session");
await extension.commands.get("ultra")!.getArgumentCompletions!("");
const tool = extension.tools.get("run_workflow")!.definition;
const parameters = tool.parameters as {
properties: {
spec: {
type: string;
properties: Record<string, unknown>;
additionalProperties?: unknown;
};
background?: unknown;
};
};
const specSchema = parameters.properties.spec;
expect(tool.renderCall).toBeTypeOf("function");
expect(specSchema.type).toBe("object");
expect(specSchema.properties).toHaveProperty("name");
expect(specSchema.properties).toHaveProperty("phases");
expect(specSchema.properties).toHaveProperty("report");
expect(specSchema.additionalProperties).not.toBe(true);
expect(tool.description).toContain(
"run saved or inline multi-agent workflows.",
);
expect(tool.promptSnippet).toBe(
"run saved or inline multi-agent workflows.",
);
expect(tool.promptGuidelines).toEqual([
"Use run_workflow when decomposition into independent or sequential child-agent steps reduces context or verification risk.",
"Use dynamic extensions when a workflow needs a reusable, task-specific capability; read the ultra-authoring skill for how to build and load them.",
]);
expect(parameters.properties.background).toBeUndefined();
expect(JSON.parse(JSON.stringify(specSchema))).toEqual(
JSON.parse(JSON.stringify(WorkflowSpecSchema)),
);
const step = { summary: "Work", prompt: "Work" };
for (const spec of [
{},
{ name: "invalid", phases: [] },
{ name: "invalid", phases: [{ id: "work", kind: "fanout", step }] },
{
name: "invalid",
phases: [{ id: "work", kind: "single", step: { prompt: "Work" } }],
},
{
name: "invalid",
phases: [
{
id: "work",
kind: "single",
step: { ...step, thinkingLevel: "invalid" },
},
],
},
{
name: "invalid",
phases: [
{ id: "work", kind: "single", step: { ...step, schema: "missing" } },
],
},
{ name: "invalid", phases: [{ id: "args", kind: "single", step }] },
]) {
await expect(
tool.execute(
"invalid",
{ spec },
undefined,
undefined,
createToolContext(),
),
).rejects.toThrow("Invalid workflow spec");
}
});
});