/**
 * @fileoverview Subagent Watcher - Real-time monitoring of Claude Code background agents.
 *
 * Watches `~/.claude/projects/{project}/{session}/subagents/agent-{id}.jsonl` files
 * and emits structured events for tool calls, progress, messages, and tool results.
 * Also detects Agent Teams teammates (distinguished by `<teammate-message>` in description).
 *
 * Key exports:
 * - `SubagentWatcher` class — singleton watcher, extends EventEmitter
 * - `subagentWatcher` — pre-instantiated singleton instance
 * - `SubagentInfo`, `SubagentToolCall`, `SubagentProgress`, `SubagentMessage`,
 *   `SubagentToolResult`, `SubagentTranscriptEntry` — data interfaces
 * - `SubagentEvents` — typed event map
 *
 * Watched patterns: `~/.claude/projects/{project}/{session}/subagents/agent-{id}.jsonl`
 * Parses JSONL entries: user/assistant messages, tool_use/tool_result blocks, progress events.
 * Tracks per-agent: status, token counts, model, description, tool call count, liveness (PID).
 *
 * @dependencies config/map-limits (MAX_TRACKED_AGENTS, PENDING_TOOL_CALL_TTL_MS),
 *   config/buffer-limits (FILE_PEEK_BYTES), utils (CleanupManager, KeyedDebouncer)
 * @consumedby web/server (SSE broadcast), session (subagent-session correlation)
 * @emits subagent:discovered, subagent:updated, subagent:tool_call, subagent:tool_result,
 *   subagent:progress, subagent:message, subagent:completed
 *
 * @module subagent-watcher
 */
import { EventEmitter } from 'node:events';
export interface SubagentInfo {
    agentId: string;
    sessionId: string;
    projectHash: string;
    filePath: string;
    startedAt: string;
    lastActivityAt: number;
    status: 'active' | 'idle' | 'completed';
    toolCallCount: number;
    entryCount: number;
    fileSize: number;
    description?: string;
    model?: string;
    modelShort?: 'haiku' | 'sonnet' | 'opus';
    totalInputTokens?: number;
    totalOutputTokens?: number;
    pid?: number;
}
export interface SubagentToolCall {
    agentId: string;
    sessionId: string;
    timestamp: string;
    tool: string;
    input: Record<string, unknown>;
    toolUseId?: string;
    fullInput: Record<string, unknown>;
}
export interface SubagentProgress {
    agentId: string;
    sessionId: string;
    timestamp: string;
    progressType: 'query_update' | 'search_results_received' | string;
    query?: string;
    resultCount?: number;
    hookEvent?: string;
    hookName?: string;
}
export interface SubagentMessage {
    agentId: string;
    sessionId: string;
    timestamp: string;
    role: 'user' | 'assistant';
    text: string;
}
export interface SubagentTranscriptEntry {
    type: 'user' | 'assistant' | 'progress';
    timestamp: string;
    agentId: string;
    sessionId: string;
    message?: {
        role: string;
        model?: string;
        usage?: {
            input_tokens?: number;
            output_tokens?: number;
        };
        content: string | Array<{
            type: 'text' | 'tool_use' | 'tool_result';
            text?: string;
            name?: string;
            id?: string;
            tool_use_id?: string;
            input?: Record<string, unknown>;
            content?: string | Array<{
                type: string;
                text?: string;
            }>;
            is_error?: boolean;
        }>;
    };
    data?: {
        type: string;
        query?: string;
        resultCount?: number;
        hookEvent?: string;
        hookName?: string;
        tool_name?: string;
    };
}
export interface SubagentToolResult {
    agentId: string;
    sessionId: string;
    timestamp: string;
    toolUseId: string;
    tool?: string;
    preview: string;
    contentLength: number;
    isError: boolean;
}
export declare class SubagentWatcher extends EventEmitter {
    private filePositions;
    private dirWatchers;
    private fileDeb;
    private agentInfo;
    private idleDeb;
    private cleanup;
    private _isRunning;
    private knownSubagentDirs;
    private pendingToolCalls;
    private _isCheckingLiveness;
    private _pollCount;
    private parentDescriptionCache;
    private dirWatcherErrorHandlers;
    private fileAgentContext;
    constructor();
    /**
     * Check if a description matches internal Claude Code agent patterns.
     * These are not real user-initiated subagents and should be filtered out.
     * Also filters out very short descriptions that are likely internal or malformed.
     */
    private isInternalAgent;
    /**
     * Mark a subagent as completed: clear PID, set status, clean up pending tool calls, emit event.
     */
    private markSubagentAsCompleted;
    /**
     * Extract text from message content, handling both string and array formats.
     * For array content, returns the text from the first 'text' block.
     */
    private extractFirstTextContent;
    /**
     * Process a tool_result content block: look up pending tool call, emit tool_result event.
     */
    private emitToolResult;
    /**
     * Find the oldest inactive (non-active) agent for LRU eviction.
     * Returns the agent ID of the oldest inactive agent, or null if all are active.
     */
    private findOldestInactiveAgent;
    /**
     * Extract short model identifier from full model name
     */
    private extractModelShort;
    /**
     * Start watching for subagent activity
     */
    start(): void;
    /**
     * Start periodic liveness checker
     * Detects when subagent processes have exited but status is still active/idle.
     *
     * Uses a 3-tier check to minimize cost:
     *   1. File mtime (stat ~0.3ms/agent) — if transcript modified recently, agent is alive
     *   2. Cached PID (/proc/{pid}/stat ~0.1ms) — if stored PID still exists, alive
     *   3. Full pgrep scan (expensive, ~500ms) — only for agents that fail tiers 1+2
     */
    private startLivenessChecker;
    /**
     * Check if a PID is still alive via /proc/{pid}/stat (single file read, ~0.1ms).
     */
    private checkPidAlive;
    /**
     * Run pgrep once and read /proc info for all Claude PIDs in parallel.
     * Returns a Map of pid -> { environ, cmdline } for subagent processes only.
     * Excludes main Codeman-managed Claude processes (CODEMAN_MUX=1).
     * Also updates cached PIDs on tracked agents when a match is found.
     */
    private getClaudePids;
    /**
     * Check if a subagent is alive using the pre-fetched pid map (no process spawning).
     */
    private checkSubagentAliveFromPidMap;
    /**
     * Check if a subagent's transcript file was recently modified.
     * Primary liveness signal — transcript files are written to continuously while agent is active.
     */
    private checkSubagentFileAlive;
    /**
     * Check if the watcher is currently running
     */
    isRunning(): boolean;
    /**
     * Stop watching and clean up all state
     */
    stop(): void;
    /**
     * Clean up stale agents to prevent unbounded memory growth.
     * - Completed agents: removed after STALE_COMPLETED_MAX_AGE_MS (1 hour)
     * - Idle agents: removed after STALE_IDLE_MAX_AGE_MS (4 hours)
     * Also enforces MAX_TRACKED_AGENTS limit with LRU eviction.
     * Also cleans up orphaned pending tool calls older than PENDING_TOOL_CALL_TTL_MS.
     */
    private cleanupStaleAgents;
    /**
     * Remove an agent and all its associated resources.
     */
    private removeAgent;
    /**
     * Manually trigger cleanup of stale agents.
     * Returns number of agents removed.
     */
    cleanupNow(): number;
    /**
     * Clear all tracked agents (for manual reset).
     * Returns number of agents cleared.
     */
    clearAll(): number;
    /**
     * Get all known subagents
     */
    getSubagents(): SubagentInfo[];
    /**
     * Get subagents for a specific Codeman session
     * Maps Codeman working directory to Claude's project hash
     */
    getSubagentsForSession(workingDir: string): SubagentInfo[];
    /**
     * Get a specific subagent's info
     */
    getSubagent(agentId: string): SubagentInfo | undefined;
    /**
     * Update a subagent's description.
     * Used to set the short description from TaskTracker when available.
     */
    updateDescription(agentId: string, description: string): boolean;
    /**
     * Find sessions whose working directory matches a project hash.
     * Returns the project hash for a given working directory.
     */
    getProjectHashForDir(workingDir: string): string;
    /**
     * Get internal statistics for memory monitoring.
     * Returns counts of internal Maps and resources.
     */
    getStats(): {
        agentCount: number;
        fileDebouncerCount: number;
        dirWatcherCount: number;
        idleTimerCount: number;
        pendingToolCallsCount: number;
        knownDirsCount: number;
        filePositionsCount: number;
    };
    /**
     * Get recent subagents (modified within specified minutes)
     */
    getRecentSubagents(minutes?: number): SubagentInfo[];
    /**
     * Kill a subagent by its agent ID
     * Uses cached PID first, falls back to findSubagentProcess if needed.
     */
    killSubagent(agentId: string): Promise<boolean>;
    /**
     * Kill all subagents for a specific Codeman session.
     * IMPORTANT: Must scope to sessionId to avoid cross-session kills.
     * All sessions in the same workingDir share a projectHash, so filtering
     * by workingDir alone would kill subagents belonging to OTHER sessions.
     */
    killSubagentsForSession(workingDir: string, sessionId?: string): Promise<void>;
    /**
     * Find the process ID of a Claude subagent by its session ID.
     * Searches /proc for claude processes with matching session ID in environment.
     * Skips main Codeman-managed Claude processes (identified by CODEMAN_MUX=1).
     * Caches the discovered PID on the matching agent info for future fast checks.
     */
    private findSubagentProcess;
    /**
     * Store a discovered PID on the agent info with matching sessionId.
     */
    private cacheAgentPid;
    /**
     * Get transcript for a subagent (optionally limited to last N entries)
     */
    getTranscript(agentId: string, limit?: number): Promise<SubagentTranscriptEntry[]>;
    /**
     * Format transcript entries for display
     */
    formatTranscript(entries: SubagentTranscriptEntry[]): string[];
    /**
     * Convert working directory to Claude's project hash format
     */
    private getProjectHash;
    /**
     * Extract a smart, concise title from a task prompt
     * Aims for ~40-50 chars that convey what the agent is doing
     */
    private extractSmartTitle;
    private _resolveDescription;
    /**
     * Extract the short description from the parent session's transcript.
     * This is the most reliable method because it reads the actual Task tool result
     * that spawned this agent, which contains the description directly.
     *
     * The parent transcript contains a 'user' entry with toolUseResult that has:
     * - agentId: the spawned agent's ID
     * - description: the Task description parameter
     *
     * We look for this format:
     * { "type": "user", "toolUseResult": { "agentId": "xxx", "description": "..." } }
     */
    private extractDescriptionFromParentTranscript;
    /**
     * Extract description from agent file by finding first user message
     */
    private extractDescriptionFromFile;
    /**
     * Scan for all subagent directories (async to avoid blocking event loop)
     */
    private scanForSubagents;
    /**
     * Watch a subagent directory for new and changed files.
     * Uses a single directory-level fs.watch() instead of per-file watchers.
     * On Linux, inotify IN_MODIFY fires for content changes within the directory.
     */
    private watchSubagentDir;
    /**
     * Handle a file content change for an already-registered agent file.
     * Tails from last known position, updates info, retries description if missing.
     */
    private handleFileChange;
    /**
     * Register a specific agent transcript file (discovery + initial read).
     * Does NOT create a per-file watcher — the directory watcher handles changes.
     * @param filePath Path to the agent transcript file
     * @param projectHash Claude project hash
     * @param sessionId Claude session ID
     * @param isInitialScan If true, skip files older than STARTUP_MAX_FILE_AGE_MS
     */
    private registerAgentFile;
    /**
     * Tail a file from a specific position
     */
    private tailFile;
    /**
     * Process a transcript entry and emit appropriate events
     */
    private processEntry;
    private _processModelInfo;
    private _processTokenInfo;
    private _processDescription;
    private _processAssistantContent;
    private _processUserContent;
    /**
     * Extract text content from tool_result content field
     */
    private extractToolResultContent;
    /**
     * Get truncated input for display (keeps primary param, truncates large content)
     */
    private getTruncatedInput;
    /**
     * Reset idle timer for an agent
     */
    private resetIdleTimer;
    /**
     * Format a tool call for display
     */
    private formatToolCall;
    /**
     * Format a progress event for display
     */
    private formatProgress;
    /**
     * Format timestamp for display
     */
    private formatTime;
}
export declare const subagentWatcher: SubagentWatcher;
//# sourceMappingURL=subagent-watcher.d.ts.map