/**
 * UpdateGate — Session-aware restart gating.
 *
 * Checks whether it's safe to restart the server for an update.
 * Only 'healthy' (actively producing output) sessions block restarts.
 * 'unresponsive', 'idle', and 'dead' sessions don't — blocking an update
 * for a broken session serves no user interest.
 *
 * Healthy sessions are NEVER killed for an update. The gate defers indefinitely
 * while healthy sessions exist, sending warnings at the configured thresholds.
 */
export interface SessionInfo {
    name: string;
    /** tmux session name; required for process-tree idle checks */
    tmuxSession?: string;
    topicId?: number;
    /** The job that spawned this session, if any */
    jobSlug?: string;
}
export interface SessionHealthEntry {
    topicId: number;
    sessionName: string;
    status: string;
    idleMinutes: number;
}
export interface GateResult {
    allowed: boolean;
    reason?: string;
    retryInMs?: number;
    /** Sessions that are actively blocking the restart */
    blockingSessions?: string[];
    /** Sessions that are unresponsive (warned but not blocking) */
    unresponsiveSessions?: string[];
    /** Background job sessions that were safe to ignore for restart gating */
    nonBlockingJobSessions?: string[];
}
export interface UpdateGateConfig {
    /** Maximum hours to defer a restart for active sessions. Default: 4 */
    maxDeferralHours?: number;
    /** Minutes before forced restart to send first warning. Default: 30 */
    firstWarningMinutes?: number;
    /** Minutes before forced restart to send final warning. Default: 5 */
    finalWarningMinutes?: number;
    /** How often to re-check sessions during deferral, in ms. Default: 5 * 60_000 (5 min) */
    retryIntervalMs?: number;
    /**
     * Primary-developer mode. When true, the gate NEVER defers a restart for
     * active sessions — `canRestart` always returns `{ allowed: true }`. The
     * agent prioritizes always running the latest version over protecting its
     * own sessions from a (session-surviving) server restart. Opt-in per agent
     * via `updates.restartImmediately`; default false leaves the fleet's
     * session-aware deferral untouched. Spec: docs/specs/restart-immediately-spec.md.
     */
    alwaysRestartImmediately?: boolean;
}
export interface UpdateGateStatus {
    /** Whether a restart is currently being deferred */
    deferring: boolean;
    /** When deferral started */
    deferralStartedAt: string | null;
    /** How long we've been deferring, in minutes */
    deferralElapsedMinutes: number;
    /** Max deferral before forced restart */
    maxDeferralHours: number;
    /** Reason for current deferral */
    deferralReason: string | null;
    /** Sessions currently blocking restart */
    blockingSessions: string[];
    /** Whether the first warning (T-30min) has been sent */
    firstWarningSent: boolean;
    /** Whether the final warning (T-5min) has been sent */
    finalWarningSent: boolean;
    /** Primary-developer mode: restarts are never deferred for active sessions */
    alwaysRestartImmediately: boolean;
}
/** Minimal interface for SessionManager — only what we need */
export interface SessionManagerLike {
    listRunningSessions(): SessionInfo[];
    hasActiveProcesses?(tmuxSession: string): boolean;
}
/** Minimal interface for SessionMonitor — only what we need */
export interface SessionMonitorLike {
    getStatus(): {
        sessionHealth: SessionHealthEntry[];
    };
}
export declare class UpdateGate {
    private config;
    private deferralStartedAt;
    private deferralReason;
    private blockingSessions;
    private firstWarningSent;
    private firstWarningPending;
    private finalWarningSent;
    private finalWarningPending;
    constructor(config?: UpdateGateConfig);
    /**
     * Toggle primary-developer mode at runtime (config may change on disk
     * without a restart). When enabled, the next `canRestart` allows immediately
     * and clears any in-flight deferral so a held restart proceeds at once.
     */
    setAlwaysRestartImmediately(value: boolean): void;
    /**
     * Check if it's safe to restart now.
     *
     * Returns { allowed: true } if restart can proceed.
     * Returns { allowed: false, retryInMs, reason } if sessions are blocking.
     */
    canRestart(sessionManager: SessionManagerLike, sessionMonitor?: SessionMonitorLike | null): GateResult;
    /**
     * Pure, side-effect-free probe: the names of active (healthy, non-job)
     * sessions that would block a restart right now.
     *
     * Unlike {@link canRestart}, this does NOT start or continue the deferral
     * clock, set warning flags, or call reset() — it is a read-only check for
     * callers that need "is the box idle?" without perturbing deferral state.
     * The restart-window gate uses it to skip the window wait when nothing is
     * active (an idle restart is invisible, so there is nothing for the window
     * to protect). Returns [] when there are no running sessions.
     */
    getBlockingSessions(sessionManager: SessionManagerLike, sessionMonitor?: SessionMonitorLike | null): string[];
    /**
     * Classify running sessions into active (blocking), unresponsive, and
     * non-blocking idle job sessions. Pure — no instance-state mutation. Shared
     * by {@link canRestart} (which then does deferral bookkeeping on the result)
     * and {@link getBlockingSessions} (read-only) so the classification can never
     * drift between the gating decision and the idle probe.
     */
    private classifyRunningSessions;
    private isSafeIdleJobSession;
    /**
     * Get current gate status for observability.
     */
    getStatus(): UpdateGateStatus;
    /**
     * Whether the first warning (T-30min before forced restart) should fire.
     * Returns true exactly once — consumes the flag on read.
     */
    shouldSendFirstWarning(): boolean;
    /**
     * Whether the final warning (T-5min before forced restart) should fire.
     * Returns true exactly once — consumes the flag on read.
     */
    shouldSendFinalWarning(): boolean;
    /**
     * Reset deferral state (called after restart proceeds or update is cancelled).
     */
    reset(): void;
}
//# sourceMappingURL=UpdateGate.d.ts.map