import { type NodeRow } from '../core/canvas/index.js'; import { FAULT_QUIET_MS } from '../core/runtime/fault.js'; export { readPidfile, isDaemonRunning } from './pidfile.js'; export { FAULT_QUIET_MS }; import { isPidAlive } from '../core/canvas/pid.js'; import { DaemonFleet, retryResumeMode } from './fleet.js'; export { retryResumeMode }; export declare function brokerThresholdsForDaemon(): { warning: number; automaticReviveCap: number; }; export declare function brokerCommandDeclaresCanvasHome(command: string, home: string): boolean; /** The `--epoch ` tag host.ts stamps into every broker's argv, or null * for a legacy/untagged broker (which the census treats as prior-epoch). */ export declare function brokerCommandEpoch(command: string): string | null; /** Startup recovery sweep (design flow C step 2): the FIRST job on the serial * work lane. The census already reaped every prior-epoch broker, so every * active|idle row at boot is known-dead — classify each through the SAME * policy table exit events use (fleet.recoverDeadNode → classifyDeadNode, * design D-12), so exit recovery and startup recovery can never diverge. A * raced API revive is safe: reviveNode's fleet.has guard makes the loser a * no-op. Capacity-gated: past the automatic-revive cap the sweep stops — the * tick's pass-2 inbox wakes own the remainder as capacity frees up. */ export declare function runStartupRecoverySweep(fleet: DaemonFleet): Promise; export declare const YIELD_STALL_GRACE_MS: number; /** Refresh-stall decision (§H refresh-authority; audit 2026-06-09, Bug 1 — node * mq5v9hfa-74493d45 and siblings; stuck-refresh — parent mq7s5o93): a `node * yield` / refresh records intent='refresh' and relies on the in-process * stophook to call shutdown at stop, but the engine can be left ALIVE — either * it HANGS instead of exiting (very large contexts) or a STALE stophook (pi * extensions never reload) ignores intent='refresh' entirely and never calls * shutdown. Either way: engine alive, turn over, intent pending forever. The * daemon only revives DEAD engines, so without this verdict the node is * permanently stuck (no revive, no wake). Pure core, mirroring wedgeVerdict; * the kill side effects live in handleYieldStall. * * 'kill' ONLY when ALL of: engine alive, intent='refresh', the turn is over * (busy marker absent — a node may legitimately keep working for many minutes * after running `node yield` mid-turn, and a working engine must never be * killed), and the state has persisted past YIELD_STALL_GRACE_MS. */ export declare function yieldStallVerdict(piPidAlive: boolean | null, intent: NodeRow['intent'], busy: boolean, stalledFor: number | null): 'leave' | 'pending' | 'kill'; export declare const WEDGE_QUIET_MS: number; export declare const WEDGE_CPU_MAX_PERCENT = 2; export type WedgeVerdict = 'leave' | 'pending' | 'wedged'; /** Pure wedge decision — mirrors yieldStallVerdict: a total * function of the observed signals, no clock or process access of its own. * `busy` false — the turn already ended (however it routed) — is never a * wedge, regardless of how stale the marker looks. `quietForMs` null means * the heartbeat marker couldn't be read (treated as “not enough evidence”, not * “wedged”). `cpuPercent` null (sample failed/unavailable) or above the * ceiling — EITHER keeps the verdict at 'pending' (keep watching next tick) * rather than firing on an unconfirmed or contradicted signal. */ export declare function wedgeVerdict(input: { busy: boolean; quietForMs: number | null; cpuPercent: number | null; }): WedgeVerdict; export declare function sumTreeCpu(psOutput: string, rootPid: number): number | null; /** Pure (issue #119): does `rootPid` have ANY descendant in this ps snapshot * — a runaway subprocess to kill — or does the broker stand ALONE in its own * tree (nothing to kill; the engine itself stalled)? Shares `parsePsTree` * with `sumTreeCpu` so the two facts the wedge remediation needs come from * one `ps` sample. Null when `rootPid` isn't in the sample at all (can't * tell — same "unknown, never treated as a fact" stance `sumTreeCpu` takes on * a missing root). Exported for direct unit testing. */ export declare function treeHasDescendants(psOutput: string, rootPid: number): boolean | null; export declare const DEAD_REAP_GRACE_MS: number; export { isPidAlive }; interface DaemonTickLifecycle { acceptsDetachedWork(): boolean; registerDetached(work: Promise): void; } /** Supervision tick over the daemon's current live set. */ export declare function superviseTick(now?: number, lifecycle?: DaemonTickLifecycle): Promise; export interface DaemonOpts { /** Milliseconds between supervision polls. Default 2000. */ intervalMs?: number; /** Opt-in TCP listener for the API server as `host:port` (§4.1). Falls back * to `CRTRD_TCP` when unset. Absent → unix socket only. */ tcp?: string; } /** Start the supervisor loop after winning the authoritative canvas claim. * * A losing claimant stays unbound and returns an inert async teardown so its * process exits cleanly. A winner returns the one serialized teardown that * quiesces admitted work, removes its readiness mirror, and releases its exact * claim. */ export declare function runDaemon(opts?: DaemonOpts): Promise<() => Promise>; export default runDaemon;