repositories / pi-ext
pi-ext
bugabingas pi extensions
owned by admin
scripts/build/pfui-runtime.test.ts
Rawimport { spawnSync } from "node:child_process";
import {
mkdirSync,
mkdtempSync,
readFileSync,
rmSync,
symlinkSync,
writeFileSync,
} from "node:fs";
import { tmpdir } from "node:os";
import { join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { afterAll, beforeAll, describe, expect, it } from "vitest";
import { buildExtension } from "./build.ts";
import { forbiddenImports } from "./verify.ts";
const root = resolve(import.meta.dirname, "../..");
const workspaceManifest = resolve(
fileURLToPath(import.meta.resolve("@earendil-works/pi-coding-agent")),
"../../package.json",
);
const hosts = [
...new Set([
workspaceManifest,
process.env["PI_CONTEXT_PACKAGE_JSON_PATH"] ?? workspaceManifest,
]),
].map((manifest) => {
const metadata = JSON.parse(readFileSync(manifest, "utf8")) as {
version: string;
bin: { pi: string };
};
return { dir: resolve(manifest, ".."), ...metadata };
});
// Observe native SDK evaluation, not merely resolution or text in a bundle.
const hook = `import { registerHooks } from 'node:module';
registerHooks({ load(url, context, nextLoad) {
const loaded = nextLoad(url, context);
if (url.replaceAll('\\\\', '/').endsWith('/pi-coding-agent/dist/index.js'))
process.stderr.write('PFUI_SDK_LOAD=' + url + '\\n');
return loaded;
}});`;
function sandboxEnvironment(sandbox: string, agentDir: string) {
const env = { ...process.env };
for (const name of Object.keys(env)) {
if (name.startsWith("PI_CONTEXT_") || name === "PI_MANAGED_INSTALL_ROOT")
delete env[name];
}
return Object.assign(env, {
HOME: sandbox,
USERPROFILE: sandbox,
APPDATA: sandbox,
LOCALAPPDATA: sandbox,
PI_CODING_AGENT_DIR: agentDir,
XDG_CONFIG_HOME: sandbox,
XDG_CACHE_HOME: sandbox,
XDG_STATE_HOME: sandbox,
XDG_DATA_HOME: sandbox,
PI_OFFLINE: "1",
});
}
function probe(host: (typeof hosts)[number], entry: string | string[]) {
const sandbox = mkdtempSync(join(tmpdir(), "pfui-runtime-"));
try {
const agentDir = join(sandbox, "agent");
mkdirSync(agentDir);
const observer = join(sandbox, "observer.ts");
writeFileSync(
observer,
`export default function(pi) {
pi.on('session_start', () => {
console.log(JSON.stringify({ type: 'pfui_context', version: process.env.PI_CONTEXT_VERSION, packageDir: process.env.PI_CONTEXT_PACKAGE_DIR }));
});
}`,
);
const env = sandboxEnvironment(sandbox, agentDir);
const started = performance.now();
const child = spawnSync(
process.execPath,
[
"--import",
`data:text/javascript,${encodeURIComponent(hook)}`,
join(host.dir, host.bin.pi),
"--mode",
"rpc",
"--no-session",
"--approve",
"--no-extensions",
"--no-context-files",
"--no-skills",
"--no-prompt-templates",
"--no-themes",
...[entry].flat().flatMap((path) => ["--extension", path]),
"--extension",
observer,
],
{
cwd: sandbox,
env,
encoding: "utf8",
timeout: 20_000,
input: '{"type":"get_state"}\n',
maxBuffer: 1024 * 1024,
},
);
const milliseconds = performance.now() - started;
expect(child.error, child.stderr).toBeUndefined();
expect(child.status, child.stderr).toBe(0);
const lines = child.stdout
.trim()
.split("\n")
.map((line) => JSON.parse(line));
expect(lines).toEqual(
expect.arrayContaining([
expect.objectContaining({
type: "response",
command: "get_state",
success: true,
}),
]),
);
const context = child.stderr
.split("\n")
.filter((line) => line.startsWith('{"type":"pfui_context"'))
.map((line) => JSON.parse(line))[0] as
| { version: string; packageDir: string }
| undefined;
expect(context, child.stderr).toBeDefined();
const sdkLoads = child.stderr
.split("\n")
.filter((line) => line.startsWith("PFUI_SDK_LOAD="));
return { context: context!, sdkLoads, milliseconds };
} finally {
rmSync(sandbox, { recursive: true, force: true });
}
}
// Drive the real public SDK synchronously to completion, not RPC startup followed
// by stdin EOF. This exercises the same resource loader and actual child sessions.
function compositionProbe(host: (typeof hosts)[number], entry: string[]) {
const sandbox = mkdtempSync(join(tmpdir(), "sdk-composition-"));
try {
const agentDir = join(sandbox, "agent");
mkdirSync(agentDir);
const observer = join(sandbox, "observer.ts");
writeFileSync(
observer,
`import { VERSION, getPackageDir } from '@earendil-works/pi-coding-agent';
export default function(pi) {
pi.registerProvider('identity', globalThis[Symbol.for('pi-ext-sdk-probe/provider')]);
const records = globalThis[Symbol.for('pi-ext-sdk-probe')] ??= [];
records.push({ stage: 'factory', version: VERSION, packageDir: getPackageDir() });
pi.on('session_start', (_event, ctx) => records.push({
stage: 'session', id: ctx.sessionManager.getSessionId(),
version: VERSION, packageDir: getPackageDir(),
contextVersion: process.env.PI_CONTEXT_VERSION,
contextPackageDir: process.env.PI_CONTEXT_PACKAGE_DIR,
activeTools: pi.getActiveTools(),
nuOwner: pi.getAllTools().find(tool => tool.name === 'nu')?.sourceInfo.path,
hasNuSession: pi.getAllTools().some(tool => tool.name === 'nu_session')
}));
pi.on('tool_result', event => {
if (event.toolName === 'nu') records.push({ stage: 'nu_result', isError: event.isError, content: event.content });
});
}`,
);
const sources = [entry[0]!, resolve(entry[3]!, ".."), observer];
writeFileSync(
join(agentDir, "settings.json"),
JSON.stringify({
angel: {
pairs: [{ executor: "identity/probe", advisor: "identity/probe" }],
subagentExtensions: sources,
},
ultra: {
autoEnable: true,
subagentExtensions: sources,
maxRetries: 0,
actionSummarizerModel: null,
},
}),
);
const driver = join(sandbox, "driver.mjs");
writeFileSync(
driver,
`import { pathToFileURL } from 'node:url';
const { hostDir, entries, observer, agentDir } = JSON.parse(process.argv[2]);
globalThis.fetch = async () => { throw new Error('Unexpected network request in SDK identity probe'); };
const sdk = await import(pathToFileURL(hostDir + '/dist/index.js').href);
const ai = await import(import.meta.resolve('@earendil-works/pi-ai', pathToFileURL(hostDir + '/package.json').href));
sdk.initTheme('dark', false);
const core = ai.createFauxCore({ api: 'openai', provider: 'identity', models: [{ id: 'probe', name: 'Identity Probe' }] });
const runtime = await sdk.ModelRuntime.create({
credentials: new ai.InMemoryCredentialStore(), modelsStore: new ai.InMemoryModelsStore(), refreshOnCreate: false
});
const provider = { api: 'openai', apiKey: 'test-key', baseUrl: 'https://identity.invalid',
streamSimple: core.streamSimple, models: [{ id: 'probe', name: 'Identity Probe', reasoning: false,
input: ['text'], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 200000, maxTokens: 8192 }] };
globalThis[Symbol.for('pi-ext-sdk-probe/provider')] = provider;
runtime.registerProvider('identity', provider);
const loader = new sdk.DefaultResourceLoader({ cwd: process.cwd(), agentDir, noExtensions: true,
noSkills: true, noPromptTemplates: true, noThemes: true, noContextFiles: true,
additionalExtensionPaths: [...entries, observer] });
await loader.reload();
const loaded = loader.getExtensions();
if (loaded.errors.length) throw new Error(JSON.stringify(loaded.errors));
const created = await sdk.createAgentSession({ cwd: process.cwd(), agentDir, modelRuntime: runtime,
model: runtime.getModel('identity', 'probe'), resourceLoader: loader,
sessionManager: sdk.SessionManager.inMemory(process.cwd()) });
const session = created.session;
const errors = [];
const unsubscribe = session.extensionRunner.onError(error => errors.push(String(error.error)));
try {
await session.bindExtensions({ mode: 'print' });
core.setResponses([ai.fauxAssistantMessage('Parent catalog initialized.')]);
await session.prompt('Initialize subagent flag catalog', { expandPromptTemplates: false, source: 'extension' });
const catalogFactories = globalThis[Symbol.for('pi-ext-sdk-probe')].filter(record => record.stage === 'factory').length;
const tool = name => {
for (const extension of loaded.extensions) {
const definition = extension.tools.get(name)?.definition;
if (definition) return definition;
}
throw new Error('Missing tool ' + name);
};
const ctx = session.extensionRunner.createToolContext('identity-probe', undefined);
core.setResponses([ai.fauxAssistantMessage('Verified Angel child.')]);
const angel = await tool('angel').execute('angel-probe', { question: 'Verify child initialization' }, undefined, undefined, ctx);
core.setResponses([
ai.fauxAssistantMessage(ai.fauxToolCall('nu', { command: '1 + 1' }), { stopReason: 'toolUse' }),
ai.fauxAssistantMessage(ai.fauxToolCall('structured_output', { verified: true }), { stopReason: 'toolUse' })
]);
const ultra = await tool('run_workflow').execute('ultra-probe', { spec: { name: 'identity', phases: [{
id: 'child', kind: 'single', step: { summary: 'Verify child', prompt: 'Return verified true',
model: 'identity/probe', tools: ['nu'], flags: { nushell: true }, schema: { type: 'object', properties: { verified: { type: 'boolean' } }, required: ['verified'], additionalProperties: false } }
}] } }, undefined, undefined, ctx);
console.log(JSON.stringify({ records: globalThis[Symbol.for('pi-ext-sdk-probe')], catalogFactories,
angel: angel.details, ultra: JSON.parse(ultra.content[0].text), errors,
warnings: [...(globalThis[Symbol.for('@bugabinga/pi-ext-pfui/state')]?.warnings ?? [])] }));
} finally {
unsubscribe();
await session.extensionRunner.emit({ type: 'session_shutdown', reason: 'quit' });
session.dispose();
}
`,
);
const child = spawnSync(
process.execPath,
[
"--import",
`data:text/javascript,${encodeURIComponent(hook)}`,
"--experimental-import-meta-resolve",
driver,
JSON.stringify({
hostDir: host.dir,
entries: entry,
observer,
agentDir,
}),
],
{
cwd: sandbox,
env: sandboxEnvironment(sandbox, agentDir),
encoding: "utf8",
timeout: 30_000,
maxBuffer: 1024 * 1024,
},
);
expect(child.error, child.stderr).toBeUndefined();
expect(child.status, child.stderr).toBe(0);
const report = JSON.parse(child.stdout.trim()) as {
records: {
stage: string;
id?: string;
version: string;
packageDir: string;
contextVersion?: string;
contextPackageDir?: string;
activeTools?: string[];
nuOwner?: string;
hasNuSession?: boolean;
isError?: boolean;
content?: { type: string; text?: string }[];
}[];
catalogFactories: number;
angel: {
kind: string;
result: { advice: string; metadata: { childSessionId: string } };
};
ultra: { executionStatus: string; result: { verified: boolean }[] };
errors: string[];
warnings: string[];
};
const sdkLoads = child.stderr
.split("\n")
.filter((line) => line.startsWith("PFUI_SDK_LOAD="))
.map((line) => fileURLToPath(line.slice("PFUI_SDK_LOAD=".length)));
return { report, sdkLoads };
} finally {
rmSync(sandbox, { recursive: true, force: true });
}
}
// Build fresh artifacts; stale ignored dist must not make this test pass.
// Every relocated package gets workspace dependencies adjacent to it, so an
// accidental native .js import loads a second workspace SDK rather than hiding.
let builds: string;
let nativeControl: string;
let nativeAngelControl: string[];
const entries = [
[
join(root, "extensions/pfui/index.ts"),
join(root, "extensions/angel/index.ts"),
join(root, "extensions/ultra/index.ts"),
join(root, "extensions/nushell/index.ts"),
],
];
beforeAll(async () => {
builds = mkdtempSync(join(tmpdir(), "pfui-runtime-builds-"));
for (const release of [false, true]) {
const packageEntries: string[] = [];
for (const name of ["pfui", "angel", "ultra", "nushell"]) {
const built = await buildExtension(name, {
distRoot: join(builds, release ? "release" : "dev"),
release,
});
symlinkSync(
join(root, "node_modules"),
join(built.dir, "node_modules"),
"junction",
);
const manifest = JSON.parse(
readFileSync(join(built.dir, "package.json"), "utf8"),
) as {
pi: { extensions: string[] };
};
expect(manifest.pi.extensions).toEqual(["index.ts"]);
expect(forbiddenImports(built.dir)).toEqual([]);
const entry = join(built.dir, manifest.pi.extensions[0]!);
packageEntries.push(entry);
if (!release && name === "pfui") {
nativeControl = join(built.dir, "native-control.js");
writeFileSync(nativeControl, readFileSync(entry));
}
if (!release && name === "angel") {
writeFileSync(
join(built.dir, "native-control.js"),
readFileSync(entry),
);
}
}
if (!release) {
nativeAngelControl = [...packageEntries];
nativeAngelControl[1] = packageEntries[1]!.replace(
/index\.ts$/u,
"native-control.js",
);
}
entries.push(packageEntries);
}
}, 30_000);
afterAll(() => {
if (builds) rmSync(builds, { recursive: true, force: true });
});
describe.each(hosts)("PFUI running Pi $version identity", (host) => {
it("detects the duplicate SDK when the same emitted module uses a native JS suffix", () => {
const result = probe(host, nativeControl);
expect(result.sdkLoads, JSON.stringify(result)).toHaveLength(1);
expect(
fileURLToPath(result.sdkLoads[0]!.slice("PFUI_SDK_LOAD=".length)),
).toBe(join(resolve(workspaceManifest, ".."), "dist/index.js"));
expect(result.context.packageDir).toBe(resolve(workspaceManifest, ".."));
});
it.skipIf(host.dir === resolve(workspaceManifest, ".."))(
"native generated Angel loads an adjacent SDK even when both installations have the same version",
() => {
const { sdkLoads } = compositionProbe(host, nativeAngelControl);
expect(sdkLoads).toEqual([
join(host.dir, "dist/index.js"),
join(resolve(workspaceManifest, ".."), "dist/index.js"),
]);
},
);
it("source plus every relocated dev and release entry reuse the running CLI SDK", () => {
for (const entry of entries) {
const result = probe(host, entry);
expect(result.context.version, JSON.stringify(result)).toBe(host.version);
expect(result.context.packageDir, JSON.stringify(result)).toBe(host.dir);
expect(result.sdkLoads, JSON.stringify(result)).toEqual([]);
}
});
it("public SDK parent activates lazy runtimes, catalogs PFUI, and completes actual Angel/Ultra children", () => {
for (const entry of entries) {
const { report, sdkLoads } = compositionProbe(host, entry);
expect(sdkLoads, JSON.stringify(report)).toEqual([
join(host.dir, "dist/index.js"),
]);
expect(report.errors).toEqual([]);
expect(report.warnings).toEqual([]);
expect(report.catalogFactories).toBe(2);
expect(report.angel.kind).toBe("complete");
expect(report.angel.result.advice).toBe("Verified Angel child.");
expect(report.ultra.executionStatus, JSON.stringify(report.ultra)).toBe(
"finished",
);
expect(report.ultra.result).toEqual([{ verified: true }]);
const sessions = report.records.filter(
(record) => record.stage === "session",
);
expect(new Set(sessions.map((record) => record.id)).size).toBe(3);
expect(sessions.map((record) => record.id)).toContain(
report.angel.result.metadata.childSessionId,
);
const nuResult = report.records.filter(
(record) => record.stage === "nu_result",
);
expect(nuResult, JSON.stringify(report)).toHaveLength(1);
expect(nuResult[0]?.isError).toBe(false);
expect(nuResult[0]?.content).toEqual(
expect.arrayContaining([
expect.objectContaining({
type: "text",
text: expect.stringMatching(/^2\s*$/),
}),
]),
);
const nuChild = sessions.find((record) =>
record.activeTools?.includes("nu"),
);
expect(nuChild, JSON.stringify(report)).toBeDefined();
expect(nuChild?.nuOwner).toBe(entry[3]);
expect(nuChild?.hasNuSession).toBe(false);
for (const record of report.records.filter(
(record) => record.stage !== "nu_result",
)) {
expect(record.version).toBe(host.version);
expect(record.packageDir).toBe(host.dir);
if (record.stage === "session") {
expect(record.contextVersion).toBe(host.version);
expect(record.contextPackageDir).toBe(host.dir);
}
}
}
}, 120_000);
});