import type { Session, SessionCreateOptions, SessionForkOptions, SessionMetadata, SessionPlacementRecord, SessionRepo, SessionTreeEntry } from "../../internal/harness.js";
import type { PlacedSessionRow } from "../../core/session.js";
import { type CorruptReadInfo } from "./corrupt-read-sink.js";
export interface FileSessionRepoOptions {
    /**
     * Disclosure sink for a durable read this repo treats as ABSENT (ruled 2026-08-03). Same name and
     * same `{path, reason}` shape as the sibling file stores' seat — "unreadable read as missing" and
     * "corrupt read as missing" are one failure grammar, so they get one seat name.
     *
     * Fires for: an unreadable sessions DIRECTORY (the listing degrades to "no sessions"), a session log
     * that could not be replayed during a listing (skipped from the result), and each corrupt interior JSONL
     * record dropped on replay plus that replay's tally (turns missing from an otherwise healthy session).
     *
     * Never fires on plain ENOENT / `not_found` — that is honest absence, and the torn TAIL stays silent
     * too (dying mid-write is the append log's documented crash-recovery shape, not corruption).
     * Swallow-guarded: a throwing sink cannot re-introduce the fault the fail-open avoids.
     *
     * `sessionId` rides every disclosure about ONE session — its log's skipped records and tally, and its log
     * skipped from a listing — naming the session the repo was reading (on a listing, the id its file name
     * spells); a disclosure about the sessions directory itself carries none.
     */
    onCorruptRead?: (info: CorruptReadInfo & {
        sessionId?: string;
    }) => void;
}
export declare class FileSessionRepo implements SessionRepo {
    private readonly dir;
    private readonly tmpDir;
    private readonly onCorruptRead;
    /** RB-437 — the shared authorities THIS repo has joined, weakly (canonical key → the joined storage).
     *  `dispose()` walks it and releases each holder share; the weak value is what makes the identity
     *  check on the way out possible without pinning a storage the table itself only holds weakly. */
    private readonly joined;
    constructor(root: string, opts?: FileSessionRepoOptions);
    /** The one delivery point for {@link FileSessionRepoOptions.onCorruptRead}'s whole-file level; `sessionId` when the file is one session's log. */
    private disclose;
    private pathFor;
    /**
     * Read + torn-tail-recover a session file into (meta, entries). Three answers: no file → `not_found`; a
     * file with no meta header → `corrupt_session`, even when every other record is intact (every write path
     * here lays the header down first and the header alone carries `createdAt` / fork / placement, so the file
     * is not a session this store wrote, or its header line is damaged); otherwise the session, each skipped
     * record disclosed.
     */
    private read;
    private storage;
    /**
     * RB-437 — release every shared authority this repo joined (LAST holder ⇒ evict + close the append fd).
     *
     * This is the lifecycle-tied half of the eviction rule the four file-REPLACING operations already
     * cover: after it, a NEW repo/backend over the same root replays each session from disk instead of
     * serving this process's pre-dispose in-memory leaf, and a Session handle held across the teardown
     * is refused with `code=log_closed` rather than writing onto a data root the process has released.
     *
     * Idempotent; safe to call on a repo that never opened anything. A repo whose authority was replaced
     * meanwhile (delete/fork/import/create-over-absent) releases NOTHING for that key — the identity check
     * means a stale holder can never revoke the rebuilt entry.
     */
    dispose(): Promise<void>;
    create(options?: SessionCreateOptions): Promise<Session>;
    open(metadata: SessionMetadata): Promise<Session>;
    list(): Promise<SessionMetadata[]>;
    /**
     * Subagent transcript persistence — the PLACED partition's own enumeration (the joint reap's
     * partition-leg input; `TtlSessionStore` re-exposes it verbatim). Age (`olderThanMs`) is judged on
     * the session FILE's mtime — the transcript's last append — falling back to `placedAt` when the
     * stat fails (mtime is also the CC orphan-adoption window's basis, so the two age reads agree).
     * Rows missing either `(scope, handle)` join half return the honest `tupleIncomplete` variant
     * (consumers fail closed on it — never age-reaped, see the SessionStore contract).
     */
    listPlaced(kind: "subagent", opts?: {
        olderThanMs?: number;
        scope?: string;
    }): Promise<PlacedSessionRow[]>;
    /** The row as it stands before a file-replacing write: `undefined` ⇔ there is no row (no file); a row
     *  whose header cannot be read answers an empty header (it exists, its cells are unknown). */
    private priorHeader;
    /** Cheap placement probe for one id (the TtlSessionStore's cold-cache release consults it so the
     *  placed real-deletion obligation holds after a restart). `undefined` = missing file OR unplaced. */
    placementOf(sessionId: string): Promise<SessionPlacementRecord | undefined>;
    delete(metadata: SessionMetadata): Promise<void>;
    fork(sourceMetadata: SessionMetadata, options?: SessionForkOptions): Promise<Session>;
    /** 2c session-sync (the FILE half): the FULL oldest-first log for export. The file backend
     *  has NO F3 floor — `read` returns EVERY persisted entry (`id`/`parentId` verbatim) — so this is the complete
     *  log, BYPASSing any bounded tail (matches `InMemorySessionRepo.exportEntries`). Missing file → not_found. */
    exportEntries(sessionId: string): Promise<SessionTreeEntry[]>;
    /** 2c session-sync: import a verbatim log into `sessionId`. The single fail-closed invariant gate runs
     *  FIRST ({@link validateEntriesForImport}); `owner` is a durable multi-tenant concept the local file backend
     *  has no field for (accepted, unused — same as `InMemorySessionRepo`). Writes the WHOLE file (meta + entries)
     *  atomically via tmp→fsync→rename — the rename is the commit point, so a crash leaves either nothing or an
     *  orphan temp, never a partially-imported session (mirrors `fork`). An existing session is REPLACED (the
     *  orchestrator's importSession owns the already-exists policy, same as InMemory's overwrite). */
    importEntries(sessionId: string, owner: string | undefined, entries: SessionTreeEntry[], options?: {
        preserveActorAssertions?: boolean;
    }): Promise<void>;
}
