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,63 @@
import type { PiTeamsConfig } from "../config/config.ts";
import type { TeamRunManifest } from "../state/types.ts";
import { appendTaskAttentionEvent } from "./attention-events.ts";
import type { CrewAgentRecord } from "./crew-agent-runtime.ts";
import { upsertCrewAgent } from "./crew-agent-records.ts";
export interface CrewControlConfig {
enabled: boolean;
needsAttentionAfterMs: number;
}
const DEFAULT_NEEDS_ATTENTION_MS = 60_000;
function positiveInt(value: unknown): number | undefined {
return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : undefined;
}
export function resolveCrewControlConfig(config: PiTeamsConfig | undefined): CrewControlConfig {
const raw = config as PiTeamsConfig & { control?: { enabled?: unknown; needsAttentionAfterMs?: unknown } } | undefined;
return {
enabled: raw?.control?.enabled === false ? false : true,
needsAttentionAfterMs: positiveInt(raw?.control?.needsAttentionAfterMs) ?? DEFAULT_NEEDS_ATTENTION_MS,
};
}
export function activityAgeMs(agent: CrewAgentRecord, now = Date.now()): number | undefined {
const timestamp = agent.progress?.lastActivityAt ?? agent.startedAt;
if (!timestamp) return undefined;
const ms = now - new Date(timestamp).getTime();
return Number.isFinite(ms) ? Math.max(0, ms) : undefined;
}
export function formatActivityAge(agent: CrewAgentRecord, now = Date.now()): string | undefined {
const age = activityAgeMs(agent, now);
if (age === undefined) return undefined;
if (age < 1000) return "active now";
const seconds = Math.floor(age / 1000);
if (seconds < 60) return agent.progress?.activityState === "needs_attention" ? `no activity for ${seconds}s` : `active ${seconds}s ago`;
const minutes = Math.floor(seconds / 60);
return agent.progress?.activityState === "needs_attention" ? `no activity for ${minutes}m` : `active ${minutes}m ago`;
}
export function applyAttentionState(manifest: TeamRunManifest, agent: CrewAgentRecord, config: CrewControlConfig, now = Date.now()): CrewAgentRecord {
if (!config.enabled || agent.status !== "running") return agent;
const age = activityAgeMs(agent, now);
if (age === undefined || age <= config.needsAttentionAfterMs) return agent;
if (agent.progress?.activityState === "needs_attention") return agent;
const updated: CrewAgentRecord = {
...agent,
progress: {
...(agent.progress ?? { recentTools: [], recentOutput: [], toolCount: agent.toolUses ?? 0 }),
activityState: "needs_attention",
},
};
upsertCrewAgent(manifest, updated);
appendTaskAttentionEvent({
manifest,
taskId: agent.taskId,
message: `${agent.agent} needs attention (no observed activity for ${Math.floor(age / 1000)}s).`,
data: { activityState: "needs_attention", reason: "idle", elapsedMs: age, taskId: agent.taskId, agentName: agent.agent, suggestedAction: "Check worker status, wait, steer, or cancel if needed." },
});
return updated;
}