/** * Runtime PID Registry * * Tracks the OS process ids of spawned agent-runtime PTY sessions so orphaned * runtime processes can be reaped after a NON-graceful backend death (crash, * OOM-kill, external `kill -9`, or the shutdown force-exit `SIGKILL`-self firing * mid-cleanup). Normal teardown (`killSession`/`forceDestroyAll`) already reaps * children; the gap this closes is the cross-restart one — a fresh backend has * no record of the previous run's children, so they live on indefinitely * (issue #715: ~7 stray `gemini --yolo` on Irissair since Feb). * * Safety is the whole point. The startup reaper: * 1. only ever touches PIDs THIS backend recorded (never a broad `pkill`), * 2. re-reads each live PID's command line and kills it ONLY if it still * matches the recorded spawn signature — so a PID reused by an unrelated * process after a crash is left untouched. * * @module services/session/runtime-pid-registry.service */ /** Registry file name under the Crewly home. */ export declare const RUNTIME_PID_FILE = "runtime-pids.json"; /** * Read the live command line for a pid, cross-platform, or null if the process * does not exist / is unreadable. * * - Linux: `/proc//cmdline` (NUL-separated argv). * - Other (macOS/BSD): `ps -o command= -p `. * * @param pid - Process id to inspect. * @returns The normalised command line, or null. */ export declare function readProcessCmdline(pid: number): string | null; /** Whether a pid is currently alive (signal 0 probe). */ export declare function isPidAlive(pid: number): boolean; /** * Persistent, identity-verified registry of spawned runtime PIDs. * * Records are written on session spawn and removed on clean teardown; whatever * remains at the next boot is a candidate orphan from a non-graceful death. */ export interface RuntimePidRegistryOptions { /** Override the registry file location (tests). */ filePath?: string; /** Override how a pid's live command line is read (tests). */ cmdlineReader?: (pid: number) => string | null; } export declare class RuntimePidRegistry { private static instance; private readonly filePath; private readonly cmdlineReader; private readonly logger; private constructor(); static getInstance(): RuntimePidRegistry; /** Reset for tests (optionally pointing at a temp file + stub cmdline reader). */ static resetForTesting(options?: RuntimePidRegistryOptions): RuntimePidRegistry; private read; private write; /** * Record a freshly spawned runtime process. * * @param pid - PTY child pid. * @param sessionName - Owning session. * @param command - Spawn executable. * @param args - Spawn args. */ record(pid: number, sessionName: string, command: string, args?: string[]): void; /** * Drop the record for a session we have torn down ourselves (so it is not a * reap candidate next boot). * * @param sessionName - Session whose record to remove. */ remove(sessionName: string): void; /** Clear all records (graceful shutdown reaped everything itself). */ clear(): void; /** * Reap orphaned runtime processes left by a previous, non-graceful backend * exit. Call ONCE at boot, before sessions are restored. Kills a recorded pid * only if it is still alive AND its live command line still matches the * recorded signature (guards against PID reuse). Clears the registry after. * * @returns The number of orphan processes reaped. */ reapOrphans(): number; } //# sourceMappingURL=runtime-pid-registry.service.d.ts.map