/**
 * design/178 — the engine-memory retrieval tools (`memory_search` / `memory_get` / `memory_index`),
 * the runner-wired consumers of the `MemoryBackend.search()`/`getByIds()` contract: a deterministic
 * keyword lookup over the session's mounted scopes, a paged full-content read, and a paged
 * enumeration of EVERY mounted entry. That enumeration is the completeness floor under the other
 * two — any committed entry is reachable through it regardless of the injected index's truncation
 * window and of whether its words match the question (out-of-window ∧ lexically disjoint is
 * otherwise structurally unreachable). The tools sit entirely ON TOP of the backend seam.
 *
 * READ-ONLY BY STRUCTURE. No tool here can write; the write path stays the file channel + harvest
 * gate. Entry content returned to the model is DATA, not instructions: every body fragment rides an
 * untrusted fence (`delimitUntrusted`) and every model-authorable header field (name/description/
 * slug/scope) is neutralized inline. Results are byte-bounded.
 *
 * THE TRIO IS ATOMIC: each tool names the others in its description, so a partial mount would teach a
 * tool that is not there — the mount site (runner/prepare-task.ts) mounts all or none.
 *
 * RESULT ORDER IS THE BACKEND CONTRACT ORDER: ascending cosine-distance score with the
 * deterministic id tie-break, exactly what `MemoryBackend.search()` promises. Multi-plane sessions
 * merge by the same comparator. Under `provenance: "carry"` (design/336 §5.2) the band key LEADS
 * that comparator on both sides of the seam — unmarked entries first, marked after, contract order
 * within a band — and marked hits render as opaque handles. No other signal participates: in
 * particular the retrieved account is WRITTEN here and never read.
 *
 * REFUSALS ARE VALUES: every negative outcome is a structured result with a machine reason; exceptions
 * are reserved for defects and for cancellation, which is re-thrown so the abort path stays the abort path.
 */
import type { ToolSpec } from "../types.js";
import type { MemoryBackend, MemoryEntryHeader } from "./types.js";
export declare const MEMORY_SEARCH_TOOL_NAME = "memory_search";
export declare const MEMORY_GET_TOOL_NAME = "memory_get";
/** #493 件① — the enumeration face. NOT `memory_list`: that wire name (and the `core.memory_list@1`
 *  contract id) belongs to the design/177 shared-memory store pair, and both families can mount in
 *  one session. */
export declare const MEMORY_INDEX_TOOL_NAME = "memory_index";
/** The atomic trio, in mount order (same shape as SHARED_MEMORY_TOOL_NAMES). */
export declare const MEMORY_ENGINE_TOOL_NAMES: readonly ["memory_search", "memory_get", "memory_index"];
/** Default / maximum hit counts for one search call. */
export declare const MEMORY_SEARCH_DEFAULT_LIMIT = 8;
export declare const MEMORY_SEARCH_MAX_LIMIT = 20;
/** Default / maximum rows of one memory_index page (a row is ≲600 bytes by the inline caps, so the
 *  extremes stay well under one tool-result's budget; the per-page byte bound below is the backstop). */
export declare const MEMORY_INDEX_PAGE_ROWS = 100;
export declare const MEMORY_INDEX_MAX_PAGE_ROWS = 500;
/** Byte bound of one memory_index page (rows past it defer to the next page, disclosed). */
export declare const MEMORY_INDEX_PAGE_CAP_BYTES: number;
/** Per-hit body-fragment bound (code points, enforced by the fence itself). */
export declare const MEMORY_SEARCH_SNIPPET_CAP = 600;
/** Default / maximum lines of one memory_get page. */
export declare const MEMORY_GET_PAGE_LINES = 200;
export declare const MEMORY_GET_MAX_PAGE_LINES = 1000;
/** Byte bound of one memory_get page body (lines past it defer to the next page). */
export declare const MEMORY_GET_PAGE_CAP_BYTES: number;
/**
 * One retrieval plane: a backend plus the scopes THIS SESSION mounted on it (dual-root sessions have
 * two, single-root sessions one). `scopes` is the containment boundary — `getByIds` is id-keyed
 * across every scope the backend knows, so results are filtered back to the mounted set here.
 * `recordRetrieved` feeds the plane's control-plane retrieved account (collect-only, never throws).
 */
export interface MemoryEnginePlane {
    backend: MemoryBackend;
    scopes: readonly string[];
    recordRetrieved: (ids: readonly string[]) => void;
    /**
     * design/180 B-2' — the plane's EXCLUSION view: entry id → why its content is withheld from the
     * model face (`challenged` = an unresolved S-grade challenge generation; `lineage_pending` = the
     * dirty latch, its commit bookkeeping is unsettled). THROWS when the underlying ledger's
     * integrity is unknowable — both tools then REFUSE SERVICE on that plane (fail-closed: a corrupt
     * ledger must never read as "nothing withheld"). Absent ⇒ a plane with no control plane (direct
     * hosts / tests) — no exclusion, v1 behavior byte-identical.
     */
    challengeExclusions?: () => ReadonlyMap<string, {
        code: "challenged" | "lineage_pending";
        generation?: number;
        at?: number;
    }>;
    /**
     * design/336 §5.5 — the recall-taint propagation seat, fired at the ONE tool-face content
     * delivery point of a marked entry (`memory_get`'s successful delivery; the search list never
     * fires it — a hit line delivers no content). The runner wires it to the session's pollution
     * mark with cause `"derived"`, closing source-scope laundering at session granularity: a session
     * that took up marked content writes marked entries from then on. Consulted only under
     * `provenance: "carry"` (the seat is not even wired under "off").
     *
     * A THROW here withholds the delivery, fail-closed (the tool answers a structured failure): if
     * the taint cannot be recorded, the content must not enter the transcript — the same law as the
     * settlement account's record-failure arm.
     */
    onTaintedDelivery?: (ids: readonly string[]) => void;
    /**
     * design/383 §1.7 — the provenance mode of the WRITE-SIDE engine that owns this plane, read off
     * the engine itself (`MemoryEngine.provenanceMode`), never re-declared by hand. Present ⇒ the
     * factory ASSERTS it against its own {@link MemoryEngineToolsOptions.provenance}: a wiring that
     * writes under `"carry"` while reading under `"off"` mints origin markers no read face will ever
     * honour (marked entries served through the clean shape, no banner, no recall-taint propagation)
     * — the two halves are one deployment posture and a split between them is a configuration fault,
     * refused at construction (`config.memory_provenance`) rather than discovered as a quiet leak.
     *
     * Scope, stated honestly: this kills CONFIGURATION divergence only. It proves nothing about
     * whether any individual entry is classified correctly — that is the 178 fail-closed default's
     * job, not this assertion's. Absent ⇒ no engine mode is declared for this plane (engine-direct
     * hosts and tests) and nothing is asserted: the factory's behaviour is byte-identical to before.
     */
    enginePlaneProvenance?: "off" | "carry";
    /**
     * The plane's COMMITTED header listing — the name source of the write-home rule
     * ({@link import("./shadowing.js").deriveShadowedSet}). Wired to
     * `MemoryEngine.listCommittedHeaders`, it is what makes an in-session edit of a `name:` field
     * retire nothing before the harvest boundary admits it. ABSENT ⇒ this plane contributes no
     * write-home names, so nothing on any plane is shadowed and every face below is byte-identical
     * to a deployment without the rule (engine-direct hosts and tests).
     */
    committedHeaders?: () => Promise<MemoryEntryHeader[]>;
}
export interface MemoryEngineToolsOptions {
    planes: ReadonlyArray<MemoryEnginePlane>;
    /** Clock (tests); default Date.now. Drives the natural-language age rendering only. */
    now?: () => number;
    /**
     * design/178 §3 (#324b) — the session's pollution READ face: the record when this session is marked
     * polluted, `undefined` when it is clean. Read-only and side-effect-free (the mark is one-way and
     * lives at the runner's mark seat); consulted ONLY on the empty outcomes, where a session whose
     * writes are being withheld deserves to hear it instead of being told to search again.
     *
     * Absent ⇒ no pollution face is wired (engine-direct hosts, tests): the empty-outcome wording is
     * then exactly the clean-session wording — nothing is inferred from the seat's absence.
     */
    sessionPollution?: () => {
        reason: string;
    } | undefined;
    /**
     * design/336 §13-3 — the provenance mode the write engine runs under. Two consumers here:
     * - the pollution sentence states what the mark actually DOES (under `"carry"` writes commit
     *   with an origin marker; the pre-336 "not admitted" sentence would be false);
     * - the READ-SIDE treatment (§5.2/§5.3/§5.5) arms only under `"carry"`: two-band search order,
     *   opaque handles for marked hits, the memory_get banner + `exposure` detail, and the
     *   recall-taint propagation. Absent ≡ `"off"` keeps every read face byte-identical to pre-336
     *   even over a store that carries marked entries (the knob's whole meaning).
     */
    provenance?: "off" | "carry";
    /**
     * The session's WRITE HOME scope — the layer every write of this session routes to. The
     * write-home name rule's one input beside the headers: a read-only layer's entry whose name this
     * scope already carries leaves the DEFAULT face of all three tools (retrievable through
     * `includeShadowed` and by id). ABSENT or `null` ⇒ no write home is declared to these faces and
     * nothing is ever shadowed — the pre-rule bytes.
     */
    writeScope?: string | null;
}
/**
 * An unmarked search hit — the pre-336 hit shape, `slug` required as before. `exposure` is a
 * `never` seat so the union below discriminates on it (`hit.exposure === "external"` narrows).
 */
export interface CleanMemorySearchHit {
    id: string;
    scope: string;
    slug: string;
    name?: string;
    description?: string;
    /** Cosine-distance ∈ [0,2], 0 best — the backend contract's score, untransformed (v1). */
    score: number;
    mtimeMs: number;
    sizeBytes: number;
    exposure?: never;
    /** design/339 §3.4 — present ⇔ this hit is SUPERSEDED (retrieved via `includeSuperseded`; the
     *  default face filters such hits out) — the value is the standing carrier entry's id. */
    supersededBy?: string;
    /** Present ⇔ this hit is SHADOWED (retrieved via `includeShadowed`; the default face filters
     *  such hits out) — the value is the write-home entry id whose name retires it. */
    shadowedBy?: string;
}
/**
 * design/336 §5.2 — a MARKED hit is an OPAQUE HANDLE: id/scope/score/age/size plus the exposure
 * discriminant, and the text seats (`slug`/`name`/`description`) structurally NEVER present — a
 * marked entry's model-authored strings are content positions, and the passive face carries zero
 * of them (an imperative sentence inside a `name:` would otherwise ride every hit line untaxed).
 * The content is read through `memory_get` by id (the active face, banner + propagation).
 */
export interface ExposedMemorySearchHit {
    exposure: "external";
    id: string;
    scope: string;
    /** Cosine-distance ∈ [0,2], 0 best — same contract score; the hit stays ranked, only banded. */
    score: number;
    mtimeMs: number;
    sizeBytes: number;
    slug?: never;
    name?: never;
    description?: never;
    /** design/339 §3.4 — see {@link CleanMemorySearchHit.supersededBy} (same seat on the handle shape). */
    supersededBy?: string;
    /** See {@link CleanMemorySearchHit.shadowedBy} (same seat on the handle shape). */
    shadowedBy?: string;
}
/**
 * The search hit union (design/336 B5 — a DISCRIMINATED union on `exposure`, deliberately not an
 * "optional members" widening: a consumer must branch to touch the text seats, so a marked hit can
 * never be rendered through the clean shape by accident). Under `provenance: "off"` every hit is
 * the clean shape, byte-identical to pre-336.
 */
export type MemorySearchHit = CleanMemorySearchHit | ExposedMemorySearchHit;
export interface MemorySearchDetails {
    outcome: "ok" | "refused" | "failed";
    reason?: string;
    hits?: MemorySearchHit[];
}
export interface MemoryGetDetails {
    outcome: "ok" | "not_found" | "ambiguous" | "refused" | "failed";
    reason?: string;
    id?: string;
    scope?: string;
    slug?: string;
    /** Candidates of an ambiguous bare-slug lookup — scope-qualified, never silently picked from. */
    candidates?: Array<{
        scope: string;
        slug: string;
        id: string;
    }>;
    offset?: number;
    lines?: number;
    totalLines?: number;
    /** design/180 — the OPAQUE withheld refusal's structured half (`reason: "challenged" |
     *  "lineage_pending"`): the unresolved challenge generation and its timestamp. Deliberately the
     *  ONLY fields beyond the id — name/description/free reason text are content positions and live
     *  on the host audit face exclusively. */
    generation?: number;
    challengedAt?: number;
    /** design/180 (intra-line cursor) — byte offset into the (neutralized) single line at `offset`
     *  this page continued from / where the next page should continue. */
    lineCursor?: number;
    nextLineCursor?: number;
    /** design/336 §5.3 — present on an `ok` delivery of a MARKED entry under `provenance: "carry"`
     *  (the structured half of the banner). Absent under "off" and for unmarked entries. */
    exposure?: "external";
    /** design/339 §3.4 — present on an `ok` delivery of a SUPERSEDED entry (mode-free, data-driven):
     *  the standing carrier entry's id (the disclosure note's structured half). Delivery proceeds —
     *  supersession is disclosure/ordering, never withholding. */
    supersededBy?: string;
    /** Present on an `ok` delivery of a SHADOWED entry: the write-home entry id whose name retires
     *  it from this session's default face (the head note's structured half). Delivery proceeds —
     *  the rule is disclosure/ordering, never withholding. */
    shadowedBy?: string;
}
/**
 * #493 件① — one row of the memory_index enumeration. The same discriminated shape law as the
 * search hits (design/336 B5): a clean row carries the text seats, a marked row (carry mode) is an
 * opaque id-only handle — a consumer must branch on `exposure` to touch the text seats. No score
 * seat: the enumeration is structural, not ranked.
 */
export interface CleanMemoryIndexRow {
    id: string;
    scope: string;
    slug: string;
    name?: string;
    description?: string;
    mtimeMs: number;
    sizeBytes: number;
    exposure?: never;
    /** Present ⇔ this row is SUPERSEDED (listed via `includeSuperseded`; the default face filters
     *  such rows out) — the value is the standing carrier entry's id. */
    supersededBy?: string;
    /** Present ⇔ this row is SHADOWED (listed via `includeShadowed`; the default face filters such
     *  rows out) — the value is the write-home entry id whose name retires it. */
    shadowedBy?: string;
}
/** The opaque marked-row handle (carry mode) — same posture as {@link ExposedMemorySearchHit}. */
export interface ExposedMemoryIndexRow {
    exposure: "external";
    id: string;
    scope: string;
    mtimeMs: number;
    sizeBytes: number;
    slug?: never;
    name?: never;
    description?: never;
    supersededBy?: string;
    shadowedBy?: string;
}
export type MemoryIndexRow = CleanMemoryIndexRow | ExposedMemoryIndexRow;
export interface MemoryIndexDetails {
    outcome: "ok" | "refused" | "failed";
    reason?: string;
    entries?: MemoryIndexRow[];
    /** Zero-based row offset this page started at (the enumeration order is deterministic). */
    offset?: number;
    /** Rows on THIS page. */
    shown?: number;
    /** Total rows the enumeration serves (after the exclusion/superseded filters). */
    total?: number;
    /** Superseded rows the default face hid (absent when zero or when `includeSuperseded` was passed). */
    supersededHidden?: number;
    /** Shadowed rows the default face hid (absent when zero or when `includeShadowed` was passed). */
    shadowedHidden?: number;
    /** Present ⇔ more rows follow — pass it as the next call's `offset`. */
    nextOffset?: number;
    /** The listing revision this page was served against (a stateless digest of the ordered id
     *  sequence the offsets address). Pass it back on continuation pages of a complete pass; a
     *  mismatch refuses with reason `listing_changed` instead of silently skipping shifted rows. */
    listingRev?: string;
}
/** Cut `text` to at most `maxBytes` UTF-8 bytes on a CODE POINT boundary (a byte-wise slice would
 *  strand half a character), reporting how many bytes were dropped. Unchanged text reports 0.
 *  Exported for the truncation-invariant registry — a bound-taking function has to be provable. */
export declare function cutToBytes(text: string, maxBytes: number): {
    text: string;
    omittedBytes: number;
};
/** Skip `startBytes` UTF-8 bytes of `text` on a code-point boundary (the intra-line cursor's seek
 *  half — {@link cutToBytes} is its cut half). A cursor landing mid-character (never minted by this
 *  module) rounds FORWARD to the character's end, so no half-character is ever emitted. */
export declare function skipBytes(text: string, startBytes: number): {
    text: string;
    skippedBytes: number;
};
export declare function createMemoryEngineTools(opts: MemoryEngineToolsOptions): ToolSpec[];
