/** * Process lifetime for the daemon — deliberately the inverse of installMcpServerLifecycle: * no stdin coupling and no transport coupling. Only signals, an explicit stop, sustained * idleness (zero sessions AND zero active runs for the TTL), or supersession end the process. * The unref'd reaper also evicts dead-client sessions so they cannot hold the daemon alive * forever. * * Supersession (a newer daemon owns this family's discovery pointer) turns the daemon into a * lame duck, and the reaper then actively drains it instead of waiting for idleness: * - every session with no request in flight is closed so its * client transparently re-initializes on the successor; workflow execution remains on the * predecessor and is reached through the internal run-control plane; * - durable whole-stop intents are scanned on every reaper cadence; * - the moment nothing is busy — no sessions, runs, or requests — the daemon exits, * regardless of the idle TTL (even a disabled one: a superseded daemon with nothing to do is * garbage, not a long-lived service). */ import type { AgentRunner } from "@automatalabs/shared-types"; import type { DaemonHandle } from "./http-daemon.js"; export type DaemonShutdownReason = "SIGTERM" | "SIGINT" | "idle" | "superseded"; interface DaemonProcessHandle { once(event: "SIGINT" | "SIGTERM", listener: () => void): unknown; removeListener(event: "SIGINT" | "SIGTERM", listener: () => void): unknown; exit(code?: number): unknown; } export interface DaemonLifecycleOptions { daemon: DaemonHandle; runner: AgentRunner; ownPid: number; /** 0 disables idle shutdown (supersession still exits the daemon once nothing is in flight). */ idleTtlMs: number; sessionTtlMs: number; reaperIntervalMs?: number; process?: DaemonProcessHandle; log?: (line: string) => void; } export interface DaemonLifecycle { shutdown(reason: DaemonShutdownReason): Promise; } export declare function installDaemonLifecycle(options: DaemonLifecycleOptions): DaemonLifecycle; export {}; //# sourceMappingURL=daemon-lifecycle.d.ts.map