mirror of
https://github.com/wassname/pi-plan.git
synced 2026-09-26 14:10:23 +08:00
65 lines
4.6 KiB
TypeScript
65 lines
4.6 KiB
TypeScript
import { spawn } from "node:child_process";
|
|
import { once } from "node:events";
|
|
import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
import { createServer } from "node:http";
|
|
import { tmpdir } from "node:os";
|
|
import { join, resolve } from "node:path";
|
|
import { expect, it } from "vitest";
|
|
|
|
it.each(["worker", "supervisor"] as const)("real Pi preserves %s delivery through compaction success, failure and cancellation", async role => {
|
|
for (const outcome of ["success", "failure", "cancel"]) {
|
|
const cwd = mkdtempSync(join(tmpdir(), "goals-native-compaction-"));
|
|
const agentDir = join(cwd, "agent");
|
|
mkdirSync(agentDir);
|
|
writeFileSync(join(agentDir, "settings.json"), JSON.stringify({ compaction: { enabled: false, keepRecentTokens: 1000, reserveTokens: 1000 }, retry: { enabled: false } }));
|
|
let requests = 0;
|
|
const server = createServer(async (req, res) => {
|
|
for await (const _chunk of req) { /* consume local request */ }
|
|
requests++;
|
|
if (outcome === "failure" && requests === 3) { res.writeHead(400); res.end(JSON.stringify({ error: { message: "deterministic compaction failure" } })); return; }
|
|
res.writeHead(200, { "content-type": "text/event-stream" });
|
|
res.end(`data: ${JSON.stringify({ choices: [{ index: 0, delta: { content: "Inspected the supplied context." }, finish_reason: null }] })}\n\ndata: ${JSON.stringify({ choices: [{ index: 0, delta: {}, finish_reason: "stop" }], usage: { prompt_tokens: 8000, completion_tokens: 10, total_tokens: 8010 } })}\n\ndata: [DONE]\n\n`);
|
|
});
|
|
await new Promise<void>(done => server.listen(0, "127.0.0.1", done));
|
|
const address = server.address();
|
|
if (!address || typeof address === "string") throw new Error("No local model port");
|
|
const child = spawn(resolve("node_modules/.bin/pi"), ["--mode", "rpc", "--no-extensions", "--model", "offline/test", "-e", resolve("test/fixtures/offline-model.ts"), "-e", resolve("test/fixtures/compaction-delivery.ts")], { cwd, env: { ...process.env, PI_CODING_AGENT_DIR: agentDir, PI_GOALS_OFFLINE_MODEL_URL: `http://127.0.0.1:${address.port}`, PI_GOALS_TEST_DELIVERY_ROLE: role, PI_GOALS_TEST_COMPACTION: outcome } });
|
|
const messages: any[] = [];
|
|
let buffer = "", stderr = "";
|
|
child.stdout.on("data", data => {
|
|
buffer += data;
|
|
while (buffer.includes("\n")) { const n = buffer.indexOf("\n"); const line = buffer.slice(0, n); buffer = buffer.slice(n + 1); if (line.trim()) messages.push(JSON.parse(line)); }
|
|
});
|
|
child.stderr.on("data", data => { stderr += data; });
|
|
const send = (message: object) => child.stdin.write(`${JSON.stringify(message)}\n`);
|
|
const wait = async (predicate: (message: any) => boolean) => {
|
|
for (let i = 0; i < 500; i++) { const found = messages.find(predicate); if (found) return found; await new Promise(done => setTimeout(done, 20)); }
|
|
throw new Error(`Timed out ${role}/${outcome}: ${stderr}\n${JSON.stringify(messages.slice(-8))}`);
|
|
};
|
|
try {
|
|
send({ type: "prompt", id: "seed", message: "Inspect this bounded test context. " + "Saved observation. ".repeat(2000) });
|
|
await wait(m => m.type === "agent_settled");
|
|
messages.length = 0;
|
|
send({ type: "prompt", id: "second-turn", message: "Keep this most recent turn for continued work. " + "Recent context. ".repeat(500) });
|
|
await wait(m => m.type === "agent_settled");
|
|
send({ type: "compact", id: "compact" });
|
|
const completion = await wait(m => m.type === "response" && m.id === "compact");
|
|
expect(completion.success, JSON.stringify(completion)).toBe(outcome === "success");
|
|
await wait(m => m.type === "message_start" && m.message?.role === "user" && JSON.stringify(m.message.content).includes("Retained evidence arrived"));
|
|
await wait(m => m.type === "message_end" && m.message?.role === "user" && JSON.stringify(m.message.content).includes("Retained evidence arrived"));
|
|
send({ type: "get_state", id: "state" });
|
|
const state = await wait(m => m.type === "response" && m.id === "state");
|
|
const transcript = readFileSync(state.data.sessionFile, "utf8");
|
|
expect(transcript).toContain('"direction":"queued"');
|
|
expect(transcript).toContain('"direction":"in"');
|
|
expect(messages.filter(m => m.type === "message_start" && m.message?.role === "user" && JSON.stringify(m.message.content).includes("Retained evidence arrived"))).toHaveLength(1);
|
|
expect(messages.filter(m => m.type === "extension_error")).toEqual([]);
|
|
console.log(`real Pi ${role}/${outcome}: retained message presented once and saved, no extension errors`);
|
|
} finally {
|
|
child.kill("SIGTERM"); await once(child, "exit");
|
|
await new Promise<void>(done => server.close(() => done()));
|
|
rmSync(cwd, { recursive: true, force: true });
|
|
}
|
|
}
|
|
}, 45_000);
|