/**
 * Background process manager — spawns and tracks long-running shell processes
 * per session. Agents create bg processes via bash_bg_create tool (extension),
 * which calls the gateway REST API. The manager broadcasts real-time events
 * (output, exit) to connected WebSocket clients.
 */
import { type ChildProcess } from "node:child_process";
import type { WebSocket } from "ws";
/**
 * Function used to spawn the underlying child process. Injected via the
 * constructor so unit tests can supply a fake EventEmitter-backed child
 * without touching the OS. Production wiring uses {@link defaultSpawn}.
 */
export type SpawnFn = (command: string, cwd: string, containerId: string | undefined) => ChildProcess;
export interface LogEntry {
    ts: number;
    text: string;
}
export interface BgProcess {
    id: string;
    /** Short human-readable name (max 3 words, agent-generated) */
    name: string;
    command: string;
    pid: number;
    child: ChildProcess;
    stdout: string[];
    stderr: string[];
    /** Combined interleaved output (capped at MAX_LOG_LINES) */
    log: LogEntry[];
    status: "running" | "exited";
    exitCode: number | null;
    startTime: number;
    endTime: number | null;
    cwd: string;
    /** If set, process was spawned inside this Docker container */
    containerId?: string;
    /**
     * Resolves once the child has emitted `exit` AND `status`/`exitCode` have
     * been updated. Awaiters are guaranteed to observe the post-exit snapshot.
     * This eliminates the previous "50ms slack" between the exit event and
     * `status === 'exited'" — and removes a long-standing flake source.
     */
    exited: Promise<void>;
}
export interface BgProcessInfo {
    id: string;
    /** Short human-readable name (max 3 words, agent-generated) */
    name: string;
    command: string;
    pid: number;
    status: "running" | "exited";
    exitCode: number | null;
    startTime: number;
    endTime: number | null;
}
export declare class BgProcessManager {
    /** sessionId → Map<bgId, BgProcess> */
    private processes;
    /** sessionId → Set<WebSocket> — populated by session manager */
    private clientsProvider;
    /** sessionId → in-flight wait AbortControllers. Aborted when a steer arrives. */
    private waits;
    /** Per-instance id sequence — keeps test runs isolated from each other. */
    private nextId;
    /** Spawner — overridable in tests so unit tests don't touch the OS. */
    private spawnFn;
    constructor(clientsProvider: (sessionId: string) => Set<WebSocket> | undefined, spawnFn?: SpawnFn);
    private broadcast;
    create(sessionId: string, command: string, cwd: string, containerId?: string, sandboxed?: boolean, name?: string): BgProcessInfo;
    list(sessionId: string): BgProcessInfo[];
    getLogs(sessionId: string, processId: string): {
        log: LogEntry[];
        stdout: string[];
        stderr: string[];
    } | null;
    /** Search logs for a pattern (string or regex). Returns matching lines with context. */
    grepLogs(sessionId: string, processId: string, pattern: string, contextLines?: number, maxResults?: number): {
        matches: {
            line: number;
            ts: number;
            text: string;
        }[];
        total: number;
    } | null;
    /** Get first N lines of logs. */
    headLogs(sessionId: string, processId: string, lines?: number): {
        log: LogEntry[];
        totalLines: number;
    } | null;
    /** Get a range of log lines (1-indexed). */
    sliceLogs(sessionId: string, processId: string, from: number, to: number): {
        log: LogEntry[];
        totalLines: number;
    } | null;
    kill(sessionId: string, processId: string): boolean;
    /** Remove an exited process from the map. Returns true if removed. */
    remove(sessionId: string, processId: string): boolean;
    /** Clean up all bg processes for a session (on terminate) */
    cleanup(sessionId: string): void;
    /**
     * Register an AbortController so that `abortAllWaits(sessionId)` (called from
     * live-steer call sites or on session termination) can cancel in-flight waits.
     */
    registerWait(sessionId: string, controller: AbortController): void;
    /** Unregister a previously registered controller (called in the handler's finally). */
    unregisterWait(sessionId: string, controller: AbortController): void;
    /** Abort every in-flight wait for a session. Leaves the bg processes untouched. */
    abortAllWaits(sessionId: string): void;
    /**
     * Wait for a process to exit, the timeout to fire, or the provided AbortSignal.
     * Return shape is additive — happy-path/timeout set `aborted: false`; on abort
     * the process is left running and `info` is a snapshot (status still running).
     */
    waitForExit(sessionId: string, processId: string, timeoutMs: number, signal?: AbortSignal): Promise<{
        info: BgProcessInfo;
        timedOut: boolean;
        aborted: boolean;
    } | null>;
    /** Remove exited processes from the map */
    prune(sessionId: string): void;
    private toInfo;
}
//# sourceMappingURL=bg-process-manager.d.ts.map