import type { CaptureTurn } from './CorrectionCaptureLoop.js';
/** The pre-scrubbed capture payload persisted for a later distill attempt. */
export interface BacklogCaptureInput {
    topicId: number;
    /** PRE-SCRUBBED turns (oldest-first) — exactly the window buildDistillPrompt
     *  would scrub. Re-scrubbed defensively on enqueue. */
    turns: CaptureTurn[];
    /** Layer-0 code-determined provenance weight of the signal message. */
    deterministicWeight: number;
    sessionId?: string | null;
    capturedAt?: number;
}
/** A row claimed for a drain attempt. */
export interface BacklogEntry {
    id: string;
    topicId: number;
    turns: CaptureTurn[];
    deterministicWeight: number;
    sessionId: string | null;
    capturedAt: number;
    attempts: number;
    lastAttemptAt: number | null;
}
export interface CorrectionCaptureBacklogOptions {
    dbPath: string;
    /** Hard cap on stored entries; oldest evicted on overflow. Default 200. 0 disables (caller should not construct). */
    maxEntries?: number;
    /** Drop an entry once attempts EXCEEDS this (i.e. after maxRetries failed drains). Default 3. */
    maxRetries?: number;
    /** Min gap (ms) before a just-attempted entry can be re-claimed. Default 60_000. */
    minRetryGapMs?: number;
    onError?: (where: string, err: unknown) => void;
    now?: () => number;
}
/**
 * Bounded durable backlog of pre-scrubbed captures awaiting an LLM headroom
 * window. Every method is fail-open — a storage fault returns a safe default and
 * never throws into the capture seam.
 */
export declare class CorrectionCaptureBacklog {
    private db;
    private readonly maxEntries;
    private readonly maxRetries;
    private readonly minRetryGapMs;
    private readonly onError;
    private readonly nowFn;
    constructor(opts: CorrectionCaptureBacklogOptions);
    /**
     * Re-scrub turns defensively (belt-and-suspenders over the §3.3 pre-scrub) and
     * return ONLY {fromUser, text} — never the raw at-timestamp ring metadata.
     */
    private sanitizeTurns;
    /** A stable hash over (topicId + scrubbed turn text) for near-identical dedupe. */
    private dedupeHashOf;
    /**
     * Persist a pre-scrubbed capture for a later distill. Dedupes against a
     * near-identical existing entry (same topic + same scrubbed turns) — a repeat
     * just bumps the existing row's captured_at forward rather than duplicating.
     * Evicts the oldest entry when at the max-entries cap. Fail-open: returns the
     * stored entry id, or null on any error (caller falls back to the old drop).
     */
    enqueue(input: BacklogCaptureInput): string | null;
    /**
     * Claim up to `n` oldest entries eligible for a drain attempt: not exhausted
     * (attempts <= maxRetries) and not attempted within minRetryGapMs. Read-only —
     * does NOT consume the row; the drainer markDistilled()s on success or
     * bumpAttempt()s on failure. Fail-open: returns [] on error.
     */
    claimBatch(n: number): BacklogEntry[];
    /** Delete an entry once it has been distilled into the ledger. Fail-open. */
    markDistilled(id: string): void;
    /**
     * Record a failed drain attempt. Increments attempts + stamps last_attempt_at;
     * DROPS the entry once attempts EXCEEDS maxRetries (give-up — bounded, no
     * infinite retry). Returns true if the entry was dropped. Fail-open.
     */
    bumpAttempt(id: string): boolean;
    /** Discard entries older than ttlMs (prune-in-transaction). Returns rows removed. Fail-open. */
    pruneExpired(ttlMs: number): number;
    /** Current backlog depth (observability). Fail-open → 0. */
    count(): number;
    close(): void;
    private rowToEntry;
}
//# sourceMappingURL=CorrectionCaptureBacklog.d.ts.map