/**
 * ReapNoticeDrain — the ALWAYS-ON delivery loop for reap notices
 * (reap-notify spec R1.3, tier0 supervision — deterministic delivery of
 * pre-authored template content, declared per P7).
 *
 * Why it exists: the durable layer's existing drain (DeliveryFailureSentinel)
 * is default-OFF fleet-wide, so "DFS will retry it" is false on a default
 * agent — the round-2 foundation audit's central finding. The R1 guarantee
 * ("every non-silent reap produces a durable, retried, recorded notice")
 * therefore ships its OWN small always-on drain, independent of the DFS flag.
 *
 * Contract:
 *  - Claims ONLY rows inside the `reap-notify:` PK range (index-compatible
 *    range predicate; DFS claims the complement). The claim itself is a CAS
 *    UPDATE — two drains can never double-claim a row.
 *  - 30s tick; idle cost is one indexed claim query (~zero on an empty store).
 *  - Per-pass send cap (15) keeps a 500-topic storm under Telegram's
 *    per-group rate: 500 durable rows drain over ~17 minutes (R1.5's global
 *    release throttle); the remainder is picked up next tick.
 *  - Delivery is the DIRECT adapter send (`sendToTopic`) — NOT the
 *    /telegram/reply relay — so the relay's tone gate, whoami check, and
 *    duplicate-suppression are structurally off this path (notices carry
 *    per-notice distinct content anyway).
 *  - Bounded retries: store-backed exponential backoff on the existing
 *    attempts/next_attempt_at columns; at `maxAttempts` (8) the row is
 *    escalated terminally into ONE aggregated attention item (updated in
 *    place — never per-row items; P17).
 *  - Outcome records (R1.3 pairs): the terminal record (`sent` /
 *    `send-failed-escalated`) is appended HERE, event-driven from the drain
 *    that owns the terminal transition.
 *  - Loop brakes (P19, declared): backoff + maxAttempts + per-pass cap +
 *    terminal escalation; plus a bounded terminal-row cleanup so the
 *    always-on lane cannot grow the store unboundedly while DFS's retention
 *    pass is off.
 */
import type { PendingRelayRow, DeliveryState } from '../messaging/pending-relay-store.js';
import type { ReapNotifyOutcome } from './ReapLog.js';
export interface ReapNoticeDrainStore {
    selectClaimableReapNotices(nowIso: string, limit?: number): PendingRelayRow[];
    claimCas(deliveryId: string, newClaimedBy: string, expected: {
        state: DeliveryState;
        claimed_by: string | null;
    }): boolean;
    transition(deliveryId: string, newState: DeliveryState, fields?: Partial<{
        claimed_by: string | null;
        next_attempt_at: string | null;
        attempts: number;
        error_body: string | null;
    }>): boolean;
    purgeTerminalReapNotices(beforeIso: string): number;
}
export interface ReapNoticeDrainDeps {
    store: ReapNoticeDrainStore;
    /** Direct adapter send to an EXISTING topic (never creates topics). */
    sendToTopic: (topicId: number, text: string) => Promise<void>;
    /** Append a notify outcome record (reap-log pairs, R1.3). */
    recordNotify: (e: {
        noticeId: string;
        topicId: number | null;
        outcome: ReapNotifyOutcome;
        detail?: string;
    }) => void;
    /** ONE aggregated, deduped attention item (stable id) for terminal
     *  escalations — updated in place, never per-row (P17). */
    emitAttention?: (item: {
        id: string;
        title: string;
        summary?: string;
        description?: string;
        category?: string;
        priority?: 'low' | 'medium' | 'high';
        sourceContext?: string;
    }) => Promise<void>;
    bootId: string;
    now?: () => number;
}
export interface ReapNoticeDrainOptions {
    tickIntervalMs: number;
    perPassSendCap: number;
    maxAttempts: number;
    leaseDurationMs: number;
    backoffBaseMs: number;
    backoffMaxMs: number;
    /** Terminal rows older than this are cleaned from the reap-notify lane. */
    terminalRetentionMs: number;
}
export declare const DEFAULT_REAP_NOTICE_DRAIN_OPTIONS: ReapNoticeDrainOptions;
export declare class ReapNoticeDrain {
    private readonly deps;
    private readonly opts;
    private readonly now;
    private timer;
    private ticking;
    /** Rolling escalation aggregate since boot (feeds the ONE attention item). */
    private escalations;
    private lastCleanupAt;
    constructor(deps: ReapNoticeDrainDeps, opts?: Partial<ReapNoticeDrainOptions>);
    start(): void;
    stop(): void;
    /** One drain pass. Public so tests (and a manual flush) can drive it. */
    tick(): Promise<{
        sent: number;
        retried: number;
        escalated: number;
    }>;
    /** Lease format mirrors DFS: `<bootId>:<pid>:<leaseUntilIso>`. */
    private isLeaseStale;
    private safeTransition;
    private safeRecord;
    /** ONE rolling deduped attention item, updated in place (P17). */
    private escalate;
}
//# sourceMappingURL=ReapNoticeDrain.d.ts.map