/**
 * Portable session bundle — the on-the-wire format behind `agents sessions
 * export` / `import` (RUSH-1710 / RUSH-1711).
 *
 * A bundle is a self-describing NDJSON stream: the FIRST line is a
 * {@link BundleHeader}, every subsequent line is one {@link BundleRecord} (one
 * constituent file of a session). NDJSON — not tar — because the bundle has to
 * pipe cleanly over `agents ssh … export --stdout | … import -` (RUSH-1712)
 * without any external archiver on either box, stays inspectable with `head`,
 * and lets each file body carry its own encryption envelope.
 *
 * This module owns the FORMAT and the import PLACEMENT only; selecting which
 * sessions to export (which needs the session DB) lives in the export command.
 * Placement reuses the sync mirror model verbatim: a foreign machine's session
 * lands at {@link mirrorPath}(spec, originMachine, relKey), exactly where the
 * cross-machine sync writes it — so the existing scanner indexes it as a
 * machine-tagged row and "local always wins" falls out of the scanner's
 * live-home-first dedup with no extra logic here.
 */
import { type SyncAgentSpec } from './sync/agents.js';
export declare const BUNDLE_KIND = "agents-session-bundle";
export declare const BUNDLE_VERSION = 1;
/** First line of a bundle: what it is and how the bodies are encoded. */
export interface BundleHeader {
    kind: typeof BUNDLE_KIND;
    version: number;
    /** ISO timestamp the bundle was produced. */
    exportedAt: string;
    /** Machine that produced the bundle (informational; per-record `machine` is authoritative for placement). */
    origin: string;
    /** True when record bodies are AES-256-GCM envelopes (see transcript-crypto). */
    encrypted: boolean;
    /** True when bodies were secret-scrubbed before hashing/sealing. */
    redacted: boolean;
    /** Number of file records that follow. */
    count: number;
    /** Distinct session count across those records. */
    sessions: number;
}
/** One constituent file of one session. Dir-shaped sessions emit several, sharing `sessionId`. */
export interface BundleRecord {
    /** SYNC_AGENTS id (claude, codex, kimi, …). */
    agent: string;
    /** ORIGIN machine of this session — where placement mirrors it to. */
    machine: string;
    sessionId: string;
    /** Storage-relative key within the agent's subdir (preserved on import). */
    relKey: string;
    /** Byte length of the plaintext body. */
    size: number;
    /** SHA-256 of the plaintext body — identity + byte-exact dedup. */
    hash: string;
    /** Human label carried from SessionMeta, if any. */
    label?: string;
    /** True when `body` is an encryption envelope rather than plaintext. */
    encrypted: boolean;
    /** File content: plaintext, or a transcript-crypto envelope when `encrypted`. */
    body: string;
}
export interface ParsedBundle {
    header: BundleHeader;
    records: BundleRecord[];
}
/** A single file selected for export, resolved to an absolute on-disk path. */
export interface FileToExport {
    agent: string;
    /** Origin machine of the session (self for live-home, the peer for a mirror). */
    machine: string;
    sessionId: string;
    relKey: string;
    absPath: string;
    label?: string;
}
export interface BuildRecordOpts {
    /** Scrub secrets from the body before hashing/sealing (default-on at the command layer). */
    redact: boolean;
    /** Non-null → seal each body with this key; null → plaintext bodies. */
    encryptKey: Buffer | null;
    /**
     * Literal secret values to mask verbatim during redaction (value-aware pass):
     * e.g. live credentials from an injected secrets bundle, so they never leak in
     * an exported transcript regardless of format. Only used when `redact` is set.
     */
    knownSecrets?: readonly string[];
}
/** Look up the sync spec for an agent id (undefined → agent not sync-representable). */
export declare function specForAgent(agentId: string): SyncAgentSpec | undefined;
/** True when an agent's sessions can be represented in a bundle (has a sync spec). */
export declare function isExportableAgent(agentId: string): boolean;
/**
 * Read one file and turn it into a bundle record. The hash and size are always
 * computed over the PLAINTEXT (post-redaction) body, so they equal what lands on
 * disk after import — keeping dedup byte-exact whether or not the bundle is
 * encrypted.
 */
export declare function buildRecord(file: FileToExport, opts: BuildRecordOpts): BundleRecord;
/** Build the header for a set of records. */
export declare function makeHeader(args: {
    origin: string;
    exportedAt: string;
    encrypted: boolean;
    redacted: boolean;
    records: BundleRecord[];
}): BundleHeader;
/**
 * Merge record sets from several bundles (e.g. a fan-out pull across hosts),
 * deduping by agent + origin machine + session + file so the same session seen
 * from two peers lands once. First occurrence wins.
 */
export declare function mergeRecords(sets: BundleRecord[][]): BundleRecord[];
/** Serialize a bundle to its NDJSON wire form (header line + one line per record). */
export declare function serializeBundle(header: BundleHeader, records: BundleRecord[]): string;
/**
 * Write a serialized bundle to disk owner-only (0600). A bundle carries raw
 * transcript bodies — even redacted, never world/group-readable — and encryption
 * is opt-in, so the file mode is the baseline confidentiality guard. `mode` on
 * `writeFileSync` only applies when the file is created, so we `chmod` too to
 * clamp an existing (possibly looser) file on overwrite.
 */
export declare function writeBundleFile(outPath: string, wire: string): void;
/** Parse an NDJSON bundle, validating the header kind + version. Throws on malformed input. */
export declare function parseBundle(text: string): ParsedBundle;
/** Placement outcome for one record, computed against what is already on disk. */
export type ImportStatus = 'new' | 'dup' | 'conflict' | 'unknown';
export interface ImportPlanItem {
    record: BundleRecord;
    /** Absolute path the file lands at (empty for `unknown` agents). */
    targetPath: string;
    status: ImportStatus;
}
export interface PlanImportOpts {
    /** Key to open encrypted record bodies (null → only plaintext bodies readable). */
    decryptKey: Buffer | null;
}
/**
 * Compute where each record lands and whether it duplicates / conflicts with an
 * existing file. Pure w.r.t. the filesystem it reads (no writes). Dedup is
 * byte-exact: a target that already holds an identical body is `dup`; a target
 * that holds a DIFFERENT body is `conflict` (only overwritten with --overwrite).
 * An agent with no sync spec is `unknown` and never placed.
 */
export declare function planImport(bundle: ParsedBundle, opts: PlanImportOpts): ImportPlanItem[];
export interface WriteResult {
    /** New files written. */
    placed: number;
    /** Byte-exact dups skipped. */
    skipped: number;
    /** Conflicts replaced (only with overwrite). */
    overwritten: number;
    /** Conflicts left in place (overwrite off). */
    conflicts: number;
    /** Records for agents with no sync spec. */
    unknown: number;
}
export interface WriteImportOpts {
    overwrite: boolean;
    decryptKey: Buffer | null;
}
/**
 * Materialize a plan to disk. `dup` records are always skipped (local wins);
 * `conflict` records are replaced only when `overwrite` is set; `unknown` records
 * are counted and skipped.
 */
export declare function writeImport(plan: ImportPlanItem[], opts: WriteImportOpts): WriteResult;
