/**
 * McpProcessReaper — Option B of the MCP-leak fix (Codey's design in
 * `.instar/apprenticeship/codey-task-mcp-leak-reaper.md`).
 *
 * The problem: a session spawns MCP-server children (Playwright, mcp-remote,
 * instar stdio). Killing the session's main pid does NOT cascade to those
 * children — they re-parent (to PID 1 / launchd) or are held by an npm-exec
 * wrapper and survive for days. The fleet accumulated ~80 such leaked procs,
 * up to 5 days old, as a persistent load floor. `OrphanProcessReaper` reaps the
 * session-level CLI proc but not these MCP *descendants*.
 *
 * This is a descendant-aware SWEEP (a sibling reaper, NOT a change to the
 * shared OrphanProcessReaper / ReapGuard path — zero blast radius on the
 * working reaper). For each allow-listed MCP proc it resolves the owning tmux
 * session by walking the ppid chain, and reaps ONLY when the proc is old AND
 * its owning session is a dead/stale *instar* session or fully orphaned. It
 * NEVER touches a proc under a live/tracked session, nor under an external
 * (non-instar) tmux session.
 *
 * Ships DARK + DRY-RUN by default (kills processes — same posture as
 * SessionReaper / AgentWorktreeReaper). Every decision is audited.
 */
import { EventEmitter } from 'node:events';
import { matchMcpSignature, type McpProcessSignature } from './mcpProcessSignatures.js';
export interface McpProcessReaperConfig {
    enabled: boolean;
    dryRun: boolean;
    /** A proc younger than this is always kept (a starting session's children). */
    minAgeMs: number;
    reapIntervalMs: number;
    /** Bounded blast radius per pass. */
    maxReapsPerPass: number;
    /** Max ppid hops when resolving a proc's owning tmux session. */
    maxAncestorHops: number;
}
export declare const DEFAULT_MCP_PROCESS_REAPER_CONFIG: McpProcessReaperConfig;
export interface McpProcessInfo {
    pid: number;
    ppid: number;
    elapsedMs: number;
    command: string;
    signatureId: McpProcessSignature['id'];
}
export type McpVerdict = 'keep' | 'reap-eligible';
export interface McpEvaluation {
    pid: number;
    signatureId: McpProcessSignature['id'];
    ageMs: number;
    owningSession: string | null;
    verdict: McpVerdict;
    /** The gate that forced KEEP, or the reap rationale. */
    reason: string;
}
/**
 * All signal sources injected so the classifier is unit-testable without a
 * real process table, tmux, or kill(2). Production wiring supplies ps/tmux/
 * SessionManager-backed implementations.
 */
export interface McpProcessReaperDeps {
    /** Allow-listed MCP-server processes currently running (this uid). */
    listMcpProcesses: () => McpProcessInfo[];
    /** Full pid → ppid map of the process table (for ancestor resolution). */
    getProcessTree: () => Map<number, number>;
    /** tmux pane pid → session name. */
    getTmuxPaneMap: () => Map<number, string>;
    /** tmux session names that are genuinely live/tracked. SACRED — never reaped. */
    getLiveSessions: () => Set<string>;
    /** All instar-pattern tmux session names present (tracked or not). Used to
     *  tell a dead/stale *instar* session (reapable) from an external one (kept). */
    getInstarSessions: () => Set<string>;
    /** SIGTERM the proc. Only called when killsEnabled. */
    killProcess: (pid: number) => void;
    /** Append one audit entry (JSONL). Never throws into the reap loop. */
    audit?: (entry: McpReapAuditEntry) => void;
    now?: () => number;
}
export interface McpReapAuditEntry {
    ts: number;
    type: 'reaped' | 'would-reap' | 'kept';
    pid: number;
    signatureId: string;
    ageMs: number;
    owningSession: string | null;
    reason: string;
    dryRun: boolean;
}
/**
 * Walk the ppid chain from `startPid` until a pid is a tmux pane pid. Returns
 * the owning tmux session name, or null when no tmux ancestor is found within
 * `maxHops` (i.e. the proc is orphaned / re-parented — its session is dead).
 * Pure — cycle-safe via a visited set.
 */
export declare function resolveOwningSession(startPid: number, tree: Map<number, number>, tmuxPaneMap: Map<number, string>, maxHops: number): string | null;
/**
 * Pure per-proc classifier. KEEP unless the proc is old AND its owning session
 * is a dead/stale instar session or fully orphaned. Order matters: the sacred
 * gates (live session, external session) short-circuit before age is consulted.
 */
export declare function classifyMcpProcess(proc: McpProcessInfo, owningSession: string | null, liveSessions: Set<string>, instarSessions: Set<string>, minAgeMs: number): McpEvaluation;
export declare class McpProcessReaper extends EventEmitter {
    private readonly cfg;
    private readonly deps;
    private readonly now;
    private timer?;
    private running;
    private lastPassAt;
    private reapedLastPass;
    constructor(deps: McpProcessReaperDeps, cfg?: Partial<McpProcessReaperConfig>);
    start(): void;
    stop(): void;
    private get killsEnabled();
    /** Resolve + classify one proc against the current process/tmux/session view. */
    private evaluate;
    /** One reap pass. Returns the per-proc evaluations + what was reaped. */
    reap(): Promise<{
        ts: number;
        evaluations: McpEvaluation[];
        reaped: number[];
        dryRun: boolean;
    }>;
    private writeAudit;
    /** Observability snapshot for GET /processes/mcp-reaper (no side effects). */
    snapshot(): {
        enabled: boolean;
        dryRun: boolean;
        minAgeMs: number;
        lastPassAt: number;
        reapedLastPass: number;
        processes: McpEvaluation[];
        reapEligible: number;
    };
}
/** Re-export for production wiring that builds McpProcessInfo from a ps line. */
export { matchMcpSignature };
//# sourceMappingURL=McpProcessReaper.d.ts.map