ワンクリックで
live-agent-lifecycle
Live agent registration, workspace isolation, termination, and eviction workflow.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Live agent registration, workspace isolation, termination, and eviction workflow.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
Iterative multi-round codebase audit with diminishing-returns detection. Run 5-20+ rounds, each focusing on one specific area. Built from 19 rounds of dogfooding pi-crew on itself.
Pi TUI crew widget data sources, display priority, and rendering performance.
Multi-phase orchestration for planners and executors.
Spawn 3 adversarial subagents (Skeptic, Pragmatist, Critic) to evaluate a decision, architecture choice, or plan. Anti-anchoring: each role receives ONLY the question, not conversation history. Aggregates votes into consensus recommendation with dissent tracking. Use when facing critical decisions, architecture choices, security tradeoffs, or plan reviews where single-perspective analysis is insufficient.
Background worker, heartbeat, stale-run, crash-recovery, and deadletter workflow. Use when debugging stuck/dead workers or changing async run reliability.
Child Pi worker spawning, lifecycle callbacks, and failure modes.
SOC 職業分類に基づく
| name | live-agent-lifecycle |
| description | Live agent registration, workspace isolation, termination, and eviction workflow. |
| origin | pi-crew |
| triggers | ["register agent","terminate agent","evict stale","ghost agent","workspace isolation"] |
Live agents are real-time, in-memory worker sessions managed by LiveAgentManager (src/runtime/live-agent-manager.ts). They are distinct from CrewAgentRecord files on disk — live agents provide real-time activity (tool names, response text, turn count) while agent records are durable snapshots.
LiveAgentHandle is the core data structure:
interface LiveAgentHandle {
agentId: string; // unique per run
taskId: string; // maps to task
runId: string; // run this agent belongs to
workspaceId: string; // manifest.cwd — workspace boundary
role?: string;
agent?: string;
modelName?: string;
session: LiveSessionHandle; // steer/prompt/abort/dispose
status: CrewAgentRecord["status"];
pendingSteers: string[];
pendingFollowUps: string[];
pendingMessages: IrcMessage[];
activity: LiveAgentActivity; // real-time tracking
createdAt: string;
updatedAt: string;
}
The in-memory liveAgents Map stores all active handles. It is never persisted — on Pi restart, the Map is empty and agents are re-created from agent records.
registerLiveAgent(input, eventLogFn?, eventsPath?) is called when a live session worker starts. It:
liveAgents Maplive_agent.registered event to events.jsonlKey caller sites:
live-session-runtime.ts — when a live session agent startslive-executor.ts — when spawning a live taskworkspaceId: string field is the workspace boundary. Set to manifest.cwd at registration time.
Why it matters: When Pi has multiple workspace folders open, agents from workspace A must not be visible or controllable from workspace B. Every handle carries its origin workspace.
Enforcement in api.ts:
listActiveLiveAgentsByWorkspace(workspaceId) — filters by workspacelive.workspaceId !== manifest.cwd → reject with errorlistLiveAgentsByWorkspace(manifest.cwd) so each workspace only sees its own agentsEnforcement in live-session-runtime.ts:
workspaceId from TeamContext.workspaceIdLiveAgentActivity provides real-time data without reading disk:
interface LiveAgentActivity {
activeTools: Map<string, string>; // toolName → description
toolUses: number; // total invocations
turnCount: number;
maxTurns?: number;
responseText: string; // last 200 chars
compactionCount: number;
startedAtMs: number;
completedAtMs: number; // 0 = still running
modelName?: string;
}
Tracking functions (called from live-executor):
trackLiveAgentToolStart(agentId, toolName) — adds tool to activeToolstrackLiveAgentToolEnd(agentId, toolName) — removes tool from activeToolstrackLiveAgentTurnEnd(agentId, compaction?) — increments turn, clears toolstrackLiveAgentResponseText(agentId, text) — stores last 200 charsmarkLiveAgentCompleted(agentId) — sets completedAtMsterminateLiveAgent(agentIdOrTaskId, status?, eventLogFn?, eventsPath?) is the canonical termination path:
live_agent.terminated event to events.jsonlsession.abort() to stop the childsession.dispose() to clean upliveAgents MapTermination call sites (4 total):
| Location | When |
|---|---|
team-runner.ts (run complete) | All agents terminated when run succeeds or fails |
team-runner.ts (task complete) | Per-task termination when terminateOnTaskComplete=true |
background-runner.ts (catch) | Termination in finally block after background run |
cancel.ts | Termination when user cancels a run |
respond.ts | Termination when responding to waiting tasks |
crash-recovery.ts (purgeStale) | Termination when cleaning up orphaned runs |
crash-recovery.ts (reconcile) | Termination when reconciling stale runs |
terminateLiveAgentsForRun(runId, status?, eventLogFn?, eventsPath?) terminates all agents for a run in parallel.
Stale handles are handles whose status is terminal (not running/queued/waiting) and older than 10 minutes. evictStaleLiveAgentHandles(now?) removes them:
const STALE_HANDLE_MS = 10 * 60 * 1000;
// Only evict terminal-status handles
if (handle.status !== "running" && handle.status !== "queued" && handle.status !== "waiting") {
const age = now - new Date(handle.updatedAt).getTime();
if (age > STALE_HANDLE_MS) {
liveAgents.delete(agentId);
safeDisposeLiveSession(handle);
}
}
Triggered on every widget refresh in crew-widget.ts:
evictStaleLiveAgentHandles(); // called at start of activeWidgetRuns()
This prevents the Map from growing indefinitely with completed agents.
On task completion, upsertCrewAgent(manifest, recordFromTask(manifest, task, "live-session")) is called to persist the final status to disk (agents.json, agents/<id>/status.json). This ensures the widget sees the correct status even after the live agent handle is evicted.
The sync chain:
task.completed → upsertCrewAgent → agents.json updated
→ live_agent.terminated event logged
→ (later) evictStaleLiveAgentHandles → handle removed from Map
Before terminating or evicting live agents, verify:
If ANY answer is NO → Stop. Verify lifecycle state before mutation.
terminateLiveAgent is not called, the handle stays in the Map forever with status "running". Use finally blocks or crash-recovery to ensure termination.evictStaleLiveAgentHandles, completed agents accumulate in the Map. This is mitigated by calling eviction on every widget refresh.workspaceId differs from the current workspace. Always check live.workspaceId === manifest.cwd.src/runtime/live-agent-manager.ts — register, terminate, evict, workspaceIdsrc/runtime/live-session-runtime.ts — 4 lifecycle gaps fixedsrc/runtime/team-runner.ts — terminate on success/failsrc/runtime/background-runner.ts — terminate on catchsrc/runtime/crash-recovery.ts — terminate in purge+reconcilesrc/extension/team-tool/api.ts — workspaceId filtersrc/extension/team-tool/cancel.ts — terminate on cancelsrc/extension/team-tool/respond.ts — terminate on respondsrc/ui/crew-widget.ts — evictStaleLiveAgentHandles on refreshcd pi-crew
npx tsc --noEmit
node --experimental-strip-types --test test/unit/live-agent-manager.test.ts test/unit/live-session-runtime.test.ts test/unit/live-agent-control.test.ts
npm test