import { type NodeMeta } from '../core/canvas/index.js'; import type { FleetEntry, FleetExitStatus, FleetRegistry } from '../core/runtime/fleet.js'; import type { HostHandle } from '../core/runtime/host.js'; /** Surface a vehicle that never booted (or exhausted its respawn budget). * * A pi that dies before its first session_start (so `pi_session_id` was never * recorded) is invisible to its parent — the spawn returned optimistically. * When the daemon observes that death it errors LOUDLY up the spine: an * urgent push so the parent learns the child failed to launch instead of just * seeing a silent `dead`. */ export declare function surfaceBootFailure(meta: NodeMeta): Promise; /** Event-driven follow-up human-work settlement at daemon-detected node death * (design-followup-consult-v2.md §B.4 step 4). Re-runs the consult-outbox * reconcile scan right where the daemon first observes a death, so an `armed` * row whose fork just died with no final report becomes the ticket's error * result immediately. A no-op for every crash that isn't a consult/visual * fork. Best-effort, mirrors the startup call's error handling. */ export declare function reconcileFollowUpWorkOnCrash(id: string): void; /** Whether a dead relaunch should resume the saved session strictly, or retry * as a fresh cycle instead. A cycling attempt (cycle_pending) that dies * before session_start must retry AS a cycle — a strict reopen only restores * the transcript at an idle prompt, it cannot restart the dead engine turn. * A dirty interrupted BUSY turn also requests a cycle; a broker-certified * clean abort is handled explicitly by classifyDeadNode before this fallback. */ export declare function retryResumeMode(meta: Pick | null, interruptedTurn?: boolean): boolean; export type DeadNodeAction = 'forget' | 'respawn-fresh' | 'dormant' | 'respawn-cycle' | 'respawn-resume' | 'boot-failure'; /** The narrow durable state the policy table reads. Structural (a Pick, not * NodeMeta itself) so tests fabricate table rows directly. */ export type DeadNodeRow = Pick; /** One pure function producing the action for a node known-dead. Used at * exactly two call sites — an exit-policy job and the startup recovery sweep * (design D-12) — so exit recovery and startup recovery can never diverge. * Evaluated in table order; `busy` is the on-disk busy marker. A clean-abort * certificate is written by the broker only after Pi persisted its aborted * entry; without it, a set busy marker means a dirty mid-turn crash. */ export declare function classifyDeadNode(row: DeadNodeRow | null, busy: boolean, cleanAbort?: boolean): DeadNodeAction; /** An exit with uptime at or above this resets the consecutive-failure * counter before classification; a shorter-lived exit increments it. */ export declare const HEALTHY_UPTIME_MS = 60000; /** At this many consecutive short-lived exits the respawn loop terminalizes: * transition('crash') + surfaceBootFailure + doctrine wake, counter cleared. */ export declare const MAX_RESPAWN_ATTEMPTS = 6; /** Backoff before the next respawn given the consecutive-failure count. * Zero failures → immediate (the sub-second yield respawn D-3 buys); the * n-th delayed attempt waits min(5s·2^n, 10min). */ export declare function respawnBackoffMs(consecutiveFailures: number): number; export interface DaemonFleetOpts { /** This daemon's epoch id (randomBytes(8).toString('hex'), minted by * runDaemon after the ownership claim). */ epoch: string; /** The serial work lane. Exit-policy jobs and backoff respawns run here, * strictly one at a time with ticks. */ enqueue: (job: () => void | Promise) => void; /** Injectable clock for tests. */ now?: () => number; } export declare class DaemonFleet implements FleetRegistry { #private; constructor(opts: DaemonFleetOpts); epoch(): string; register(nodeId: string, handle: HostHandle): void; has(nodeId: string): boolean; get(nodeId: string): FleetEntry | undefined; size(): number; entries(): Iterable<[string, FleetEntry]>; forget(nodeId: string): void; onChildExit(nodeId: string, status: FleetExitStatus): void; deliverExit(nodeId: string, status: FleetExitStatus): void; /** Startup recovery sweep entry (design flow C step 2 / D-12): classify a * known-dead row and enact immediately through the SAME table exit events * use. Returns the action so the caller can capacity-gate launches. */ recoverDeadNode(nodeId: string): Promise; /** A revive refusal before its launch try/catch leaves the row active but * with no fleet entry. Convert that otherwise-silent strand into the same * terminal outcome as a boot failure. The fresh read avoids duplicating * reviveNode's launch-failure crash path. */ terminalizeIfStillNonTerminal(nodeId: string, event: string, error: unknown): Promise; /** Daemon shutdown: cancel every pending backoff respawn. Live processes * are torn down by the daemon's fleet-teardown stage, not here. */ dispose(): void; }