/**
 * loadRelevant — read-side stage that embeds a query and fetches the
 * most similar entries from a vector-capable `MemoryStore`.
 *
 * Reads from scope:   `identity`, `messages` (or `newMessages`, or custom queryFrom)
 * Writes to scope:    `loaded` (MemoryEntry[], ordered best-first — narrowed
 *                     by `pickByBudget` downstream)
 *                     `retrieved` (RetrievalEvidence — what was considered,
 *                     what was admitted, and why about each one)
 *
 * Query derivation:
 *   Default: the last user message. That's the natural "what is the user
 *   asking about?" signal. Override with `queryFrom` for custom retrieval.
 *
 * Empty behavior:
 *   No query text → no search → `loaded = []`. Downstream `pickByBudget`
 *   picks nothing and the formatter emits nothing — safe, and now
 *   RECORDED: the evidence still lands, saying the query was empty.
 *
 * Feature detection:
 *   Throws at stage build time if the store doesn't implement `search()`.
 *   Fail-loud — a semantic pipeline configured against a non-vector store
 *   is a config bug, not a runtime condition.
 *
 * ─── Why the threshold moved out of the store (8.8.0) ──────────────────
 *
 * Until 8.8.0 the quality floor was passed to `store.search({ minScore })`,
 * so every candidate that failed it was filtered INSIDE the store and never
 * came back. The consequence was not a small one: a retrieval that injected
 * nothing left no trace of what it nearly injected, and "why did the agent
 * not read that passage" had no answer anywhere in the recording.
 *
 * The floor is now applied here, over a pool of `k + rejectWindow`. **This
 * does not change which entries are admitted, ever.** Proof: `search`
 * returns score-descending, and the pool is at least `k`. Either
 *
 *   (a) every entry in the pool clears the floor — then the admitted set is
 *       the first `k` of them, which is exactly what `{ minScore, k }` would
 *       have returned; or
 *   (b) some entry at position p fails the floor — then every entry after p
 *       fails it too, so the pool already contains EVERY entry in the
 *       namespace that clears the floor, and admitting all of them (capped
 *       at `k`) is again exactly what `{ minScore, k }` would have returned.
 *
 * `rejectWindow` therefore only controls how many near-misses we can SHOW.
 * It cannot change what the model sees.
 *
 * ─── The size bound (8.19.0) ───────────────────────────────────────────
 *
 * `k` is a COUNT bound. It says how many passages may reach the prompt and
 * nothing about how much text that is — which is how ten ordinary headings
 * became eleven thousand characters against a 4000-character slot budget,
 * with nothing but defaults on either side. `maxChars` is the bound on the
 * other axis: a character budget spent across the admitted passages in rank
 * order, tail dropped, every drop named `'over-char-budget'` in the record.
 * Unset by default, so a retriever that does not ask for it behaves exactly
 * as it did before.
 */
import type { TypedScope } from 'footprintjs';
import type { MemoryStore } from '../store/index.js';
import type { MemoryState } from '../stages/index.js';
import type { RetrievalStrategy } from '../retrieval/types.js';
import type { Embedder } from './types.js';
export interface LoadRelevantConfig {
    /** The vector-capable store. Must implement `search()`. */
    readonly store: MemoryStore;
    /**
     * Embedder used to turn the query text into a vector.
     *
     * Optional since 9.3.0, for a store that declares `ranksBy: 'server-text'`:
     * it takes the question as words (`SearchOptions.text`) and ranks it on the
     * backend's side, so no vector is ever produced, nothing is billed for one,
     * and no `agentfootprint.embedding.generated` is emitted — the recording
     * says an embedding happened only when one did.
     */
    readonly embedder?: Embedder;
    /**
     * Identifier for the embedder. When set, the search filters entries
     * to those produced by the same embedder (prevents cross-model
     * similarity pollution).
     */
    readonly embedderId?: string;
    /** Top-k to retrieve. Default 20 — picker will narrow further by budget. */
    readonly k?: number;
    /** Minimum cosine score [-1, 1] to consider a match. Default: none. */
    readonly minScore?: number;
    /**
     * A character budget for the admitted passages, spent in RANK order
     * (8.19.0). Default: none — `k` is the only bound, exactly as before.
     *
     * Counts passage characters, not rendered prompt bytes. See
     * {@link RetrievalEvidence.maxChars}.
     */
    readonly maxChars?: number;
    /** Filter results by tier. */
    readonly tiers?: ReadonlyArray<'hot' | 'warm' | 'cold'>;
    /**
     * The rule that decides which candidates reach the prompt. Defaults to
     * `topK({ k, threshold: minScore })` — i.e. `k` and `minScore` above are
     * the shorthand, and this is the same rule spelled out. Pass a strategy
     * to replace the rule entirely.
     */
    readonly retrieval?: RetrievalStrategy;
    /**
     * Extract the query text from scope. Default: the last user message.
     * Override for custom retrieval signals.
     */
    readonly queryFrom?: (scope: TypedScope<MemoryState>) => string;
}
export declare function loadRelevant(config: LoadRelevantConfig): (scope: TypedScope<MemoryState>) => Promise<void>;
/** Test seam — the once-per-process warning is per PROCESS, which tests must be able to reset. */
export declare function __resetEmptyCorpusWarnings(): void;
