/** * Staggered boot restore: agents with work in hand come back one at a time * (each Claude Code relaunch on a loaded machine takes minutes), the owner's * agents first. An on-demand wake for an agent still waiting in the queue * moves it up instead of starting a second launch in parallel. * * @module services/agent/restore-queue */ import { type BootPressureSnapshot, type BootPressureThresholds } from './boot-pressure.js'; /** Defaults for the restore queue. */ export declare const RESTORE_QUEUE_CONSTANTS: { readonly DEFAULT_CONCURRENCY: 1; readonly MAX_CONCURRENCY: 3; /** Per-agent budget before the queue moves on and retries it later */ readonly DEFAULT_TIMEOUT_MS: number; /** Times an agent that timed out is tried again, at the back of the queue */ readonly MAX_RETRIES: 1; }; /** Restore priority tiers, lowest first. */ export declare const RESTORE_TIER: { readonly OWNER: 1; readonly ORCHESTRATOR: 2; readonly LEAD: 3; readonly OTHER: 4; }; /** One agent to restore. */ export interface RestoreEntry { name: string; tier: number; /** Why it has this position (logged in the plan) */ reason: string; /** Age of the oldest owner message waiting for it (ms epoch); orders the owner tier */ ownerSince?: number; /** Start the agent; resolves when it is registered. `attempt` is 0 on the first try. */ run: (attempt: number) => Promise<{ success: boolean; }>; /** Called right after the agent registered */ onReady?: () => Promise; /** * Launched even while memory is tight (crewly#1353): the owner waits on it * (owner message, owed promise, owner thread) or it is the orchestrator. * Everything else is deferred to the retry when the machine is tight. */ essential?: boolean; /** The queue handed the agent to the deferred-start retry instead of launching it (tight memory) */ onDeferred?: (snapshot: BootPressureSnapshot | null, reason: string) => void; } /** Memory awareness and pacing of the queue (tests inject fakes). */ export interface RestoreQueueOptions { /** Current memory reading (default: the live one; null = unknown, treated as calm) */ snapshot?: () => BootPressureSnapshot | null; thresholds?: BootPressureThresholds; now?: () => number; sleep?: (ms: number) => Promise; /** A launch began (default: the process-wide pacer) */ onLaunch?: () => void; settleMs?: number; pollMs?: number; holdMaxMs?: number; pressureConcurrency?: number; } /** Progress counters exposed in /health. */ export interface RestoreQueueStats { running: boolean; pending: number; started: number; ready: number; failed: number; timedOut: number; /** Handed to the deferred-start retry because memory was tight */ deferred: number; order: string[]; } /** * Concurrency from `CREWLY_RESTORE_CONCURRENCY`, clamped to 1..3. * * @param raw - Env value * @returns Concurrency */ export declare function restoreConcurrencyFromEnv(raw?: string | undefined): number; /** * Order entries: tier first; inside the owner tier, oldest owner message first. * * @param entries - Entries to order * @returns A new, ordered array */ export declare function orderRestoreEntries(entries: readonly RestoreEntry[]): RestoreEntry[]; /** The queue itself. */ export declare class RestoreQueue { private readonly concurrency; private readonly timeoutMs; private pending; private active; private counters; private running; private done; private readonly logger; private readonly snapshot; private readonly thresholds; private readonly now; private readonly sleep; private readonly onLaunch; private readonly settleMs; private readonly pollMs; private readonly holdMaxMs; private readonly pressureConcurrency; /** When the last launch settled (registered, failed or timed out); spacing counts from here */ private lastSettledAt; constructor(concurrency?: number, timeoutMs?: number, opts?: RestoreQueueOptions); /** * Plan and run the restore. Resolves when every entry is ready, failed or * out of retries. Returns the same promise while already running. * * @param entries - Agents to restore * @returns Promise settled when the queue is drained */ start(entries: readonly RestoreEntry[]): Promise; /** Whether the agent is still waiting (not yet started) in the queue. */ isPending(name: string): boolean; /** Whether the agent is waiting in the queue or being started by it right now. */ isRestoring(name: string): boolean; /** * An on-demand wake arrived for an agent. Owner-triggered wakes move it to * the front (behind earlier promoted ones); other wakes keep queue order. * * @param name - Session name * @param ownerTriggered - The wake carries an owner message * @returns True when the agent is waiting in the queue (the caller must not start it) */ wake(name: string, ownerTriggered: boolean): boolean; /** Progress for /health. */ stats(): RestoreQueueStats; private worker; /** * Memory is tight and nobody waits on this agent: hand it to the * deferred-start retry (it is owed something, so it is not dropped) and * log the reading it was deferred on. */ private defer; /** * Wait before a launch: always the launch spacing since the previous one * settled (RSS growth is visible before the next); under pressure also the * settle time, the pressure concurrency, and the snapshot clearing (at most * `holdMaxMs`, then the owner-waiting agent launches anyway). */ private paceBeforeLaunch; private runSlot; } /** The process-wide restore queue. */ export declare function getRestoreQueue(): RestoreQueue; /** Test hook: drop the singleton. */ export declare function resetRestoreQueue(): void; //# sourceMappingURL=restore-queue.d.ts.map