Add 5 pi extensions: pi-subagents, pi-crew, rpiv-pi, pi-interactive-shell, pi-intercom

This commit is contained in:
2026-05-08 15:59:25 +10:00
parent d0d1d9b045
commit 31b4110c87
457 changed files with 85157 additions and 0 deletions

View File

@@ -0,0 +1,114 @@
import * as fs from "node:fs";
import type { TeamRunManifest } from "../state/types.ts";
import { agentOutputPath, readCrewAgents } from "./crew-agent-records.ts";
import type { CrewAgentRecord } from "./crew-agent-runtime.ts";
const TOOL_LABELS: Record<string, string> = {
read: "reading",
bash: "running command",
edit: "editing",
write: "writing",
grep: "searching",
find: "finding files",
ls: "listing",
};
export interface TextTailResult {
path: string;
text: string;
bytes: number;
truncated: boolean;
}
export function readTextTail(filePath: string, maxBytes = 64_000): TextTailResult {
if (!fs.existsSync(filePath)) return { path: filePath, text: "", bytes: 0, truncated: false };
const stat = fs.statSync(filePath);
const bytesToRead = Math.min(stat.size, Math.max(0, maxBytes));
const fd = fs.openSync(filePath, "r");
try {
const buffer = Buffer.alloc(bytesToRead);
fs.readSync(fd, buffer, 0, bytesToRead, stat.size - bytesToRead);
return { path: filePath, text: buffer.toString("utf-8"), bytes: stat.size, truncated: stat.size > bytesToRead };
} finally {
fs.closeSync(fd);
}
}
function compactDuration(ms: number | undefined): string | undefined {
if (ms === undefined || !Number.isFinite(ms)) return undefined;
if (ms < 1000) return `${Math.round(ms)}ms`;
if (ms < 60_000) return `${(ms / 1000).toFixed(1)}s`;
return `${Math.floor(ms / 60_000)}m${Math.floor((ms % 60_000) / 1000)}s`;
}
function ageBetween(start: string | undefined, end: string | undefined): string | undefined {
if (!start) return undefined;
const stop = end ? new Date(end).getTime() : Date.now();
const ms = Math.max(0, stop - new Date(start).getTime());
return compactDuration(ms);
}
function activityText(agent: CrewAgentRecord): string {
const parts: string[] = [];
if (agent.progress?.activityState) parts.push(agent.progress.activityState);
if (agent.progress?.currentTool) parts.push(TOOL_LABELS[agent.progress.currentTool] ?? `tool=${agent.progress.currentTool}`);
if (agent.toolUses !== undefined) parts.push(`tools=${agent.toolUses}`);
if (agent.progress?.tokens !== undefined) parts.push(`tokens=${agent.progress.tokens}`);
if (agent.progress?.turns !== undefined) parts.push(`turns=${agent.progress.turns}`);
const duration = compactDuration(agent.progress?.durationMs) ?? ageBetween(agent.startedAt, agent.completedAt);
if (duration) parts.push(duration);
if (agent.progress?.failedTool) parts.push(`failedTool=${agent.progress.failedTool}`);
if (agent.progress?.recentOutput?.length) parts.push(`last=${agent.progress.recentOutput.at(-1)}`);
return parts.join(" ") || "idle";
}
function statusGlyph(status: CrewAgentRecord["status"]): string {
if (status === "completed") return "✓";
if (status === "failed") return "✗";
if (status === "running") return "▶";
if (status === "cancelled" || status === "stopped") return "■";
return "·";
}
function outputWarning(manifest: TeamRunManifest, agent: CrewAgentRecord): string {
if (agent.status !== "completed") return "";
try {
const outputPath = agentOutputPath(manifest, agent.taskId);
if (!fs.existsSync(outputPath)) return " no-output";
return fs.statSync(outputPath).size === 0 ? " no-output" : "";
} catch {
return " no-output";
}
}
function agentLine(manifest: TeamRunManifest, agent: CrewAgentRecord): string {
return `- ${statusGlyph(agent.status)} ${agent.taskId} ${agent.role}${agent.agent} · ${agent.status} · ${agent.runtime} · ${activityText(agent)}${outputWarning(manifest, agent)}${agent.error ? ` · error=${agent.error}` : ""}`;
}
export function buildAgentDashboard(manifest: TeamRunManifest): { text: string; groups: Record<string, CrewAgentRecord[]> } {
const agents = readCrewAgents(manifest);
const groups: Record<string, CrewAgentRecord[]> = {
running: agents.filter((agent) => agent.status === "running"),
queued: agents.filter((agent) => agent.status === "queued"),
recent: agents.filter((agent) => agent.status !== "running" && agent.status !== "queued"),
};
const lines = [
`Crew agents for ${manifest.runId}`,
`Run: ${manifest.status} · ${manifest.team}/${manifest.workflow ?? "none"} · agents=${agents.length}`,
`Counts: running=${groups.running.length}, queued=${groups.queued.length}, recent=${groups.recent.length}`,
"",
"## Running",
...(groups.running.length ? groups.running.map((agent) => agentLine(manifest, agent)) : ["- (none)"]),
"",
"## Queued",
...(groups.queued.length ? groups.queued.map((agent) => agentLine(manifest, agent)) : ["- (none)"]),
"",
"## Recent",
...(groups.recent.length ? groups.recent.slice(-10).map((agent) => agentLine(manifest, agent)) : ["- (none)"]),
];
return { text: lines.join("\n"), groups };
}
export function readAgentOutput(manifest: TeamRunManifest, taskId: string, maxBytes?: number): TextTailResult {
return readTextTail(agentOutputPath(manifest, taskId), maxBytes);
}