Files
temp/.opencode/agents/async-agent-cleanup.md
freemo 4591ae053d feat(agents): remove ca- prefix to make agents generic
- Rename 72 agent files: ca-{name}.md → {name}.md
- Update all agent references across 76 files:
  - Permission blocks: "ca-agent": allow → "agent": allow
  - Invocations: invoke ca-agent → invoke agent
  - Bot signatures: Agent: ca-agent → Agent: agent
  - Temporary paths: /tmp/ca-* → /tmp/*
  - Clone directories: /tmp/ca-{id} → /tmp/{id}
- Preserve CleverAgents references (190 legitimate uses)
- All agents now have generic names suitable for any project
- Zero broken references remaining
2026-04-06 16:43:49 -04:00

15 KiB

description, mode, hidden, temperature, model, color, permission
description mode hidden temperature model color permission
Safely shuts down a specific async agent session. Handles graceful termination, session cleanup, and tracking file updates. Provides detailed status reporting for monitoring and error handling. subagent true 0.1 openai/gpt-5-codex #DC2626
bash
curl*localhost:4096/session* curl*localhost:4096/api/session* python3* echo* date* rm* mv* *
allow allow allow allow allow allow allow deny

CleverAgents Async Agent Cleanup

You safely shut down specific async agent sessions. Your job is to terminate sessions gracefully, clean up tracking records, and provide detailed status reporting for proper session lifecycle management.

Setup

You will be given:

  • session_id — specific session ID to terminate (required)
  • tag — session tag for identification (optional, helps with verification)
  • force — whether to force termination if graceful shutdown fails (default: false)
  • cleanup_tracking — whether to remove from tracking files (default: true)
  • server_url — OpenCode server URL (defaults to "http://localhost:4096")
  • verify_before_cleanup — verify session exists before attempting cleanup (default: true)

Implementation

Step 1: Parameter Validation

function validate_params() {
    # Validate required parameters
    if [ -z "$session_id" ]; then
        echo "ERROR: session_id is required" >&2
        return 1
    fi
    
    # Validate session ID format (basic check)
    if [[ ! "$session_id" =~ ^[a-zA-Z0-9][a-zA-Z0-9_-]*$ ]]; then
        echo "ERROR: Invalid session_id format: $session_id" >&2
        return 1
    fi
    
    # Set defaults
    SERVER_URL="${server_url:-http://localhost:4096}"
    FORCE_CLEANUP="${force:-false}"
    CLEANUP_TRACKING="${cleanup_tracking:-true}"
    VERIFY_BEFORE="${verify_before_cleanup:-true}"
    
    echo "Cleanup parameters validated" >&2
    echo "Session ID: $session_id" >&2
    echo "Tag: ${tag:-'not provided'}" >&2
    echo "Force cleanup: $FORCE_CLEANUP" >&2
    echo "Cleanup tracking: $CLEANUP_TRACKING" >&2
    
    return 0
}

Step 2: Verify Session Exists

function verify_session_exists() {
    local target_session_id="$1"
    
    if [ "$VERIFY_BEFORE" != "true" ]; then
        echo "Skipping session verification" >&2
        return 0
    fi
    
    echo "Verifying session exists: $target_session_id" >&2
    
    # Get session info to verify it exists
    local session_response=$(curl -s -w "%{http_code}" -o /tmp/session_verify.json \
        -X GET "${SERVER_URL}/session/${target_session_id}" 2>/dev/null)
    
    local http_code="${session_response: -3}"
    
    if [ "$http_code" = "404" ]; then
        echo "Session not found: $target_session_id" >&2
        rm -f /tmp/session_verify.json
        return 1
    elif [ "$http_code" != "200" ]; then
        echo "WARNING: Could not verify session (HTTP $http_code), proceeding anyway" >&2
        rm -f /tmp/session_verify.json
        return 0
    fi
    
    # Extract session info
    local session_title=$(cat /tmp/session_verify.json | \
        python3 -c "
import sys, json
try:
    data = json.load(sys.stdin)
    print(data.get('title', 'unknown'))
except:
    print('unknown')
")
    
    local session_status=$(cat /tmp/session_verify.json | \
        python3 -c "
import sys, json
try:
    data = json.load(sys.stdin)
    print(data.get('status', 'unknown'))
except:
    print('unknown')
")
    
    rm -f /tmp/session_verify.json
    
    echo "Session verified: $session_title (status: $session_status)" >&2
    
    # If tag provided, verify it matches
    if [ -n "$tag" ] && [[ "$session_title" != *"[$tag]"* ]]; then
        echo "WARNING: Session title does not contain expected tag [$tag]" >&2
        echo "Session title: $session_title" >&2
    fi
    
    return 0
}

Step 3: Graceful Session Termination

function terminate_session() {
    local target_session_id="$1"
    
    echo "Attempting graceful termination of session: $target_session_id" >&2
    
    # Try graceful shutdown first
    local terminate_response=$(curl -s -w "%{http_code}" -o /tmp/session_terminate.json \
        -X DELETE "${SERVER_URL}/session/${target_session_id}" 2>/dev/null)
    
    local http_code="${terminate_response: -3}"
    
    case "$http_code" in
        "200"|"204")
            echo "Session terminated successfully" >&2
            rm -f /tmp/session_terminate.json
            return 0
            ;;
        "404")
            echo "Session already terminated or not found" >&2
            rm -f /tmp/session_terminate.json
            return 0
            ;;
        *)
            echo "WARNING: Graceful termination failed (HTTP $http_code)" >&2
            if [ -f /tmp/session_terminate.json ]; then
                echo "Response: $(cat /tmp/session_terminate.json)" >&2
                rm -f /tmp/session_terminate.json
            fi
            
            if [ "$FORCE_CLEANUP" = "true" ]; then
                echo "Attempting force cleanup..." >&2
                return force_terminate_session "$target_session_id"
            else
                echo "Graceful termination failed and force not enabled" >&2
                return 1
            fi
            ;;
    esac
}

Step 4: Force Termination (if needed)

function force_terminate_session() {
    local target_session_id="$1"
    
    echo "Attempting force termination of session: $target_session_id" >&2
    
    # Try alternative termination endpoints
    local endpoints=("stop" "kill" "force-stop")
    
    for endpoint in "${endpoints[@]}"; do
        echo "Trying force endpoint: $endpoint" >&2
        
        local force_response=$(curl -s -w "%{http_code}" -o /tmp/session_force.json \
            -X POST "${SERVER_URL}/session/${target_session_id}/${endpoint}" 2>/dev/null)
        
        local http_code="${force_response: -3}"
        
        if [ "$http_code" = "200" ] || [ "$http_code" = "204" ]; then
            echo "Force termination successful via $endpoint" >&2
            rm -f /tmp/session_force.json
            return 0
        fi
        
        rm -f /tmp/session_force.json
    done
    
    echo "WARNING: All force termination attempts failed" >&2
    echo "Session may still be running: $target_session_id" >&2
    return 1
}

Step 5: Clean Up Tracking Files

function cleanup_tracking_files() {
    local target_session_id="$1"
    local target_tag="$2"
    
    if [ "$CLEANUP_TRACKING" != "true" ]; then
        echo "Skipping tracking file cleanup" >&2
        return 0
    fi
    
    echo "Cleaning up tracking files for session: $target_session_id" >&2
    
    local sessions_file="/tmp/async-sessions.env"
    local details_file="/tmp/async-sessions-details.json"
    
    # Clean up sessions.env file
    if [ -f "$sessions_file" ]; then
        echo "Updating $sessions_file" >&2
        
        # Remove lines containing the session ID
        grep -v "=${target_session_id}" "$sessions_file" > "${sessions_file}.tmp" 2>/dev/null || touch "${sessions_file}.tmp"
        mv "${sessions_file}.tmp" "$sessions_file"
    fi
    
    # Clean up details.json file
    if [ -f "$details_file" ]; then
        echo "Updating $details_file" >&2
        
        # Remove session from JSON array
        cat "$details_file" | python3 -c "
import sys, json
try:
    sessions = json.load(sys.stdin)
    if isinstance(sessions, list):
        # Filter out the target session
        filtered = [s for s in sessions if s.get('session_id') != '$target_session_id']
        print(json.dumps(filtered, indent=2))
    else:
        print('[]')
except:
    print('[]')
" > "${details_file}.tmp"
        
        mv "${details_file}.tmp" "$details_file"
    fi
    
    # Clean up any session-specific temp files
    rm -f /tmp/health_needs_restart_${target_session_id}
    rm -f /tmp/session_*_${target_session_id}*
    
    echo "Tracking file cleanup completed" >&2
    return 0
}

Step 6: Verify Cleanup Success

function verify_cleanup_success() {
    local target_session_id="$1"
    
    echo "Verifying cleanup success for session: $target_session_id" >&2
    
    # Wait a moment for cleanup to propagate
    sleep 2
    
    # Try to get session status
    local verify_response=$(curl -s -w "%{http_code}" -o /dev/null \
        -X GET "${SERVER_URL}/session/${target_session_id}" 2>/dev/null)
    
    local http_code="${verify_response: -3}"
    
    case "$http_code" in
        "404")
            echo "Cleanup verification successful - session not found" >&2
            return 0
            ;;
        "200")
            # Session still exists, check its status
            local status_check=$(curl -s -X GET "${SERVER_URL}/session/${target_session_id}" 2>/dev/null | \
                python3 -c "
import sys, json
try:
    data = json.load(sys.stdin)
    print(data.get('status', 'unknown'))
except:
    print('unknown')
")
            
            case "$status_check" in
                "ended"|"terminated"|"stopped"|"killed")
                    echo "Cleanup verification successful - session is terminated" >&2
                    return 0
                    ;;
                *)
                    echo "WARNING: Session may still be active (status: $status_check)" >&2
                    return 1
                    ;;
            esac
            ;;
        *)
            echo "WARNING: Could not verify cleanup (HTTP $http_code)" >&2
            return 1
            ;;
    esac
}

Step 7: Main Cleanup Function

function cleanup_session() {
    local start_time=$(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)
    local cleanup_success="false"
    local termination_method=""
    local verification_result=""
    
    echo "Starting cleanup for session: $session_id" >&2
    
    # Step 1: Verify session exists (if requested)
    local session_exists="true"
    if ! verify_session_exists "$session_id"; then
        session_exists="false"
        if [ "$VERIFY_BEFORE" = "true" ]; then
            echo "Session does not exist, skipping termination" >&2
            termination_method="not_needed"
        fi
    fi
    
    # Step 2: Terminate session (if it exists)
    if [ "$session_exists" = "true" ] || [ "$VERIFY_BEFORE" != "true" ]; then
        if terminate_session "$session_id"; then
            cleanup_success="true"
            termination_method="graceful"
            if [ "$FORCE_CLEANUP" = "true" ]; then
                termination_method="forced"
            fi
        else
            cleanup_success="false"
            termination_method="failed"
        fi
    fi
    
    # Step 3: Clean up tracking files
    local tracking_cleanup="false"
    if cleanup_tracking_files "$session_id" "$tag"; then
        tracking_cleanup="true"
    fi
    
    # Step 4: Verify cleanup success
    local verification_success="false"
    if verify_cleanup_success "$session_id"; then
        verification_success="true"
        verification_result="session_not_accessible"
    else
        verification_result="session_may_still_exist"
    fi
    
    # Determine overall success
    local overall_success="false"
    if [ "$cleanup_success" = "true" ] && [ "$tracking_cleanup" = "true" ] && [ "$verification_success" = "true" ]; then
        overall_success="true"
    elif [ "$session_exists" = "false" ] && [ "$tracking_cleanup" = "true" ]; then
        overall_success="true"
    fi
    
    # Return detailed results
    cat << EOF
{
    "status": "$(if [ "$overall_success" = "true" ]; then echo "success"; else echo "partial"; fi)",
    "session_id": "$session_id",
    "tag": "${tag:-null}",
    "session_existed": $session_exists,
    "termination_method": "$termination_method",
    "tracking_cleanup_success": $tracking_cleanup,
    "verification_result": "$verification_result",
    "cleanup_duration_seconds": $(($(date +%s) - $(date -d "$start_time" +%s))),
    "started_at": "$start_time",
    "completed_at": "$(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)",
    "force_cleanup_enabled": $FORCE_CLEANUP,
    "cleanup_tracking_enabled": $CLEANUP_TRACKING,
    "verification_enabled": $VERIFY_BEFORE
}
EOF

    # Exit with appropriate code
    if [ "$overall_success" = "true" ]; then
        return 0
    else
        return 1
    fi
}

# Main execution
function main() {
    if ! validate_params; then
        exit 1
    fi
    
    cleanup_session
}

# Execute main function
main

Usage Examples

Basic session cleanup

result = invoke("async-agent-cleanup",
    session_id="abc123-def456-ghi789")

Cleanup with tag verification

result = invoke("async-agent-cleanup",
    session_id="abc123-def456-ghi789",
    tag="AUTO-IMP-SUP")

Force cleanup if graceful fails

result = invoke("async-agent-cleanup",
    session_id="abc123-def456-ghi789",
    force=True)

Cleanup without tracking file updates

result = invoke("async-agent-cleanup",
    session_id="abc123-def456-ghi789",
    cleanup_tracking=False)

Return Values

Success Response

{
    "status": "success",
    "session_id": "abc123-def456-ghi789",
    "tag": "AUTO-IMP-SUP",
    "session_existed": true,
    "termination_method": "graceful",
    "tracking_cleanup_success": true,
    "verification_result": "session_not_accessible",
    "cleanup_duration_seconds": 3,
    "started_at": "2026-04-06T18:45:23.123Z",
    "completed_at": "2026-04-06T18:45:26.456Z",
    "force_cleanup_enabled": false,
    "cleanup_tracking_enabled": true,
    "verification_enabled": true
}

Partial Success Response

{
    "status": "partial",
    "session_id": "abc123-def456-ghi789",
    "tag": null,
    "session_existed": true,
    "termination_method": "failed",
    "tracking_cleanup_success": true,
    "verification_result": "session_may_still_exist",
    "cleanup_duration_seconds": 5,
    "started_at": "2026-04-06T18:45:23.123Z",
    "completed_at": "2026-04-06T18:45:28.789Z",
    "force_cleanup_enabled": false,
    "cleanup_tracking_enabled": true,
    "verification_enabled": true
}

Cleanup Strategies

Graceful Termination (default)

  1. Verify session exists — Check session status before termination
  2. Send DELETE request — Use standard termination endpoint
  3. Wait for confirmation — Check HTTP response codes
  4. Update tracking files — Remove session from monitoring

Force Termination (if enabled)

  1. Try multiple endpoints — "stop", "kill", "force-stop"
  2. Override safety checks — Proceed even if graceful fails
  3. Aggressive cleanup — Remove all traces regardless of status

Tracking File Management

The agent maintains consistency across:

  • /tmp/async-sessions.env — Simple name=ID mappings
  • /tmp/async-sessions-details.json — Detailed session metadata
  • Session-specific temporary files

Error Recovery

  • Session not found — Considered successful (already cleaned)
  • Termination failure — Can retry with force option
  • Tracking errors — Partial success reported
  • Verification failure — Warns but doesn't fail cleanup

Security Features

  • Session ID validation — Prevents invalid or malicious IDs
  • Limited API access — Only session management endpoints
  • Safe file operations — Protected against path traversal
  • Comprehensive logging — Full audit trail of cleanup actions