Adds exitCode to AgentInfo type and propagates it through all toAgentInfo() implementations. Enhances getAgent to also return taskName and initiativeName from their respective repositories. Adds two new filesystem-reading tRPC procedures for the Agent Details tab: getAgentInputFiles (reads .cw/input/ files with binary detection, 500 KB cap, sorted) and getAgentPrompt (reads .cw/agent-logs/<name>/PROMPT.md with 1 MB cap and structured ENOENT handling). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
109 lines
3.7 KiB
TypeScript
109 lines
3.7 KiB
TypeScript
/**
|
|
* CleanupStrategy — Centralized cleanup logic based on debug mode and agent state.
|
|
*
|
|
* Determines when and how to clean up agent workdirs and resources.
|
|
* Supports archive mode for debugging vs. immediate cleanup for production.
|
|
*/
|
|
|
|
import { createModuleLogger } from '../../logger/index.js';
|
|
import type { CleanupManager } from '../cleanup-manager.js';
|
|
|
|
const log = createModuleLogger('cleanup-strategy');
|
|
|
|
export type CleanupAction = 'remove' | 'archive' | 'preserve';
|
|
|
|
export interface AgentInfo {
|
|
id: string;
|
|
name: string;
|
|
status: string;
|
|
initiativeId?: string | null;
|
|
worktreeId: string;
|
|
exitCode?: number | null;
|
|
}
|
|
|
|
export interface CleanupStrategy {
|
|
shouldCleanup(agent: AgentInfo, isDebugMode: boolean): Promise<CleanupAction>;
|
|
executeCleanup(agent: AgentInfo, action: CleanupAction): Promise<void>;
|
|
}
|
|
|
|
export class DefaultCleanupStrategy implements CleanupStrategy {
|
|
constructor(private cleanupManager: CleanupManager) {}
|
|
|
|
/**
|
|
* Determine what cleanup action should be taken for an agent.
|
|
* Considers agent status and debug mode setting.
|
|
*/
|
|
async shouldCleanup(agent: AgentInfo, isDebugMode: boolean): Promise<CleanupAction> {
|
|
log.debug({
|
|
agentId: agent.id,
|
|
name: agent.name,
|
|
status: agent.status,
|
|
isDebugMode
|
|
}, 'evaluating cleanup action for agent');
|
|
|
|
// Never cleanup agents waiting for user input
|
|
if (agent.status === 'waiting_for_input') {
|
|
log.debug({ agentId: agent.id, status: agent.status }, 'preserving agent waiting for input');
|
|
return 'preserve';
|
|
}
|
|
|
|
// Never cleanup running agents
|
|
if (agent.status === 'running') {
|
|
log.debug({ agentId: agent.id, status: agent.status }, 'preserving running agent');
|
|
return 'preserve';
|
|
}
|
|
|
|
// For completed/idle/crashed agents, decide based on debug mode
|
|
if (agent.status === 'idle' || agent.status === 'completed' || agent.status === 'crashed') {
|
|
if (isDebugMode) {
|
|
log.debug({ agentId: agent.id, status: agent.status }, 'archiving agent in debug mode');
|
|
return 'archive';
|
|
} else {
|
|
log.debug({ agentId: agent.id, status: agent.status }, 'removing agent in production mode');
|
|
return 'remove';
|
|
}
|
|
}
|
|
|
|
// For stopped agents, clean up immediately regardless of debug mode
|
|
if (agent.status === 'stopped') {
|
|
log.debug({ agentId: agent.id, status: agent.status }, 'removing stopped agent');
|
|
return 'remove';
|
|
}
|
|
|
|
// Default to preserve for any unrecognized status
|
|
log.debug({ agentId: agent.id, status: agent.status }, 'preserving agent with unrecognized status');
|
|
return 'preserve';
|
|
}
|
|
|
|
/**
|
|
* Execute the determined cleanup action.
|
|
*/
|
|
async executeCleanup(agent: AgentInfo, action: CleanupAction): Promise<void> {
|
|
log.debug({
|
|
agentId: agent.id,
|
|
name: agent.name,
|
|
action
|
|
}, 'executing cleanup action');
|
|
|
|
switch (action) {
|
|
case 'remove':
|
|
await this.cleanupManager.removeAgentWorktrees(agent.name, agent.initiativeId ?? null);
|
|
await this.cleanupManager.removeAgentBranches(agent.name, agent.initiativeId ?? null);
|
|
await this.cleanupManager.removeAgentLogs(agent.id);
|
|
log.info({ agentId: agent.id, name: agent.name }, 'agent workdir and resources removed');
|
|
break;
|
|
|
|
case 'archive':
|
|
await this.cleanupManager.archiveForDebug(agent.worktreeId, agent.id);
|
|
log.info({ agentId: agent.id, name: agent.name }, 'agent workdir archived for debugging');
|
|
break;
|
|
|
|
case 'preserve':
|
|
log.debug({ agentId: agent.id, name: agent.name }, 'agent workdir preserved');
|
|
break;
|
|
|
|
default:
|
|
log.warn({ agentId: agent.id, action }, 'unknown cleanup action, preserving by default');
|
|
}
|
|
}
|
|
} |