import { type Embedder, type MemoryNoteHeader, type MemoryNoteRecord, type MemoryStore, type MemoryVectorMode, type ScoredMemory, type StructuredNoteInput } from "../../core/memory.js";
export declare class FileMemoryStore implements MemoryStore {
    private readonly root;
    /** Optional injected embedder (design/81 Slice 5). When present, note bodies are embedded (in the background,
     *  after the durable note write) into a rebuildable sidecar index and `searchScored` ranks by cosine distance
     *  (portable / in-process, mirrors PgMemoryStore's jsonb mode). Absent ⇒ lexical, byte-identical to before. */
    private readonly embedder?;
    /** ms budget for a background embed; a slower/stuck endpoint yields no vector (row stays lexical), never
     *  blocking the durable note write (which already completed). */
    private readonly embedTimeoutMs;
    /** In-flight background embeds (so tests can flush + `close` can drain). */
    private readonly pending;
    /**
     * RB-146 (2026-07-25, 按面收口): scope state is shared per PHYSICAL directory, like every other file
     * backend. This was the last store on this surface still caching replayed state on the INSTANCE: a second
     * `FileMemoryStore` over one directory replayed its own view on first touch and then served it forever,
     * so a note durably committed through one instance was invisible to the other — the same shape as RB-73
     * (session), RB-55 (agent), RB-59 (run) and RB-134 (checkpoint). Refcounted, and the last holder closes
     * the logs; the `closed` latch below already existed for the fd-recycle guard.
     */
    private get scopes();
    private readonly sharedScopes;
    private readonly scopeKey;
    /** Set by {@link close}: a background embed that resolves AFTER close must NOT writeSync to the (now closed)
     *  vlog fd — a stray write on a recycled fd could corrupt an unrelated file. */
    private closed;
    constructor(root: string, opts?: {
        embedder?: Embedder;
        embedTimeoutMs?: number;
    });
    /** design/81 Slice 5 rung: `portable` (in-process cosine over the sidecar) when an embedder is injected,
     *  else `lexical`. File never reaches `native` (no backend index). */
    get vectorMode(): MemoryVectorMode;
    /** True when an embedder was injected. Exposed so a consumer/test can confirm it was accepted. */
    get hasEmbedder(): boolean;
    /** Await all in-flight background embeds (tests / `close`). */
    flushEmbeds(): Promise<void>;
    /** Open (replay) a scope's notes + sidecar vectors once and cache it. */
    private scopeState;
    /**
     * RB-267 — record that `scope` holds live descriptors, and evict the coldest scopes past
     * {@link MAX_OPEN_MEMORY_SCOPES}. Eviction releases the handles ONLY (`closeForSwap`, the RB-167 lazy
     * reopen); the replayed `ScopeState` stays in the map, so nothing about the authority — the entry
     * list, the cursor, the vector cache — is rebuilt or lost, and a later write to an evicted scope
     * reopens its log transparently.
     */
    private touchOpenLogs;
    /** RB-267 — the notes log for a WRITE. Every durable append goes through here so the LRU sees the
     *  access: a write is also the only thing that can (re)open a descriptor (`AppendLog.append` reopens
     *  a swap-closed log lazily), so touching here is what keeps "has an open handle ⇒ tracked" true.
     *  Reads never open anything and deliberately do not touch. */
    private writeLog;
    /** RB-267 — the sidecar vector log, opened on FIRST WRITE. Only {@link queueEmbed} writes vectors, so
     *  a store with no embedder never opens this file at all (it used to take a descriptor per scope for
     *  a file it would never touch). Replay reads the path directly and needs no handle. */
    private vectorLog;
    /** Queue a background embed of `body` → sidecar vector for `id` (best-effort, timeout-bounded, non-blocking).
     *  Only when an embedder is injected; a failure/timeout leaves the row lexical (fail-open). The durable note
     *  write has ALREADY happened before this is called, so this never gates durability. */
    private queueEmbed;
    /** Rewrite the derived `MEMORY.md` projection atomically (CC markdown parity), STAGED IN THE TARGET'S
     *  OWN DIRECTORY: `rename(2)` is defined only within one filesystem, so staging beside the target makes
     *  the two share a mount by construction where a fixed scratch dir elsewhere made it deployment luck (a
     *  memory tree on its own device took `EXDEV` on every note). EVERY caller is POST-DURABLE, so a failure
     *  here cannot mean the note was lost: it is {@link MemoryProjectionError}, that phase's one name. */
    private writeProjection;
    read(scope: string): string | null;
    append(scope: string, note: string): string | void;
    appendStructured(scope: string, note: StructuredNoteInput): string;
    /** Append + fsync the note event BEFORE updating the in-memory list, then refresh the projection. */
    private commit;
    clear(scope: string): void;
    /** design/84 Seam B: read the cached periodic-consolidation cursor (replayed from the last `op:cursor`). */
    getConsolidationCursor(scope: string): string | undefined;
    /** design/84 Seam B: persist the periodic-consolidation cursor via a durable `op:cursor` append-log event,
     *  then update the cached state (durable FIRST, like `commit`). */
    setConsolidationCursor(scope: string, cursor: string): void;
    /**
     * Keyword search backing the `recall` tool. design/81 Slice 2b — uses the SHARED synonym/stem-aware
     * {@link import("../../core/memory.js").lexicalSearchMatch} (the SAME matcher as
     * {@link import("../../core/memory.js").InMemoryMemoryStore.search}, ZERO model/native/embedder dep) so
     * "auth token" matches a note saying "credentials" / "bearer", and the `recall` tool returns IDENTICAL
     * results across the File and InMemory backends. A bullet matches a query term iff it contains ANY of that
     * term's expansions, and must match EVERY query term (AND across terms, OR within a term's synonyms). With
     * no synonym/stem hit this is byte-identical to the plain substring filter.
     */
    search(scope: string, query: string, limit?: number): string[];
    /** Manifest (design/65 §2.2): a header per note, body excluded — in-memory parity. */
    listStructuredNotes(scope: string): MemoryNoteHeader[];
    getByIds(scope: string, ids: string[]): MemoryNoteRecord[];
    /**
     * Id-addressable similarity retrieval (design/81 Slice 2 — the consolidation candidate source). The
     * `score` is a cosine **DISTANCE** ∈ [0,2], **0 = identical** ({@link ScoredMemory} contract): it is the
     * lexical `1 - Jaccard(terms)` ∈ [0,1] ⊂ [0,2] stand-in (byte-identical to
     * {@link InMemoryMemoryStore.searchScored} / the lexical {@link import("../pg.js").PgMemoryStore} fallback),
     * so the `{0.05, 0.3}` consolidation band needs no retune. An injected embedder is ACCEPTED but vector
     * ranking is the DEFERRED Slice 5 — so this method ALWAYS uses the lexical distance for now (an injected
     * embedder is harmless, NOT a throw). Excludes the query's own id (a note must not self-match — see below)
     * and zero-overlap entries; returns the `limit` nearest, ascending by distance.
     *
     * **Self-id exclusion:** byte-identical to {@link InMemoryMemoryStore.searchScored} / the lexical
     * {@link import("../pg.js").PgMemoryStore} fallback, the SOLE consumer (consolidation) excludes the
     * query note's own id (and its sibling this-task ids) by filtering the returned `ScoredMemory[]` on its
     * `thisTaskIds` set — keeping the store's ranking pure and the cross-backend contract identical.
     */
    searchScored(scope: string, query: string, limit?: number): ScoredMemory[] | Promise<ScoredMemory[]>;
    /**
     * Consolidation UPDATE (design/81 Slice 2): replace a note's body by id, keeping the id stable. Appends a
     * durable `update` event FIRST, then mutates the in-memory replay + refreshes the projection — same
     * durable-before-visible order as {@link commit}. An auto-derived description is re-derived from the new
     * body (so the manifest doesn't drift); an explicit one is preserved (design/65 [79], in-memory parity).
     * Unknown id → no-op (defensive). NB: no `sanitizeUntrustedText` here — the fence lives at the
     * inlets/recall, NOT in `update` (design/81 §1; in-memory `update` only trims).
     */
    update(scope: string, id: string, text: string): void;
    /**
     * Consolidation DELETE (design/81 Slice 2): remove a note by id (id never reused afterward). Appends a
     * durable `delete` tombstone FIRST, then drops it from the in-memory replay + refreshes the projection.
     * Unknown id → no-op.
     */
    delete(scope: string, id: string): void;
    /** Release all open scope handles (best-effort). */
    close(): void;
}
