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