feat: add Pi Coding Agent backend runtime, update dashboard UI

This commit is contained in:
Tanmay Karande
2026-03-03 00:24:19 -05:00
parent 453389f55c
commit 4c3a58b680
21 changed files with 4955 additions and 192 deletions

View File

@@ -4,6 +4,7 @@ import { ClaudeCodeBackend } from "../../src/backends/claude-backend.js";
import { CodexBackend } from "../../src/backends/codex-backend.js";
import { GeminiBackend } from "../../src/backends/gemini-backend.js";
import { OpenCodeBackend } from "../../src/backends/opencode-backend.js";
import { PiBackend } from "../../src/backends/pi-backend.js";
import type { BackendAdapterConfig } from "../../src/backends/types.js";
// ── Shared arbitraries ──────────────────────────────────────────────
@@ -151,6 +152,35 @@ describe("Property 5: Session resume args across backends", () => {
);
});
});
describe("Pi: --session <id> --continue when session provided, absent otherwise", () => {
it("includes --session <id> --continue when session ID is provided", () => {
fc.assert(
fc.property(nonEmptyString, nonEmptyString, sessionId, (prompt, sysPr, sid) => {
const backend = new PiBackend(makeConfig());
const args = backend.buildArgs(prompt, sysPr, sid);
const sessionIdx = args.indexOf("--session");
return (
sessionIdx !== -1 &&
args[sessionIdx + 1] === sid &&
args.includes("--continue")
);
}),
{ numRuns: 100 },
);
});
it("does not include --session or --continue when no session ID is provided", () => {
fc.assert(
fc.property(nonEmptyString, nonEmptyString, (prompt, sysPr) => {
const backend = new PiBackend(makeConfig());
const args = backend.buildArgs(prompt, sysPr);
return !args.includes("--session") && !args.includes("--continue");
}),
{ numRuns: 100 },
);
});
});
});
// ═══════════════════════════════════════════════════════════════════
@@ -229,6 +259,24 @@ describe("Property 6: Output parsing extracts correct fields", () => {
{ numRuns: 100 },
);
});
it("Pi: parses NDJSON with result type and session_id", () => {
fc.assert(
fc.property(responseText, sessionId, (text, sid) => {
const backend = new PiBackend(makeConfig());
const lines = [
JSON.stringify({ type: "result", result: text, session_id: sid }),
].join("\n");
const result = backend.parseOutput(lines);
return (
result.isError === false &&
result.responseText === text &&
result.sessionId === sid
);
}),
{ numRuns: 100 },
);
});
});
// ═══════════════════════════════════════════════════════════════════
@@ -252,6 +300,7 @@ const backendErrorPrefixes: Record<string, string> = {
codex: "Codex CLI error",
gemini: "Gemini CLI error",
opencode: "OpenCode CLI error",
pi: "Pi CLI error",
};
/**
@@ -272,7 +321,7 @@ function simulateErrorResult(
}
describe("Property 8: Non-zero exit code produces error result", () => {
const backendNames = ["claude", "codex", "gemini", "opencode"] as const;
const backendNames = ["claude", "codex", "gemini", "opencode", "pi"] as const;
it("for any backend, non-zero exit code and stderr, result has isError=true and responseText contains stderr", () => {
fc.assert(