All files / src/agent/lifecycle cleanup-strategy.ts

6.06% Statements 2/33
0% Branches 0/21
33.33% Functions 1/3
6.06% Lines 2/33

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');
    }
  }
}