import type { MemoryEntry, ScoredMemoryEntry } from "./types.js";
/**
 * design/142 §3 — the per-replica per-scope sync cursor: the COMMON BASELINE a reconciliation run
 * judges divergence against (`id → rev` as of the last completed sync round). This is a NEW
 * persistence face — deliberately NOT revs.json (that is the local disk-vs-committed ledger): File
 * plane = a control-plane sidecar, DB plane = a service-owned table (PG+TiDB dual-dialect).
 * Serializable by construction.
 */
export interface MemorySyncCursor {
    scope: string;
    /** The PEER this cursor tracks (e.g. "central", a repo remote name) — one cursor per peer. */
    peer: string;
    /** id → rev at the last completed sync round with this peer. */
    baseRevs: Record<string, string>;
    /** Caller-injected clock (never Date.now inside the engine — clock-injection precedent). */
    updatedAtMs: number;
}
export interface MemorySyncConflict {
    id: string;
    /** Present when the side still has the entry (absent = deleted on that side). */
    local?: MemoryEntry;
    remote?: MemoryEntry;
    baseRev?: string;
}
export interface MemorySyncPlan {
    /** Entries the LOCAL side should send to the peer (new here or changed-here-only). 独立轨 F8 —
     *  CAS discipline for the peer write: a push item exists ONLY when the peer is still at the
     *  baseline, so the peer-side conditional write uses `baseRevs[id]` as its baseRev (absent from
     *  the baseline ⇒ the peer has no such entry ⇒ plain add). Never write the peer unconditionally. */
    push: MemoryEntry[];
    /** Entries the LOCAL side should adopt from the peer (new there or changed-there-only). Same CAS
     *  discipline as {@link push}, mirrored: local conditional write with `baseRevs[id]`. */
    pull: MemoryEntry[];
    /** DELETE propagation (without these the removal never crosses and every round
     *  re-plans the same state = livelock): the peer removed an entry the local side left UNCHANGED
     *  since baseline ⇒ delete locally (CAS on baseRev — a local edit racing in makes the delete a
     *  conflict on the next round instead of silent loss). */
    deleteLocal: Array<{
        id: string;
        baseRev: string;
    }>;
    /** Symmetric: locally removed, peer unchanged since baseline ⇒ delete on the peer. */
    deleteRemote: Array<{
        id: string;
        baseRev: string;
    }>;
    /** Both sides diverged from the baseline (or delete-vs-edit races): the caller resolves via the
     *  §3 ladder — winner by the caller's policy, LOSER MINTED AS A SIBLING entry (never dropped),
     *  the pair recorded so a three-way exchange cannot rebuild the same conflict forever. */
    conflicts: MemorySyncConflict[];
    /** Ids deleted on BOTH sides since the baseline (pure cursor cleanup, no data movement). */
    cleared: string[];
}
/**
 * Pure three-way set reconciliation over one scope. `baseRevs` = the persisted common baseline
 * (empty object = first sync round: everything present is "new" on its side; identical revs on
 * both sides collapse to no-op).
 *
 * Judgment per id (rev lineage, mtime never consulted):
 *  - both sides at baseline rev → no-op;  identical revs (any lineage) → no-op + baseline advance;
 *  - changed on exactly ONE side → push/pull;
 *  - changed on BOTH sides, or delete-on-one × edit-on-the-other → conflict (caller's ladder);
 *  - deleted on one side × UNCHANGED on the other → the delete propagates (deleteLocal/deleteRemote,
 *    CAS on baseRev: without this leg removals never cross and the plan livelocks);
 *  - present only on one side with NO baseline → new entry → propagate;
 *  - absent on both but present in baseline → cleared (cursor cleanup).
 */
export declare function reconcileMemoryEntries(baseRevs: Readonly<Record<string, string>>, local: readonly MemoryEntry[], remote: readonly MemoryEntry[]): MemorySyncPlan;
/** The baseline to persist AFTER a sync round completes: every id both sides now agree on. The
 *  caller passes the POST-round entry sets (after applying push/pull and resolving conflicts). */
export declare function nextSyncBaseline(local: readonly MemoryEntry[], remote: readonly MemoryEntry[]): Record<string, string>;
/**
 * A third-party semantic recall side-channel (Mem0/Zep/Cognee/… adapters — research 2026-07-12:
 * all five surveyed engines fit THIS seat; none fits the authority seat). STRICTLY READ-ONLY by
 * construction: a RecallSource feeds candidate hits into injection-time recall; it never sees the
 * write path, never participates in harvest, and the system runs identically without it.
 *
 * Score contract: DISTANCE in [0, 2] (0 = identical, 2 = orthogonal — the vector-rung convention);
 * adapters normalize their similarity/relevance scores into this scale. Returned entries may be
 * the third party's RE-STATEMENTS (extractive engines rewrite) — that is acceptable HERE precisely
 * because the seat is advisory: the authoritative body is re-read from the real backend by id/slug
 * when a hit is followed. Mount point: a deployment composes its RecallSource(s) inside its recall
 * select hook (the design/65 selective-recall trust seat) or pre-queries before prepare; core adds
 * no RunnerDeps seat until a real integration signals the need (YAGNI two-lane ruling).
 */
export interface RecallSource {
    /** Stable adapter name (diagnostics/attribution: hits are labeled, never silently mixed). */
    name: string;
    search(query: string, scopes: readonly string[], opts?: {
        limit?: number;
    }): Promise<ScoredMemoryEntry[]>;
}
/** Merge recall hits from multiple sources with the authority backend's own results: id-deduped
 *  (FIRST occurrence wins — callers order sources by trust, authority first), ascending by score
 *  (distance), capped at `limit`. Pure — no I/O, no source calls. */
export declare function mergeRecallHits(hitLists: ReadonlyArray<readonly ScoredMemoryEntry[]>, limit: number): ScoredMemoryEntry[];
