/**
 * Per-agent adapter for sync. Each supported agent declares where its
 * transcripts live and how to derive a session id and storage-relative key
 * from a file path. The merge (crdt.ts) and transport (r2.ts) are fully
 * agent-agnostic — adding a new agent is just another entry in SYNC_AGENTS.
 *
 * Mirror layout: synced-in transcripts land under
 *   ~/.agents/.history/backups/<agent>/<machine>/<subdir>/<relKey>
 * which is already a scan root (getAgentSessionDirs scans backups/<agent>/<ts>),
 * so the existing incremental scanner indexes them with no changes. Because the
 * scanner dedups by session id with the live home scanned first, a session that
 * also exists locally always wins — the mirror only ever fills in sessions
 * originated on other machines.
 */
/** One constituent file of a session — a session has exactly one for file-shaped
 *  agents (Claude, Codex, …), and many for directory-shaped ones (Kimi). */
export interface SessionFile {
    /** Absolute path on this machine. */
    absPath: string;
    /** Path relative to the agent's subdir root — preserved in the mirror layout. */
    relKey: string;
}
export interface LocalTranscript {
    /** Globally-unique session id (the grouping key across machines). */
    sessionId: string;
    /** Every file that makes up this session. Length 1 for file-shaped agents. */
    files: SessionFile[];
}
export interface SyncAgentSpec {
    id: string;
    /** Config subdir under the agent home that holds transcripts. */
    subdir: string;
    /** File extension to walk for this agent (defaults to .jsonl). */
    ext?: string;
    /**
     * A session is a DIRECTORY of files (e.g. Kimi: state.json + agents/…/wire.jsonl
     * + per-tool task sidecars), not a single transcript. When set, every file under
     * the session dir (matching `exts`, passing `fileFilter`) syncs, is stored under
     * its own R2 sub-key, and is mirrored at its own relative path — instead of the
     * file-shaped "one transcript per session" model.
     */
    dirShaped?: boolean;
    /** Extensions a dir-shaped agent walks (defaults to `[ext ?? '.jsonl']`). */
    exts?: string[];
    /** Optional per-file exclusion for dir-shaped agents (lock/scratch files, …). */
    fileFilter?(relKey: string): boolean;
    /**
     * Extensions whose files are append-only event logs and therefore CRDT-mergeable
     * (G-Set union across forked copies). A dir-shaped session usually mixes an
     * append-only conversation log (`wire.jsonl`) with mutable metadata blobs
     * (`state.json`) — line-unioning the latter would corrupt it, so any file NOT
     * matching an entry here is reconciled last-writer-wins instead. Undefined (the
     * file-shaped default) means every file is mergeable — the single `.jsonl`
     * transcript keeps its existing union behaviour.
     */
    mergeableExts?: string[];
    /** Derive the session id from a storage-relative key. */
    sessionIdFromRelKey(relKey: string): string;
}
/** True when a file at `relKey` is an append-only log that CRDT-unions across
 *  forks; false when it must be reconciled last-writer-wins (mutable blob). */
export declare function isMergeableFile(spec: SyncAgentSpec, relKey: string): boolean;
export declare const SYNC_AGENTS: SyncAgentSpec[];
/**
 * List this machine's own transcript files for an agent, EXCLUDING the sync
 * mirror (we never re-upload another machine's files under our prefix). Dedups
 * by session id so a session present in multiple version homes is uploaded once.
 */
export declare function listLocalTranscripts(spec: SyncAgentSpec): LocalTranscript[];
/** Session ids this machine holds locally (live home), used to skip mirror writes. */
export declare function localSessionIds(spec: SyncAgentSpec): Set<string>;
/**
 * Absolute mirror path for a remote machine's transcript — lands in a scan root.
 *
 * `machine` and `relKey` come from a peer's manifest (untrusted: any peer with
 * write access to the shared bucket controls them). Unlike the push side, which
 * already drops `relKey` starting with `..` when building the manifest, the pull
 * side would otherwise `fs.writeFileSync` at this path with peer-controlled
 * content. `machine` is constrained to a single segment and `relKey` (which may
 * legitimately nest, e.g. `projects/x/y.jsonl`) is contained beneath the
 * per-machine mirror root, so a crafted `relKey` like `../../../.ssh/authorized_keys`
 * cannot write outside `~/.agents/.history/backups/<agent>/<machine>/<subdir>`.
 */
export declare function mirrorPath(spec: SyncAgentSpec, machine: string, relKey: string): string;
/**
 * R2 object key for a transcript.
 *  - file-shaped (relKey omitted): sessions/<machine>/<agent>/<sessionId>.jsonl —
 *    unchanged, so existing claude/codex/droid objects keep their keys.
 *  - dir-shaped (relKey given): sessions/<machine>/<agent>/<sessionId>/<relKey> —
 *    one object per constituent file of the session directory.
 */
export declare function objectKey(machine: string, agentId: string, sessionId: string, relKey?: string): string;
/** R2 object key for a machine's manifest. */
export declare function manifestKey(machine: string): string;
/** Prefix under which all machine manifests live (for discovery). */
export declare const SESSIONS_PREFIX = "sessions/";
