/**
 * WorkingSetPull — P2.2 of multi-machine coherence: both sides of the
 * chunked, bounded, verified `working-set-pull` mesh verb.
 *
 * Spec: docs/specs/WORKING-SET-HANDOFF-SPEC.md §3.2 (the verb), §3.5
 * (never-clobber). Transport-agnostic: the server registers the serve side
 * as the verb handler; the puller drives the receive side through an
 * injected `send` seam (MeshRpcClient upstream).
 *
 * Load-bearing transport rules (§3.2):
 *  - Every response carries at most `pullMaxBatchBytes` (1 MiB default) of
 *    content — a single 32 MiB JSON.stringify is the host's DOCUMENTED
 *    event-loop-starvation root cause.
 *  - Cross-chunk consistency anchor: the offset-0 response alone carries the
 *    whole-file sha256 (ONE full read+hash, amortized) + a cheap fstat
 *    anchor {bytes, mtimeNs, ino}; later chunks carry the fstat anchor only.
 *    Anchor mismatch → restart FROM 0, bounded by chunkRestartCap, then the
 *    file is `unstable` (surfaced, never a livelock).
 *  - `busy` is retry-without-penalty: it never consumes the pending-pull
 *    breaker's failure budget (§3.2/§3.4).
 *  - maxTotalBytes counts ASSEMBLED, verification-passed bytes — never raw
 *    wire bytes (anchor restarts can't starve the set's remaining files).
 *  - Serve side: the FRESH manifest is the allowlist (recomputed per
 *    request); O_NOFOLLOW open + fd identity verify (TOCTOU defense);
 *    at most `serveConcurrency` requests served at once.
 *  - Receive side: peer relPath is HOSTILE input; full jail before any
 *    join; never-clobber with hash-suffixed alongside copies (cap 2,
 *    eviction via SafeFsExecutor — the single, narrow deletion exception).
 */
import { type WorkingSetCaps, type WorkingSetManifestResult } from './WorkingSetManifest.js';
import type { OwnAutonomousRuns } from './CoherenceJournalReader.js';
export declare const DEFAULT_PULL_MAX_BATCH_BYTES: number;
export declare const DEFAULT_CHUNK_RESTART_CAP = 3;
export declare const DEFAULT_CHUNKS_PER_TICK = 8;
export declare const DEFAULT_SERVE_CONCURRENCY = 2;
export declare const DEFAULT_BUSY_RETRY_CAP = 10;
/** Cheap per-chunk tear detector (§3.2) — fstat, never a content re-hash. */
export interface GenerationAnchor {
    bytes: number;
    mtimeNs: string;
    ino: string;
}
export interface ServedBlob {
    relPath: string;
    offset: number;
    dataB64: string;
    anchor: GenerationAnchor;
    /** Offset-0 responses only: the whole file's sha256 (the assembly authority). */
    fileSha256?: string;
    /** True when this chunk reaches end-of-file. */
    eof: boolean;
    /** Served hash differs from the requester's manifest view (§3.2 — served wins). */
    changedSinceManifest?: boolean;
}
export type RefusalReason = 'refusedPolicy' | 'goneSinceManifest' | 'liveSource' | 'secretFlagged' | 'tooLarge';
export interface ServeResult {
    busy?: boolean;
    manifest?: WorkingSetManifestResult;
    blobs?: ServedBlob[];
    refused?: {
        relPath: string;
        reason: RefusalReason;
    }[];
}
export interface ServeDeps {
    stateDir: string;
    /** Own-stream journal evidence for the topic (reader, threaded by server). */
    readRuns: (topic: number) => OwnAutonomousRuns;
    caps?: Partial<WorkingSetCaps>;
    pullMaxBatchBytes?: number;
    serveConcurrency?: number;
    logger?: (msg: string) => void;
}
export interface WorkingSetPullCmd {
    type: 'working-set-pull';
    topic: number;
    manifestOnly?: boolean;
    want?: {
        relPath: string;
        offset: number;
    }[];
}
/**
 * The serve-side handler — stateless per request except the concurrency
 * gate. Registered by the server as the `working-set-pull` verb handler.
 */
export declare class WorkingSetPullServer {
    private inFlight;
    private readonly d;
    constructor(deps: ServeDeps);
    handle(cmd: WorkingSetPullCmd): ServeResult;
    private handleInner;
    /**
     * TOCTOU-safe single-chunk read (§3.2): open O_NOFOLLOW (refuses a symlink
     * swapped in at the final component), fstat the FD (regular file), and
     * verify FD identity against a fresh lstat of the same path (same dev+ino
     * → the fd IS the file at the jailed path, not a different object raced
     * in). Bytes are read once; the offset-0 read covers the whole file so the
     * assembly hash comes from the exact served bytes.
     */
    private serveChunk;
}
export interface PullerDeps {
    stateDir: string;
    /** Send one verb request to the nominee peer. Throws on transport failure. */
    send: (cmd: WorkingSetPullCmd) => Promise<ServeResult>;
    /** Short stable id of the SENDER machine (alongside naming, from env.sender). */
    senderShortId: string;
    /** Re-checked before EVERY write (§3.3): still the owner at this epoch? */
    stillCurrent: () => boolean;
    caps?: Partial<WorkingSetCaps>;
    pullMaxBatchBytes?: number;
    chunkRestartCap?: number;
    chunksPerTick?: number;
    busyRetryCap?: number;
    /** Event-loop yield between chunk groups (test seam; default setImmediate). */
    yieldFn?: () => Promise<void>;
    /** Inter-chunk delay under pressure (ms); 0 default, the scheduler stretches. */
    interChunkDelayMs?: number;
    logger?: (msg: string) => void;
}
export interface PullFileOutcome {
    relPath: string;
    outcome: 'written' | 'skippedExisting' | 'alongside' | 'unstable' | 'refused' | 'liveSourceDeferred' | 'superseded' | 'budgetExhausted' | 'busyExhausted' | 'verifyFailed';
    reason?: string;
    alongsidePath?: string;
}
export interface PullReport {
    topic: number;
    files: PullFileOutcome[];
    assembledBytes: number;
    /** True when ANY outcome warrants a pending-pull record (§3.4). */
    needsPendingPull: boolean;
}
export declare class WorkingSetPuller {
    private readonly d;
    constructor(deps: PullerDeps);
    /**
     * Pull one topic's transferables from the nominee behind `send`.
     * The caller (trigger/ledger layer) handles nomination, single-flight,
     * staggered drain, and pending-pull filing off `needsPendingPull`.
     */
    pullTopic(topic: number): Promise<PullReport>;
    private pullFile;
    private pullFileOnce;
    private writeNeverClobber;
    /**
     * Cap-2 alongside retention (§3.5) — the single, narrow deletion exception:
     * alongside files only, inside the jail, through SafeFsExecutor. The
     * evicted copy's content still exists on its producer machine (first-hop
     * provenance) — nothing is lost to the fleet, only to this replica.
     */
    private evictAlongsideOverCap;
    private sendWithBusyRetry;
    private yieldNow;
}
//# sourceMappingURL=WorkingSetPull.d.ts.map