import type { FileCacheMixin } from '../../cache/file/mixin.ts';
import { type OpRecord } from '../../observe/record.ts';
import { DriftPolicy } from '../../types.ts';
import type { MountEntry } from '../mount/mount.ts';
import type { FingerprintEntrySnapshot } from './types.ts';
/**
 * Raised at load time when a remote VFS's live fingerprint differs
 * from what was recorded in the snapshot.
 *
 * Indicates the underlying source has been modified since the snapshot
 * was taken, so reading current bytes would silently diverge from what
 * the original agent saw. Surface to the caller rather than mask.
 */
export declare class ContentDriftError extends Error {
    readonly path: string;
    readonly snapshotFingerprint: string;
    readonly liveFingerprint: string | null;
    constructor(path: string, snapshotFingerprint: string, liveFingerprint: string | null);
}
interface RegistryLike {
    tryMountFor(path: string): MountEntry | null;
    allMounts(): readonly MountEntry[];
}
export interface MountLookup {
    tryMountFor(path: string): MountEntry | null;
}
/**
 * Fingerprint checks a load queued, drained on the first async op.
 *
 * `Workspace.load` records one entry per read whose snapshot manifest
 * carried a fingerprint but no stable revision (a pinned read needs no
 * check: the pin guarantees the bytes). The first `dispatch` or
 * `shell` drains them, so downstream code can rely on consistent
 * state. Mirrors the Python `DriftQueue` in `snapshot/drift.py`.
 */
export declare class DriftQueue {
    private entries;
    private isPending;
    get pending(): boolean;
    /** Paths still queued for a check (audit surface). */
    get paths(): string[];
    /** Drop any queued state (a re-install starts fresh). */
    clear(): void;
    queue(path: string, fingerprint: string, mountId?: string | null): void;
    /**
     * Stat every queued path in parallel; throw on the first drift.
     * Subsequent calls are no-ops. Stats run concurrently so first-op
     * latency does not scale linearly with the number of recorded reads.
     */
    drain(registry: MountLookup, statFn: (path: string) => Promise<unknown>): Promise<void>;
}
/**
 * Walk a loaded snapshot's fingerprint manifest. For entries with a
 * revision, install the pin on the owning mount so replay reads pin to
 * that revision. For fingerprint-only entries, queue the path on the
 * drift queue. OFF skips the checks and evicts the snapshot cache for
 * fingerprinted paths so reads serve current state.
 *
 * Idempotent: clears queued state before installing. Called from
 * `Workspace.fromState`.
 */
export declare function installFingerprints(registry: RegistryLike, cache: Pick<FileCacheMixin, 'remove'>, drift: DriftQueue, state: {
    fingerprints?: FingerprintEntrySnapshot[];
    live_only_mounts?: string[];
}, policy: DriftPolicy): Promise<void>;
/**
 * Walk recorded ops and emit one pin per path still worth checking.
 *
 * A single forward pass over the time-ordered records, so the last word on
 * a path wins. Three things can be that last word:
 *
 * - an op that removed or replaced the object (`RETRACT_FINGERPRINT_OPS`)
 *   drops the pin, and every pin beneath it — `rm -r` and a prefix rename
 *   take a subtree with them;
 * - an op that changed the bytes without describing them — a write whose
 *   backend returned no token, or any `append` — drops the pin too,
 *   because the token on file no longer names what is there;
 * - an op carrying a token (`STAMP_FINGERPRINT_OPS`) replaces the pin
 *   whole, never field-merging, so a read's revision cannot survive onto a
 *   later write's fingerprint and pin replay to pre-write bytes.
 *
 * A read that reported no token changes nothing and leaves the pin alone.
 * Each token is what the backend returned at the moment the agent moved
 * the bytes, not a fresh stat at snapshot time.
 *
 * Paths on a mount that opts out of snapshot replay are never pinned; a
 * retraction still applies to them, because dropping a pin is the safe
 * direction and the mount a retraction names may no longer be the one that
 * set the pin.
 */
export declare function captureFingerprints(records: readonly OpRecord[], registry: RegistryLike): FingerprintEntrySnapshot[];
/**
 * Return mount prefixes whose VFS opts out of snapshot replay.
 *
 * These mounts will serve current state at load time with no drift
 * detection. Surfaced in the snapshot manifest so the load layer can
 * log them and so users can audit which paths are non-replayable.
 */
export declare function liveOnlyMountPrefixes(registry: RegistryLike): string[];
/**
 * Stat `path` and throw {@link ContentDriftError} if the live fingerprint
 * does not match `recorded`. No-op if the mount cannot be resolved or the
 * VFS cannot fingerprint.
 *
 * The caller provides `statFn` (typically a thin wrapper over
 * {@link Workspace.dispatch}) so that drift.ts stays decoupled from the
 * workspace's op-resolution machinery.
 */
export declare function checkDrift(registry: MountLookup, statFn: (path: string) => Promise<unknown>, path: string, recorded: string, mountId?: string | null): Promise<void>;
export {};
//# sourceMappingURL=drift.d.ts.map