/**
 * InboxDrainer.ts — moves accepted fleet feedback from the cloud inbox (Vercel
 * Blob, written by the canonical front) into the durable canonical FeedbackStore
 * on the operated machine.
 *
 * This is the Echo-machine half of the Option-B receiving end: the front makes a
 * report DURABLE the instant it is accepted (no operated machine in the intake
 * critical path); this drainer ingests at its own pace, so a machine asleep /
 * restarting / tunnel-down only DELAYS processing — it never loses a report.
 *
 * Delivery semantics: AT-LEAST-ONCE, made idempotent by the feedbackId dedup the
 * receiver path already has (`store.hasFeedback`). The ordering invariant is
 * delete-AFTER-durable-commit: a blob is removed from the inbox only after the
 * store append has returned. A crash between commit and delete re-delivers the
 * blob on the next tick and dedup drops it.
 *
 * Poison handling: a blob that cannot be parsed/validated is QUARANTINED (re-put
 * under `quarantine/`, then deleted from the inbox) so one malformed object can
 * never wedge the drain loop, and nothing is silently destroyed.
 *
 * Signal-vs-authority: this component holds NO decision authority — every
 * accept/reject decision happened in the front's intake defense chain. The
 * drainer is pure transport + persistence, plus read-only counters for
 * `GET /feedback-inbox/status`.
 */
import type { BlobInboxClient } from './BlobInboxClient.js';
import type { FeedbackItem } from '../processor/types.js';
export interface InboxDrainerOptions {
    client: BlobInboxClient;
    store: {
        hasFeedback(feedbackId: string): boolean;
        addFeedback(item: FeedbackItem): void;
    };
    /** Inbox prefix the front writes under. */
    prefix?: string;
    /** Poll cadence. */
    pollIntervalMs?: number;
    /** Page size per list call. */
    pageSize?: number;
    log?: (msg: string) => void;
}
export interface InboxDrainerStatus {
    running: boolean;
    prefix: string;
    pollIntervalMs: number;
    drained: number;
    duplicates: number;
    quarantined: number;
    errors: number;
    ticks: number;
    lastTickAt: string | null;
    lastDrainAt: string | null;
    lastError: string | null;
}
export declare class InboxDrainer {
    private readonly client;
    private readonly store;
    private readonly prefix;
    private readonly pollIntervalMs;
    private readonly pageSize;
    private readonly log;
    private timer;
    private ticking;
    private drained;
    private duplicates;
    private quarantined;
    private errors;
    private ticks;
    private lastTickAt;
    private lastDrainAt;
    private lastError;
    constructor(opts: InboxDrainerOptions);
    start(): void;
    stop(): void;
    /**
     * One drain pass: list → (read → ingest → delete) per blob, page by page.
     * Per-blob failures are isolated — one bad object never aborts the pass.
     */
    drainOnce(): Promise<{
        drained: number;
        duplicates: number;
        quarantined: number;
        errors: number;
    }>;
    /** Returns true when the blob was REMOVED from the inbox (drained / duplicate / quarantined). */
    private drainBlob;
    status(): InboxDrainerStatus;
}
/** Validate the minimal shape the receiver wrote. Null = quarantine. */
export declare function parseInboxItem(content: string): FeedbackItem | null;
//# sourceMappingURL=InboxDrainer.d.ts.map