/** * Memory-aware boot (crewly#1353): the snapshot the boot restore and the boot * maintenance passes read, the gate that holds a maintenance pass until the * restore queue is drained and the machine is calm, the pacer that spaces * launches across the restore queue and the deferred-start retry, and the * one-line owner note when boot restores were held back for long. * * Pressure mode (ResourceModeService) needs two tight 30 s samples before it * engages; the first minute after a boot is exactly when everything starts at * once, so the boot reads the raw memory reading as well. * * @module services/agent/boot-pressure */ /** What the boot looks at before a launch or a maintenance pass. */ export interface BootPressureSnapshot { usedPercent: number; freeMB: number; /** Physical memory; scales the free floor on small machines (0 or absent: not scaled) */ totalMB?: number; swapUsedPercent: number; osPressure: boolean; /** ResourceModeService is in pressure mode */ pressureMode: boolean; } /** Thresholds, with the optional env overrides applied. */ export interface BootPressureThresholds { swapUsedPercent: number; minFreeMB: number; launchSpacingMs: number; } /** * Thresholds from the constants block, with env overrides. * * @param env - Environment (tests inject one) * @returns Effective thresholds */ export declare function bootPressureThresholds(env?: NodeJS.ProcessEnv): BootPressureThresholds; /** * The free-memory floor for this machine: the configured floor, capped at * MIN_FREE_FRACTION of physical memory. A fixed 3 GB floor reads an 8 GB * laptop as tight on every boot (2026-10-17: the Air held Fern back while * its own health check said memory was fine). * * @param snapshot - Reading (its totalMB scales the floor) * @param thresholds - Effective thresholds * @returns Floor in MB */ export declare function effectiveMinFreeMB(snapshot: BootPressureSnapshot, thresholds: BootPressureThresholds): number; /** * Why the snapshot is tight for the boot, or null when it is calm. Tight: * pressure mode, the OS pressure flag, free memory under the floor, or swap * at or above the threshold while free memory is also under twice the * floor. Swap fill alone does not count: macOS grows its swap file on * demand, so a small swap file sits near full on a healthy machine. An * unreadable snapshot (null) is calm: a boot must not stall on a reading * that failed. * * @param snapshot - Reading, or null when unavailable * @param thresholds - Effective thresholds * @returns One-line reason, or null */ export declare function bootPressureReason(snapshot: BootPressureSnapshot | null, thresholds?: BootPressureThresholds): string | null; /** * The live snapshot: ResourceModeService's memory reading and mode. Null when * the reading throws or memory is unknown (totalMB 0). * * @returns Snapshot, or null */ export declare function readBootPressureSnapshot(): BootPressureSnapshot | null; /** * Remembers when the last boot launch began, so the restore queue and the * deferred-start retry keep the launch spacing between them. */ export declare class BootLaunchPacer { private readonly now; private lastAt; constructor(now?: () => number); /** A launch began now. */ note(): void; /** Milliseconds since the last launch began (Infinity when none yet). */ msSinceLast(): number; } /** The process-wide pacer. */ export declare function getBootLaunchPacer(): BootLaunchPacer; /** Test hook: drop the pacer. */ export declare function resetBootLaunchPacer(): void; /** What {@link bootLaunchHoldReason} looks at. */ export interface BootLaunchHoldDeps { snapshot: () => BootPressureSnapshot | null; /** The restore queue is still launching agents */ restoreRunning: () => boolean; pacer: BootLaunchPacer; /** Epoch ms of the boot */ bootAt: number; thresholds?: BootPressureThresholds; now?: () => number; holdMaxMs?: number; } /** * Why a boot launch outside the restore queue (the deferred-start retry) must * wait this round: memory is tight (only within `holdMaxMs` of the boot), the * restore queue is still launching, or another boot launch began less than * the launch spacing ago. Null = go. * * @param deps - Snapshot, restore state, pacer, boot time * @returns Reason to hold, or null */ export declare function bootLaunchHoldReason(deps: BootLaunchHoldDeps): string | null; /** What {@link waitForBootQuiet} needs. */ export interface BootQuietGateDeps { /** The boot restore is over (queue drained, or there was nothing to restore). */ restoreDone: () => boolean; /** Current reading (default: the live one). */ snapshot?: () => BootPressureSnapshot | null; thresholds?: BootPressureThresholds; sleep?: (ms: number) => Promise; now?: () => number; pollMs?: number; maxWaitMs?: number; } /** Outcome of the gate. */ export interface BootQuietGateResult { waitedMs: number; /** The gate gave up waiting; the pass should run reduced. */ reduced: boolean; /** Why it was still waiting when it gave up */ reason?: string; } /** * Hold a boot maintenance pass until the restore queue is drained and the * machine is not tight, polling every `pollMs`. After `maxWaitMs` it gives * up and returns `reduced: true` so the pass runs with a smaller batch. * * @param deps - Restore state, snapshot, clock * @returns How long it waited and whether the pass should run reduced */ export declare function waitForBootQuiet(deps: BootQuietGateDeps): Promise; /** * Names as a sentence fragment: "Ella", "Ella and Pia", "Ella, Pia and Owen". * * @param names - Agent names * @returns Joined names */ export declare function joinNames(names: readonly string[]): string; /** What {@link scheduleHeldBackNotice} needs. */ export interface HeldBackNoticeDeps { /** Agents held back by the boot that are still not running */ stillHeld: () => Promise; /** Sends one line to the owner; false when nothing is connected */ notify: (line: string) => Promise; afterMs?: number; setTimer?: (fn: () => void, ms: number) => void; } /** * One line to the owner, once, if agents the boot held back for memory are * still down `afterMs` later. Nothing is sent when they came back in time. * The line names the agents and says they will return; no mechanics. * * @param deps - Held-back check and notifier * @returns Resolves when the check ran (tests); callers do not await it */ export declare function scheduleHeldBackNotice(deps: HeldBackNoticeDeps): Promise; //# sourceMappingURL=boot-pressure.d.ts.map