import { type BackgroundAgentReapOptions, type BackgroundAgentRecord, type BackgroundAgentRowSummary, type BackgroundAgentStore, type BackgroundAgentUpdateIfExpect } from "../../core/background-agent-store.js";
import { type CorruptReadInfo } from "./corrupt-read-sink.js";
export interface FileBackgroundAgentStoreOptions {
    /** This store's half of {@link import("./shared-ledger.js").LedgerPaths.onCorruptRead} — the same seat,
     *  over the agent row model. */
    onCorruptRead?: (info: CorruptReadInfo) => void;
    /** When false, a ledger append is NOT fsync'd. Default true. */
    fsync?: boolean;
    /** Compact the ledger into a snapshot once it exceeds this many events (then truncate). Default 1000. */
    compactEvery?: number;
}
export declare class FileBackgroundAgentStore implements BackgroundAgentStore {
    private readonly fsyncEnabled;
    private readonly compactEvery;
    /** RB-55: all mutable state is SHARED per canonical directory (see the note above the class). */
    private readonly ledger;
    /** RB-145: this instance's own close latch — the refcount belongs to the DIRECTORY, not to callers. */
    private closed;
    private get rows();
    constructor(root: string, opts?: FileBackgroundAgentStoreOptions);
    /**
     * RB-60 (2026-07-25, red probe): the composite key must be INJECTIVE — `get`/`update` no longer re-check
     * scope, so tenant isolation rests entirely on this function. A SPACE separator is not injective
     * (`scope="a", handle="b c"` and `scope="a b", handle="c"` produce the same key), which let one tenant's
     * row collide with another's. The in-memory reference implementation (`core/background-agent-store.ts`)
     * uses `\u0000` for exactly this reason ("the composite key keeps the info-hiding property structural") —
     * a NUL can never occur in a scope or handle, so the mapping is injective. Matching it also restores this
     * backend's stated "byte-for-byte the InMemory reference implementation's" claim.
     *
     * MIGRATION: the key is IN-MEMORY only (the ledger stores whole records with their own `scope`/`handle`
     * fields, and replay rebuilds keys through this function), so changing the separator needs no on-disk
     * migration — an existing directory replays into the new key space on next open.
     */
    static key(handle: string, scope: string): string;
    private withLock;
    /** Durable append FIRST, in-memory flip AFTER, then best-effort compaction — all in the base
     *  (RB-150/RB-167: a failing housekeeping compaction must never be reported as this CAS failing). */
    private commit;
    put(record: BackgroundAgentRecord): Promise<void>;
    get(handle: string, scope: string): Promise<BackgroundAgentRecord | null>;
    update(handle: string, scope: string, record: BackgroundAgentRecord, expect: {
        rev: number;
    }): Promise<boolean>;
    updateIf(handle: string, scope: string, record: BackgroundAgentRecord, expect: BackgroundAgentUpdateIfExpect): Promise<boolean>;
    listBySession(scope: string, sessionId: string, opts?: {
        status?: BackgroundAgentRecord["status"];
        limit?: number;
    }): Promise<BackgroundAgentRowSummary[]>;
    listByScope(scope: string, opts?: {
        status?: BackgroundAgentRecord["status"];
        limit?: number;
    }): Promise<BackgroundAgentRowSummary[]>;
    listScopes(): Promise<string[]>;
    delete(handle: string, scope: string, expect?: {
        rev: number;
    }): Promise<boolean>;
    reap(scope: string, now: number, opts?: BackgroundAgentReapOptions): Promise<number>;
    /** Force a compaction now (test/inspection). RB-167's closeForSwap recipe lives in the base. */
    compactNow(): void;
    /** Test/inspection helper: number of stored rows. */
    get size(): number;
    /** Release the append handle (best-effort). RB-55: refcounted — the directory's shared authority
     *  (rows/locks/log) is torn down only when the LAST instance over it closes, so a sibling instance
     *  is never left holding a closed fd or a half-dropped view. */
    close(): void;
}
