import type { MemoryNoteHeader, MemoryNoteRecord, MemoryStore } from "./memory.js";
/**
 * Selective memory recall (design/65 §2.2 — port of CC `findRelevantMemories`). For a LARGE memory scope,
 * instead of injecting the whole `<user_memory>` block (inject-all), we inject a compact **manifest** (one
 * header line per note) + a side-query that picks the ≤K relevant note ids + only those bodies. Small
 * scopes keep inject-all (the common case after consolidation) — this path is threshold-gated, so most
 * tasks pay nothing.
 *
 * ⚠️ WHAT IS ACTUALLY IN THIS FILE (RB-446, corrected 2026-07-31 — the text below used to describe a
 * DRIVER that no longer exists here, in the present tense). The 2.0.0 B19 export sweep removed the
 * function that ran the pipeline; what remains, and what the rest of core consumes, is the set of PURE
 * HELPERS plus the declared shape of that pipeline:
 *   · helpers, all live and in use: {@link formatMemoryAge}, {@link RECALL_CAVEAT},
 *     {@link buildManifestText}, {@link validateSelectedIds}, {@link resolveLinkedIds},
 *     {@link composeSelectiveBody}, {@link encodeSurfacedKey};
 *   · seam DECLARATIONS with no in-tree caller: {@link MemorySelector} /
 *     {@link SelectiveRecallOptions} / {@link SelectiveRecallResult} / {@link LayeredRecallOptions} /
 *     {@link LayeredRecallResult}. They stay exported because they are the published contract a
 *     deployment implements its own driver against — the helpers above are exactly the pieces such a
 *     driver assembles — but nothing under `src/` calls them today. Read them as a specification, not
 *     as a description of code that runs.
 *
 * The pipeline those declarations specify: pure + deterministic given a selector — validate the returned
 * ids against the manifest (reject hallucinated ids, council #5), cap at K, apply a hard timeout
 * (council #12), and on ANY selector failure/timeout DEGRADE to inject-all (council #4 — never silently
 * drop knowledge). Output goes in the variable tail (manifest + selected bodies are per-user/volatile,
 * like the current memory block). `validateSelectedIds` is the one rung of it that IS implemented here.
 */
/** Default max notes the side-query may select (CC `findRelevantMemories` ≤5). */
export declare const DEFAULT_MAX_SELECTED = 5;
/** Default max 1-hop link-followed notes appended after the selected ones (design/65 P3). */
export declare const DEFAULT_MAX_LINKED = 3;
/** Default side-query hard timeout (ms) — council #12; on timeout we degrade to inject-all. */
export declare const DEFAULT_SELECT_TIMEOUT_MS = 5000;
/**
 * Render a memory note's age as a natural-language phrase (CC-parity P0-1, design/65). CC's eval found
 * models reason about memory age much better from natural language ("3 days ago") than from a boolean
 * "older note" flag (verify-claims 0/2 → 3/3 once the age was spelled out), because the model can weigh
 * staleness proportionally instead of as a single cliff. This is the relative phrase only; the
 * point-in-time / verify-before-trust discipline still lives in {@link RECALL_CAVEAT} + the per-note
 * "verify it's still current" hint (the age is ADDITIONAL signal, not a replacement).
 *
 * Buckets (each chooses the coarsest unit that's ≥1): `today` (<1 day, incl. future mtime / clock skew →
 * clamped to today, never a negative age), `yesterday` (1–2 days), `N days ago` (2–7 days),
 * `N weeks ago` (1 week – ~1 month), `N months ago` (≥ ~1 month). N is floored to the whole unit.
 */
export declare function formatMemoryAge(ageMs: number): string;
/**
 * Read-side recall caveat (design/65 §8#2 / §9#1 — ported from CC's MEMORY_DRIFT_CAVEAT +
 * TRUSTING_RECALL_SECTION). The council found CC's two-section form is **eval-validated** (verify-claims
 * 0/2→3/3) and that the structure carries the effect: the precise decision-point heading
 * "## Before recommending from memory" scored 3/3 while an abstract heading or a buried bullet scored 0/3.
 * So this is deliberately TWO sections — a drift bullet + a titled action section with the frame-shift
 * sentence and concrete verify actions — NOT one merged paragraph (same semantics, 3× worse eval).
 * MEMORY_SAFETY already carries the authority hierarchy; this adds the point-in-time / verify-before-trust
 * discipline selective recall specifically needs.
 */
export declare const RECALL_CAVEAT: string;
export interface MemorySelectRequest {
    /** The task objective the recall is for. */
    objective: string;
    /** The manifest the selector chooses from (id + description [+ type]). */
    manifest: MemoryNoteHeader[];
    /** Tools the task will use — for these, keep gotchas/warnings, skip pure usage notes (design/65 §2.2). */
    recentTools?: string[];
}
/** The side-query seam: pick the relevant note ids from the manifest. MUST be fail-fast (throw → degrade). */
export type MemorySelector = (req: MemorySelectRequest, signal?: AbortSignal) => Promise<string[]>;
export interface SelectiveRecallOptions {
    store: MemoryStore;
    scope: string;
    objective: string;
    selector: MemorySelector;
    recentTools?: string[];
    /** Absolute time for the freshness caveat (default Date.now()). */
    nowMs?: number;
    maxSelected?: number;
    /** Max 1-hop `[[name]]` link-followed notes appended after the selected ones (design/65 P3).
     *  `0` disables link-following. Default {@link DEFAULT_MAX_LINKED}. */
    maxLinked?: number;
    timeoutMs?: number;
    signal?: AbortSignal;
    /**
     * CC-parity P1 (durable cross-worker recall de-dup). Note ids ALREADY surfaced to this logical
     * conversation in earlier turns/runs (CC's in-process `alreadySurfaced`/readFileState filter, lifted
     * to a **durable, caller-owned** set so it survives task hops, resume, and multi-worker fan-out). When
     * provided, these ids are **filtered out of the manifest BEFORE the selector sees it** — the selector
     * never sees an already-shown note, so it cannot re-select it, so a long/resumed/multi-worker session
     * does not re-inject the same bodies. core does NOT store this set (like {@link CheckpointStore}, the
     * durable store is the caller's): the caller persists {@link surfacedIds} from one call and feeds it
     * back here as `priorSurfacedIds` on the next. Omitted ⇒ no filtering (the prior, non-de-duped behavior).
     */
    priorSurfacedIds?: ReadonlySet<string>;
}
/**
 * Result of a single-scope selective-recall pass. `surfacedIds` (CC-parity P1) is the set of note ids whose
 * BODIES this call injected into the prompt — the caller appends them to its durable surfaced-set and
 * feeds that back as {@link SelectiveRecallOptions.priorSurfacedIds} next time, so a long/resumed/
 * multi-worker session does not re-inject the same notes. THREE shapes (all carry `surfacedIds`):
 *
 *  - **block branch** (`{ block: string }`): a selective-recall block was rendered. `surfacedIds` = the
 *    validated selection + the 1-hop linked notes (everything whose body was rendered). The caller injects
 *    `block` verbatim.
 *
 *  - **no-new branch** (`{ block: undefined }`): a SUCCESS, NOT a degrade. Every selectable candidate was
 *    already surfaced earlier (filtered out by {@link SelectiveRecallOptions.priorSurfacedIds}), so there is
 *    NOTHING NEW to inject. The caller injects NOTHING (no block) — it must NOT fall back to inject-all,
 *    because inject-all would re-dump the WHOLE scope including the already-shown notes, defeating the
 *    de-dup. `surfacedIds` is `[]` (this call added no new note to the durable set; the prior ids are
 *    already in the caller's store). Distinguished from `degrade` precisely so the caller does not
 *    re-inject (the bug this branch fixes). `"block" in result` is TRUE here (the key is present, value
 *    `undefined`), so the caller's `"degrade" in result` narrow routes this to the non-degrade path.
 *
 *  - **degrade branch** (`{ degrade: true }`): a true FALLBACK — the store can't do selective recall
 *    (missing manifest/getByIds), the scope is empty, OR the selector failed/timed out. The caller falls
 *    back to inject-all (never drops knowledge, council #4), a best-effort FULL DUMP that
 *    `composeMemoryBlock` hard-truncates to a head slice (so it does NOT reliably render every body), and
 *    the caller already holds the prior surfaced ids. Degrade is therefore not a precise per-id surface that
 *    can be de-duped: `surfacedIds` is ALWAYS `[]` and degrade does **not** advance the durable surfaced-set.
 *    (Reporting the full header id list here would over-claim the truncated tail as surfaced → permanent
 *    silent under-recall on the next turn.)
 */
export type SelectiveRecallResult = {
    block: string;
    surfacedIds: string[];
} | {
    block: undefined;
    surfacedIds: string[];
} | {
    degrade: true;
    surfacedIds: string[];
};
export declare function resolveLinkedIds(headers: MemoryNoteHeader[], selected: MemoryNoteRecord[], max: number): string[];
/** Render the manifest as compact lines, double-capped (council #7). One line per note, guaranteed. */
export declare function buildManifestText(headers: MemoryNoteHeader[]): string;
/** Keep only ids present in the manifest (reject hallucinated, council #5), de-duped, capped at `max`. */
export declare function validateSelectedIds(headers: MemoryNoteHeader[], ids: string[], max: number): string[];
/**
 * Render the BODY of a selective-recall block: the optional `recall` manifest index + the selected note
 * bodies (+ 1-hop linked notes), WITHOUT the outer `<user_memory>` wrapper and WITHOUT {@link RECALL_CAVEAT}.
 * The wrapper + caveat are the caller's job — a single-scope caller places ONE caveat
 * inside ONE `<user_memory scope=… mode="selective">`, and the layered renderer places ONE caveat inside ONE
 * `<user_memory mode="selective">` ahead of N `<scope>` subsections (MAJOR fix: no nested fences, no per-scope
 * caveat duplication).
 *
 * `recallable` controls the "Memory index — load in full …" affordance (MAJOR fix): the on-demand recall
 * tool is bound to a SINGLE scope, so the "load in full" instruction is only TRUE for that scope. Pass
 * `false` for non-recallable layers — the selected bodies are already rendered inline, so the index is just a
 * false affordance that wastes a tool call (model searches the wrong scope → "No memory matches").
 *
 * `recallToolName` (#181-F1, additive) is the WIRE name of the host-mounted recall tool the affordance
 * teaches. Core mounts no such tool ("Recall" is a RETIRED name — tool-name-aliases.ts), so a hard-coded
 * name here can only ever be stale; the host that mounted the tool passes the name it mounted it under.
 * Omitted ⇒ a name-free "search the memory-recall tool" wording (honest without inventing a name).
 *
 * Notes are tagged with their natural-language age (CC-parity P0-1, design/65; >1-day-old ones keep the
 * "verify it's still current" hint, council #8). `linked` (design/65 P3) are 1-hop `[[name]]`-followed notes,
 * rendered in their own clearly-labeled subsection AFTER the selected ones — expanded as DATA under the same
 * fence/sanitize/byte-cap discipline (design/65 §8#9: a followed link must not become markup or escalate authority).
 *
 * SEAT (test B14/B15): this is a HOST-side recall-pipeline API — a BYOM host
 * that builds its own selective-recall lane composes the injected block through it. The engine's
 * own `inject()` path deliberately does NOT route here (it assembles instruction+index+announcements
 * only; live recall is the model-driven memory_search tool with its own caps). Public export with
 * no in-engine consumer is the intended shape, not dead code.
 */
export declare function composeSelectiveBody(manifestText: string, selected: MemoryNoteRecord[], nowMs: number, linked?: MemoryNoteRecord[], recallable?: boolean, recallToolName?: string): string;
/** A `(scope, id)` composite key for the multi-scope durable de-dup (design/84 Seam A decision 5). Note ids
 *  are only unique WITHIN a scope, so the cross-scope `priorSurfacedKeys`/`surfacedKeys` set MUST be keyed on
 *  both. Core owns the FORMAT so a caller can't drift the wire format (decision 7) — the encoding is OPAQUE
 *  and length-prefixes the scope so a scope/id containing the separator cannot self-declare a collision.
 *
 *  ONE-WAY by design ((d)): the key is only ever PRODUCED here (`surfacedKeys`) and compared by
 *  STRING EQUALITY against the caller's persisted set ({@link LayeredRecallOptions.priorSurfacedKeys}, applied
 *  at memory-recall.ts via `priorSurfacedKeys.has(encodeSurfacedKey(h.scope, h.id))`). Nothing ever needs the
 *  `(scope, id)` BACK out of a key, so there is intentionally NO `decodeSurfacedKey` — adding a decode would
 *  widen the public surface (and invite callers to parse the opaque format) for zero consumer. Treat the key
 *  as an opaque token: persist it, feed it back, compare for equality — never split it. */
export declare function encodeSurfacedKey(scope: string, id: string): string;
export interface LayeredRecallOptions {
    store: MemoryStore;
    /** Ordered scopes (design/84 Seam A): list order = priority; the last is highest. */
    scopes: ReadonlyArray<string>;
    objective: string;
    selector: MemorySelector;
    recentTools?: string[];
    nowMs?: number;
    maxSelected?: number;
    maxLinked?: number;
    timeoutMs?: number;
    signal?: AbortSignal;
    /** CC-parity P1, multi-scope: `(scope,id)` composite keys ({@link encodeSurfacedKey}) already surfaced to
     *  this logical conversation. Filtered out of the merged manifest BEFORE the selector sees it. */
    priorSurfacedKeys?: ReadonlySet<string>;
}
/** Result of a multi-scope (layered) selective-recall pass. `surfacedKeys` are `(scope,id)` composite keys
 *  ({@link encodeSurfacedKey}) — the caller appends them to its durable set and feeds them back as
 *  {@link LayeredRecallOptions.priorSurfacedKeys}. Mirrors {@link SelectiveRecallResult}'s three shapes. */
export type LayeredRecallResult = {
    block: string;
    surfacedKeys: string[];
} | {
    block: undefined;
    surfacedKeys: string[];
} | {
    degrade: true;
    surfacedKeys: string[];
};
