Files
pi-plan/src/herdr.ts
T
wassname 489298d58b Replace supervision mailbox polling with pi-intercom
Scope messages to each pairing, wait for supervisor readiness, retain instructions in session history until acknowledgment, and rejoin after disconnect. Reuse installed Intercom or load the package dependency. Validate over an isolated real broker; existing user panes are untouched.
2026-09-08 16:31:58 +08:00

84 lines
2.8 KiB
TypeScript

import { execFile } from "node:child_process";
import { promisify } from "node:util";
const execFileAsync = promisify(execFile);
interface LaunchSupervisorInput {
cwd: string;
sourceSessionFile: string;
workerSessionId: string;
planPath: string;
approvalId: string;
extensionPath: string;
model: string | null;
}
function shellQuote(value: string): string {
return `'${value.replaceAll("'", "'\\''")}'`;
}
function findPaneId(value: unknown): string | null {
if (!value || typeof value !== "object") return null;
const record = value as Record<string, unknown>;
for (const key of ["pane_id", "paneId"]) {
if (typeof record[key] === "string") return record[key];
}
for (const child of Object.values(record)) {
const found = findPaneId(child);
if (found) return found;
}
return null;
}
async function herdr(args: string[], json = true): Promise<unknown> {
const bin = process.env.HERDR_BIN_PATH ?? "herdr";
const { stdout } = await execFileAsync(bin, args, { encoding: "utf8", timeout: 15_000 });
if (!json) return stdout.trim();
return stdout.trim() ? JSON.parse(stdout) : {};
}
function stalePaneError(error: unknown): boolean {
const record = error as { stdout?: unknown; stderr?: unknown; message?: unknown };
const text = [record.stdout, record.stderr, record.message].filter((value): value is string => typeof value === "string").join("\n");
return /\b(?:NOT_FOUND|PANE_GONE|PANE_NOT_FOUND)\b/i.test(text);
}
export function supervisorCommand(input: LaunchSupervisorInput): string {
const env = [
"PI_GOALS_ROLE=supervisor",
`PI_GOALS_WORKER_ID=${input.workerSessionId}`,
`PI_GOALS_PLAN_PATH=${input.planPath}`,
`PI_GOALS_APPROVAL_ID=${input.approvalId}`,
`PI_GOALS_OWNER_SESSION_ID=${input.workerSessionId}`,
];
const args = [
"pi",
"--no-extensions",
"-e", input.extensionPath,
"--fork", input.sourceSessionFile,
"--name", `goals-supervisor-${input.workerSessionId.slice(0, 8)}`,
];
if (input.model) args.push("--model", input.model);
return `env ${[...env, ...args].map(shellQuote).join(" ")}`;
}
export async function openSupervisorPane(input: LaunchSupervisorInput, onOpened: (paneId: string) => void): Promise<string> {
if (process.env.HERDR_ENV !== "1") throw new Error("Ready needs a Herdr session so pi-goals can open the supervisor session.");
await herdr(["--version"], false);
const split = await herdr(["pane", "split", "--current", "--direction", "right", "--cwd", input.cwd, "--no-focus"]);
const paneId = findPaneId(split);
if (!paneId) throw new Error("Herdr did not return the new supervisor pane ID.");
onOpened(paneId);
await herdr(["pane", "run", paneId, supervisorCommand(input)]);
return paneId;
}
export async function closeSupervisorPane(paneId: string): Promise<void> {
try {
await herdr(["pane", "close", paneId]);
} catch (error) {
if (stalePaneError(error)) return;
throw error;
}
}