Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | 15x 23x | /**
* 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;
}
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');
}
}
} |