import type { MemoryStore } from "./memory.js";
import { type ConsolidationLLM, type ConsolidationStats } from "./runner/memory-consolidation.js";
/**
 * design/84 Seam B (M6+M10) — **cursor-incremental scope consolidation**. A thin function over the pure
 * {@link runMemoryConsolidation}: it reads the scope's persisted cursor, feeds ONLY the notes appended
 * AFTER it (the incremental batch) into one reconcile pass, then — IFF the whole pass succeeds — advances
 * the cursor to the high-water mark it observed. This is the externally-triggerable periodic path (a
 * deployment's timer / N-session gate fires it); the TRIGGER and any cross-process LOCK live OUTSIDE this
 * function (the deployment shell's exec/persist axes — see {@link ConsolidateScopeOptions.acquire}).
 *
 * **Cursor marker semantics (no-miss / no-dup):** the cursor is an OPAQUE ordering marker the STORE defines
 * (this core never parses it). In the three reference stores it is a note **id** — `uuidv7`, which is
 * lexicographically time-sortable, so "after the cursor" is a string `>` comparison and the high-water mark
 * is the lexicographic MAX. Each pass:
 *  - reads `cursor = getConsolidationCursor(scope)` (undefined ⇒ whole scope pending),
 *  - lists the scope's note headers (stable store ordering) and keeps those with `id > cursor` AND not
 *    already `consolidationGenerated` (a prior pass's own output — excluded so it is never re-merged),
 *  - reconciles that incremental batch via `runMemoryConsolidation` (which itself re-excludes
 *    `consolidationGenerated` + this-batch ids from each note's candidate set),
 *  - advances the cursor to the high-water mark of the fed batch ∪ `stats.addedIds`, but capped STRICTLY
 *    BELOW the smallest note this pass FAILED to process (`stats.failedIds`) — a contiguous successful-prefix
 *    marker, not the plain max (the MAJOR1 BLOCKER: a single max marker cannot keep a sub-max failed note
 *    pending; see {@link cappedHighWater}).
 * No-dup: a note with `id <= cursor` is never re-fed. No-miss: a note appended after the advance, or one at/
 * above a failure floor this pass, keeps `id > cursor` (uuidv7 monotonic) and is picked up by a later pass. A
 * pass's own ADDs carry `consolidationGenerated:true` AND (when below the failure floor) are folded into the
 * high-water mark, so they are excluded twice over.
 *
 * **Whole-pass advance (decision 3):** the cursor advances (up to the success boundary) ONLY when the pass
 * returns (no throw). A pass that THROWS (LLM hard failure) does NOT advance at all — the batch is fully
 * retried next time. A pass that returns with per-note failures advances only past the contiguous successful
 * prefix below the smallest failure (fail-open: at worst a near-duplicate survives an extra pass, never data
 * loss; consistent with the memory-store contract).
 *
 * **No-op safety (the BLOCKER §2 closes):** when the store lacks the cursor pair
 * ({@link supportsPeriodicConsolidation} false) this is a NO-OP that calls `onWarn` — it NEVER degrades into a
 * full re-consolidation of the whole scope (which would re-merge already-consolidated notes forever). It is
 * also a no-op when the store can't consolidate at all ({@link supportsConsolidation} false) or the manifest
 * read pair ({@link MemoryStore.listStructuredNotes} / {@link MemoryStore.getByIds}) is incomplete.
 */
export interface ConsolidateScopeDeps {
    store: MemoryStore;
    llm: ConsolidationLLM;
    /** Routes diagnostics (skipped decisions, no-op-because-unsupported, lock-busy) — same sink as the Runner's
     *  `onError(phase:"memory")`. Never throws back into this function. */
    onWarn?: (err: unknown) => void;
    /**
     * design/84 Seam B (TOC profile) — OPTIONAL cross-process consolidation lock. When provided,
     * {@link consolidateScope} acquires it for `scope` before the pass and releases it after; a `release` of
     * `undefined` (lock busy) makes the pass a NO-OP (another process is already consolidating this scope).
     * The implementation (writeThenLink + stale-PID prune) lives in the deployment shell (`stores/file`), NOT
     * core — core only DEFINES the injection point (the constitutional persist/exec-axis split).
     */
    acquire?: (scope: string) => Promise<(() => void) | undefined> | (() => void) | undefined;
}
/** Tuning for one {@link consolidateScope} pass (mirrors the inline consolidation settings; all optional). */
export interface ConsolidateScopeOptions {
    band?: {
        lo: number;
        hi: number;
    };
    searchLimit?: number;
    /** Cap on notes fed into ONE pass (token bound). The cursor advances only past the contiguous SUCCESSFUL
     *  prefix of the FED batch (the first `maxNotes` pending) — never past the un-fed tail beyond the cap, and
     *  never past a note this pass FAILED to process — so a backlog larger than `maxNotes`, and any failed note,
     *  keep `id > cursor` and drain over successive passes without ever re-feeding the notes already handled. */
    maxNotes?: number;
}
/**
 * Run ONE cursor-incremental consolidation pass for `scope` (design/84 Seam B). Returns the pass stats
 * (or `undefined` when it was a no-op: unsupported store, empty incremental batch, or lock busy). Never
 * throws for a no-op reason; a hard LLM failure inside `runMemoryConsolidation` propagates (the caller's
 * fire-and-forget envelope catches it) and the cursor is left UN-advanced (the batch retries next pass).
 */
export declare function consolidateScope(scope: string, deps: ConsolidateScopeDeps, opts?: ConsolidateScopeOptions): Promise<ConsolidationStats | undefined>;
/**
 * design/84 Seam B (切片 4) — advance ONLY the cursor after an INLINE (task-end) consolidation pass has
 * already run, so a later periodic {@link consolidateScope} starts from the inline-processed high-water mark
 * (it does NOT re-consolidate the notes the inline pass just handled). The inline path consolidates the
 * notes the model saved THIS task (the newest, highest-id notes); advancing the cursor over the PROCESSED
 * ids ∪ addedIds makes inline and periodic share ONE cursor.
 *
 * **MAJOR1 BLOCKER:** the advance caps STRICTLY BELOW the smallest FAILED note (`failedIds`). A single
 * max-marker cursor cannot keep a sub-max failed note pending, so a low-id note whose handling failed must
 * stop the cursor below it even when a higher-id sibling succeeded — otherwise the failed note (id <= the
 * plain max) would lose `id > cursor` and be exiled from consolidation forever. Pass the pass's
 * `stats.consolidatedIds` (processed), `stats.addedIds`, and `stats.failedIds`.
 *
 * No-op when the store lacks the cursor pair (back-compat: an old store keeps the pure-inline behavior).
 * Never throws — a cursor-advance failure routes to `onWarn` (the inline notes are already durable; the
 * worst case is the next periodic pass re-sees them, which `runMemoryConsolidation` handles idempotently).
 */
export declare function advanceCursorAfterInline(store: MemoryStore, scope: string, seenNoteIds: ReadonlyArray<string | undefined>, addedIds: ReadonlyArray<string>, failedIds?: ReadonlyArray<string>, onWarn?: (err: unknown) => void): Promise<void>;
