import type { AgentTool } from "../internal/harness-types.js";
/**
 * Pluggable backing store for **offloaded large tool results** (design/30). When a tool returns more
 * text than the threshold, the full content is moved here and the conversation keeps only a preview +
 * a `ref`; the model fetches the rest via the injected `read_tool_result` tool. This is the local,
 * provider-agnostic equivalent of CC's `toolResultStorage` (no Anthropic `cache_edits` dependency).
 *
 * Default is {@link InMemoryToolResultStore} (task-scoped, lives only for the run). Inject a durable
 * implementation (TiDB/blob, keyed by the opaque `ref`) so an offloaded result survives wake/resume;
 * with the in-memory default, a fetch after a cross-process wake returns undefined and the model is told
 * the output is no longer available — the preview still stands in context, so it degrades, never crashes.
 */
export interface ToolResultStore {
    /**
     * Managed-retention declaration (ruled 2026-08-05, retention.ts): `"managed"` promises the
     * `ManagedRetentionCapability` contract (scheduled, tenant-scoped, audited deletion); `"none"` (or
     * ABSENT — read fail-closed as `"none"`) states the store cannot delete on schedule. A deployment
     * with a LOCKED retention policy refuses to start over a `"none"` store. Declaration, never
     * duck-typing.
     */
    readonly retention?: import("./retention.js").RetentionDeclaration;
    /**
     * Persist full content under a stable `ref`. **Write-once and idempotent**: if `ref` already exists,
     * keep the existing content and do nothing (the aggregate budget re-runs every query with the same
     * deterministic ref, so `put` for an already-stored ref MUST be a no-op — re-writing would risk a
     * non-identical preview and break the prompt cache). A durable backend treats `ref` as the primary key
     * and should validate/sanitize it (it is composed from sessionId+toolCallId, not raw model input, but a
     * durable store still owns key hygiene for its storage layer). The floor of that hygiene is
     * NOT per-backend — every implementation rejects the unsafe ref set via {@link assertSafeToolResultRef}
     * (a volatile backend silently accepting what a durable one rejects is the drift this closes).
     *
     * `provenance` (backlog #119, 2026-08-10) states WHO the stored bytes belong to, so a host-side read
     * face can authorize a fetch without parsing the ref. Its rules, uniform on every backend:
     *   - **the write-once election decides the owner too** — whoever wins the race to store the content
     *     is the owner, and no later writer can change that. A backend that publishes content and owner
     *     as two objects (the file one does) may, if it is interrupted between them, leave an entry
     *     content-complete and UNOWNED; that is the fail-closed direction (a read face refuses it) and it
     *     is never mis-attribution, but it does mean "unowned" is a reachable state, not only a
     *     pre-provenance one. A single-row backend (memory/SQL) has no such window;
     *   - a later `put` on the same ref carrying the **same** provenance (or none) is the usual idempotent
     *     no-op — the stored owner is neither re-written nor erased;
     *   - a later `put` carrying a **different** provenance is a typed refusal
     *     ({@link ToolResultRefConflictError}, `code: "tool_result.ref_conflict"`), never a silent no-op:
     *     `ref` is the storage PRIMARY KEY, and with a host-supplied `sessionId` two distinct owners can
     *     land on one ref — under a silent write-once the second owner would read back the first owner's
     *     bytes, which is an identity fault, not a caching detail;
     *   - provenance must never be FINER-GRAINED than the ref's own key. A ref keyed on
     *     (session, tool call) is legitimately written by two different tasks of one session — the
     *     Runner shares one store across its tasks on purpose, and a BYOM brain may mint the same
     *     tool-call id in both — so stamping a `taskId` there would turn a designed sharing case into a
     *     refusal. Every engine write site therefore states the session only (#167) — including the
     *     background-agent and monitor spills, whose ref DOES carry the registry handle id: that id is
     *     an addressing segment of the ref, not an ownership coordinate a read-face caller can present
     *     (see {@link ToolResultProvenance.taskId} for the namespace rule);
     *   - an entry stored **without** provenance (written by an older engine, or by a call site that has
     *     none) stays UNOWNED: `put` never back-fills an owner onto it (that would be adoption by a party
     *     that cannot prove it wrote it) and {@link ownerOf} keeps answering `undefined`, which a read face
     *     reads fail-closed. Back-filling such rows is an explicit migration, never a side effect of a write.
     */
    put(ref: string, content: string, provenance?: ToolResultProvenance): Promise<void> | void;
    /** Read a slice of the stored content. Unknown `ref` → undefined. */
    get(ref: string, opts?: {
        offset?: number;
        limit?: number;
    }): Promise<ToolResultSlice | undefined> | ToolResultSlice | undefined;
    /**
     * Backlog #119 — the READ-side half of provenance: who owns `ref`. `undefined` for an unknown ref AND
     * for a stored-but-unowned one (a row whose write stated no owner), which a read face treats identically:
     * fail-closed, nobody is authorized. Without this operation a `put`-only provenance parameter would be
     * write-only bookkeeping — the read face would still have nothing to decide on.
     *
     * Typed OPTIONAL so an implementation compiled against an older engine still satisfies the interface;
     * the published contract kit (`toolResultStoreContract`) nevertheless REQUIRES it, because a backend
     * that cannot answer this cannot back a host read face at all.
     */
    ownerOf?(ref: string): Promise<ToolResultProvenance | undefined> | ToolResultProvenance | undefined;
    /**
     * Erase every entry this store recorded as belonging to `sessionId` — the DELETE half of the same
     * ownership coordinate {@link ownerOf} answers on. Without it a durable backend keeps a deleted
     * session's offloaded and spilled results on its media indefinitely: `put`/`get` alone give a
     * deployment no way to complete a session deletion, and the engine ships no scheduler that would
     * eventually collect them.
     *
     * Rules every implementation follows:
     *   - **selection is by RECORDED PROVENANCE, never by parsing the ref.** A ref is an opaque handle:
     *     its session segment may be folded ({@link buildToolResultRef}), refs minted before the
     *     injective form decompose two ways (`tr_team_blue_x` is both ("team","blue_x") and
     *     ("team_blue","x")), and a caller may mint its own. Prefix-matching a ref therefore both
     *     misses rows and reaches rows of a NEIGHBOURING session, and over-deletion here destroys a
     *     live session's readable bytes. The owner recorded at the winning write is the one coordinate
     *     that means exactly one thing, and it is the same one a read face authorizes against;
     *   - **`taskId` is ignored in the match.** A stored `{sessionId, taskId}` is a NARROWING of the
     *     same session, not a different owner — write sites stamped one before the namespace rule
     *     settled the shape (#167), and such rows still belong to the session being deleted;
     *   - **an UNOWNED entry is never deleted.** A row whose write stated no provenance (an older
     *     engine, a call site that has none, or a two-object backend interrupted between publishing
     *     content and owner) belongs to no session this sweep can name, and it is still READABLE via
     *     `get` — deleting it on a guess would destroy another session's output. It is counted instead
     *     ({@link ToolResultDeletionReport.unattributable}) so the caller learns the deletion was
     *     incomplete rather than being told a clean "done";
     *   - **idempotent**: deleting a session with no entries is a no-op, and a repeat call after a
     *     partial (loudly failed) sweep resumes it;
     *   - **concurrency-tolerant**: an entry that disappears between enumeration and removal is honest
     *     absence, not an error.
     *
     * Typed OPTIONAL, and an OPTIONAL leg of the published contract kit (`toolResultStoreContract`,
     * its #196 case): unlike `ownerOf`, a backend that cannot enumerate by owner is still a usable
     * offload store, so absence is never a contract breach — the kit verifies the four-state
     * semantics when the member is present and reports absence through its `onOptionalMember`
     * callback (a checkable fact, not a failure). Present ⇒ the store can complete a session
     * deletion; absent ⇒ the deployment owns that gap.
     *
     * Implementing it does NOT make a store `retention: "managed"` — that declaration promises the whole
     * {@link import("./retention.js").ManagedRetentionCapability} (domain enumeration, tombstones,
     * audit receipts, scheduled execution), of which this is one caller-driven erase.
     */
    deleteBySession?(sessionId: string): Promise<ToolResultDeletionReport> | ToolResultDeletionReport;
}
/**
 * What a {@link ToolResultStore.deleteBySession} sweep actually did. A count, not a `void`, because the
 * sweep has a documented INCOMPLETE outcome (unowned entries it must not delete) and a caller running a
 * session-deletion face has to be able to tell "the session's results are gone" from "some rows survive
 * that no session can clear".
 */
export interface ToolResultDeletionReport {
    /** Entries removed by THIS call. A retry of an already-swept session reports `0`, not an error. */
    readonly deleted: number;
    /**
     * Entries the sweep could not attribute to ANY session and therefore left in place: stored without
     * provenance, or with an owner record that is present but unreadable. Store-wide, not
     * session-specific — an unowned row may or may not have been this session's, which is precisely why
     * it cannot be deleted — so the same figure recurs on every session's sweep until a migration or an
     * operator clears those rows.
     */
    readonly unattributable: number;
}
/**
 * Backlog #119 — WHO a stored tool result belongs to. Structured (not a single opaque string) because
 * the read face authorizes by COMPARING fields it already knows; `taskId` is present when the write site
 * has one (it is a narrowing of the session, never a substitute for it).
 *
 * A stamped `taskId` must name a coordinate the read face's CALLER presents —
 * the host's own task/run id — never an engine-internal registry handle id. A handle id lives in a
 * namespace only the engine's in-memory registry can resolve, so an owner stamped with one compares
 * unequal to every caller coordinate forever: the entry becomes readable by NOBODY across the wire
 * (fail-closed, but pointlessly — the disclosure text tells the caller to read it back). The engine's
 * own write sites all state the session only; the handle id, where a ref needs it, is an ADDRESSING
 * segment of the ref, not part of the owner.
 *
 * Deliberately NOT part of it: `toolCallId`. It is a retention/addressing coordinate, not an ownership
 * one — two different tool calls in one session are the same owner.
 */
export interface ToolResultProvenance {
    readonly sessionId: string;
    readonly taskId?: string;
}
/** Backlog #119 — the `code` on {@link ToolResultRefConflictError}, so callers branch on a constant. */
export declare const TOOL_RESULT_REF_CONFLICT_CODE = "tool_result.ref_conflict";
/**
 * Backlog #119 — a `put` whose provenance disagrees with the stored one. Typed (a `code`, like
 * `CheckpointError`/`WorkflowRunStoreError`) rather than a silent no-op: this is the one write-once
 * outcome where "keep what is there" would hand the second caller a reference to somebody else's bytes.
 */
export declare class ToolResultRefConflictError extends Error {
    readonly ref: string;
    readonly stored: ToolResultProvenance;
    readonly incoming: ToolResultProvenance;
    readonly code = "tool_result.ref_conflict";
    constructor(ref: string, stored: ToolResultProvenance, incoming: ToolResultProvenance);
}
/**
 * Backlog #119 — the ONE comparison every backend uses, so "same owner" cannot mean two things in two
 * stores. A missing PROVENANCE on either side ⇒ no verdict (an unowned entry is never adopted, and a
 * put with no provenance never disturbs a stored owner). Both present ⇒ every field must agree,
 * including `taskId`: a missing task id is a DIFFERENT owner from a stated one, not a wildcard that
 * matches it — two writers that disagree about how narrowly they own a key have not proven they are
 * the same writer. Unequal ⇒ {@link ToolResultRefConflictError}.
 */
export declare function assertToolResultProvenanceMatch(ref: string, stored: ToolResultProvenance | undefined, incoming: ToolResultProvenance | undefined): void;
/** Normalize to the stored shape: an ABSENT `taskId` and an explicit `undefined` one are one value, so
 *  round-tripping through a durable backend (NULL column, missing JSON key) compares equal. */
export declare function normalizeToolResultProvenance(p: ToolResultProvenance): ToolResultProvenance;
/**
 * The ref-safety half of the {@link ToolResultStore} contract, shared by every backend.
 *
 * Why it is central and not per-backend: the file backend has always rejected these refs (its path
 * component guard, `stores/file/fs-atomic.ts:48`), while the in-memory backend accepted them
 * silently — the SAME ref stored fine in dev and threw in a durable deployment. That is the
 * "backend outside the shared contract drifts on its own" mechanism (same family as RB-86/MB1 on
 * MailboxStore), and the honest direction is the strict one: a ref that no durable backend can key
 * is a defect at the WRITE, not something a volatile backend should quietly absorb.
 *
 * The rejected set is deliberately the intersection-unsafe one — empty / `.` / `..` / path
 * separators / control characters — i.e. refs that are unusable as a key ANYWHERE (they traverse in
 * a path-backed store and are ambiguous in every other). Backends may still be stricter about their
 * own storage layer (the file backend additionally enforces a filename charset); that extra
 * strictness is a backend capability, not part of this contract.
 *
 * Read face stays lenient by design: `get` with such a ref returns `undefined` (an unknown ref), it
 * does NOT throw — the model is told the output is gone and the run degrades instead of crashing.
 *
 * RB-273 amends one clause of the above: a backend may be stricter about its own STORAGE LAYER, but not
 * about which refs it accepts. A ref this function accepts must round-trip on every backend — a backend
 * whose native key space is narrower (the file one's filename charset) owns an injective ENCODING into
 * that space, not a rejection. Otherwise the same "dev green / deployment red" split this rule closed
 * for `..` simply reappears one charset over (it did: a `:` in a provider-minted toolCallId).
 */
export declare function assertSafeToolResultRef(ref: string): void;
/**
 * The SINGLE mint point for a tool-result `ref`. Three call sites compose the same
 * `tr_<sessionId>~<toolCallId>` string (`withToolResultOffload` here, the aggregate budget's preview
 * pass, and the runner's clear-with-offload persist); this is that string's one definition.
 *
 * Why it must sanitize: `sessionId` is engine-minted, but **`toolCallId` is PROVIDER-minted** — it is
 * whatever the model gateway put in the tool-call frame, not something core controls. Providers ship
 * ids with `:` in them, and nothing stops one shipping a `/`. Composed raw, that produced two distinct
 * failures, both far from their cause:
 *   - `/` or a control char ⇒ the ref violates {@link assertSafeToolResultRef} ⇒ `put` THROWS at mint,
 *     i.e. an offload (a context-economy optimization) turns a working tool call into a failed one;
 *   - `:` ⇒ accepted by memory/pg, rejected by the file backend's filename charset ⇒ the classic
 *     dev-green/deployment-red split (`probe-r5-store-parity-matrix`'s last two divergent cells).
 *
 * The mapping is IDENTITY for every id already in the native charset — which is all of them in practice
 * (`toolu_…`, `call_…`, uuids) — so refs minted before this existed are byte-identical afterwards and
 * the prompt-cache stability the preview rests on is untouched. Only an out-of-charset (or absurdly
 * long) segment is folded, and then INJECTIVELY: readable base + a sha256 of the RAW segment, the same
 * construction (and the same reason: two distinct ids must never collide onto one ref, which would
 * cross-serve one tool's output as another's) as `sanitizeScope` in the file store.
 *
 * Read-back of refs minted BEFORE this change is unaffected in both directions: safe ids are unchanged,
 * and an unsafe one only ever lived in a memory/pg deployment, where the ref the model reads back comes
 * from the `<persisted-output ref="…">` text already in its context — the read path does not re-mint,
 * and `get` is keyed on that literal string.
 *
 * ── Backlog #119 (2026-08-10): the composition is now INJECTIVE ───────────────────────────────────
 * It was not. With `_` between the segments — a character legal INSIDE both — `tr_team_blue_x`
 * decomposed as ("team","blue_x") and as ("team_blue","x"), i.e. two distinct (session, tool call)
 * pairs minted one ref. `ref` is the store's PRIMARY KEY under a write-once contract, so the second
 * pair did not get an error, it got the FIRST pair's bytes back — and `sessionId` is host-supplied in a
 * BYOM deployment, so the two pairs need not be the same tenant. (Measured on the published artifact:
 * ("s","1_a_b") and ("s_1","a_b") produced the same ref.)
 *
 * The separator is now {@link REF_SEGMENT_SEPARATOR} — a character OUTSIDE {@link NATIVE_REF_CHARSET}.
 * `refSegment` emits only characters from that charset (identity branch by test; fold branch by
 * construction: a readable base with every out-of-charset char replaced, plus hex), so the separator
 * can never occur inside a segment and `tr_<seg>~<seg>` decomposes exactly one way. Injectivity of the
 * whole map then follows from `refSegment`'s own injectivity per segment.
 *
 * Why this is NOT a breaking change, spelled out because it looks like one:
 *   - refs are OPAQUE HANDLES. A ref reaches the model inside the preview text and comes back verbatim
 *     to `ReadToolResult`; `get` is keyed on that literal string and no read path re-mints;
 *   - so an entry written under the old form stays readable forever: an old ref in an old context reads
 *     back byte-for-byte, on every backend (the file backend's filename encoding is likewise keyed on
 *     the literal ref, and is identity for the old form);
 *   - the mint is deterministic per process, so a ref MINTED here is also RESOLVED here — there is no
 *     window in which one process writes `~` and another expects `_` for the same pair. The upgrade
 *     boundary is a process restart, and it needs no double-read window;
 *   - the only observable change is the ref STRING for newly offloaded results, which no wire contract
 *     pins (it is generated text, and the length ceiling downstream validators apply is unchanged).
 * The store-key charset is unchanged as far as the shared rule is concerned ({@link assertSafeToolResultRef}
 * accepts `~`: it is not empty/dot/separator/control). A backend whose NATIVE key space excludes `~`
 * (the file one's filename charset) already owns an injective encoding into it — the RB-273 rule — so
 * for that backend this simply moves engine refs from the identity branch to the digest branch.
 *
 * `extraSegments` (also #119) is how a call site that addresses something FINER than one tool call —
 * a `details` member's JSON path, a stale-projection content digest — keeps that coordinate in its own
 * segment instead of splicing it into the `toolCallId` one. Splicing was the ticket's other residue:
 * a ref built from the compound `"<callId> <path>"` is exactly the ref another call would mint if its
 * provider-assigned toolCallId happened to BE that whole string. With the coordinate in its own
 * segment the two can no longer coincide — the separator cannot occur inside a segment, so refs with
 * different segment COUNTS are always distinct, and same-count refs are distinct unless every segment
 * matches.
 */
export declare function buildToolResultRef(sessionId: string, toolCallId: string, ...extraSegments: string[]): string;
/**
 * Backlog #119 — the ceiling on a ref this module can mint, so a durable backend can size its key
 * column from a stated invariant instead of a guess (TiDB's was `VARCHAR(191)`, which a long
 * three-segment ref overruns — a truncating key aliases distinct refs).
 *
 * Derivation: `"tr_"` (3) + four segments at their own ceiling + three separators. A segment is either
 * an identity one (≤ {@link MAX_REF_SEGMENT_CHARS}) or a folded one (marker + ≤32 base + `-` + 64 hex
 * = 102), so {@link MAX_REF_SEGMENT_CHARS} is the larger. Four segments is the engine's own widest
 * shape (session + tool call + a `details` path + the content coordinate); `buildToolResultRef` is
 * variadic, so a caller that passes more owns the wider bound.
 */
export declare const MAX_MINTED_TOOL_RESULT_REF_CHARS: number;
/**
 * Backlog #119 (falsification review, round 2) — the CONTENT coordinate of an offload ref.
 *
 * A tool-call id carries no cross-turn uniqueness contract: a BYOM brain legitimately mints recurring
 * ids like `call-1`, and the Runner shares one store across a session's tasks on purpose. Keyed on
 * (session, tool call) alone, the second result under a recurring id hits a write-once no-op — a
 * SUCCESSFUL put that stored nothing — while its own preview goes out naming that ref, so paging it
 * back returns the FIRST result's bytes. Provenance cannot see this (both writers are the same
 * session, and they legitimately are), so the ref itself has to carry content identity.
 *
 * 128 bits, the same width and the same reasoning as the stale-result projection that closed this in
 * its own lane earlier: tool results are untrusted text (web content rides them), and a 48-bit
 * truncation is birthday-collidable at ~2^24 work — a deliberate collision plus a recurring id would
 * re-open exactly the cross-serving this closes. Deterministic, so a re-derived ref for identical
 * content is byte-identical and the preview stays prompt-cache stable.
 */
export declare function toolResultContentSegment(text: string): string;
export interface ToolResultSlice {
    content: string;
    /** Byte/char offset this slice starts at. */
    offset: number;
    /** Total chars of the full stored content. */
    totalChars: number;
}
/** Default in-memory store (no cross-process durability); write-once per ref.
 *
 *  Field-reported (`ReadToolResult(ref)` came back empty for a workflow child's
 *  offloaded output): the Runner now shares ONE instance across its tasks (instead of a per-task
 *  island), so a parent can deref a child's ref within the process. A shared long-lived instance
 *  needs a bound — `maxTotalChars` FIFO-evicts the oldest refs past the cap (an evicted deref lands
 *  on the existing honest "may have expired" text). Durability across restarts still requires a real
 *  store (e.g. FileToolResultStore) via `RunnerDeps.toolResultStore`. */
export declare class InMemoryToolResultStore implements ToolResultStore {
    private readonly opts?;
    /** put/get only — no scheduled deletion contract: honestly `"none"` (retention.ts). */
    readonly retention: "none";
    /** The entry is content AND owner as ONE value — the write-once election that decides the
     *  content decides the owner in the same step (no window where an entry exists ownerless). */
    private readonly map;
    private totalChars;
    constructor(opts?: {
        maxTotalChars?: number;
    } | undefined);
    put(ref: string, content: string, provenance?: ToolResultProvenance): void;
    /** The owner recorded at the winning write; `undefined` for unknown AND for unowned entries,
     *  which a read face treats identically (fail-closed). */
    ownerOf(ref: string): ToolResultProvenance | undefined;
    /** Erase this session's entries, selected on the owner recorded at the winning write (the interface
     *  states the rules). Implemented here as well as on the durable backends so the bundled pair does
     *  not answer the same operation two ways: the entry holds content and owner as ONE value, so the
     *  same provenance match applies with none of the file backend's two-object bookkeeping. Deleting
     *  from a Map while iterating it is defined (a removed key is simply not revisited). */
    deleteBySession(sessionId: string): ToolResultDeletionReport;
    /** design/80 D-2: true when NOTHING has been offloaded — a durable suspend can then proceed safely even on
     *  this in-memory store, because a cross-replica resume has no offloaded result to deref to null. */
    isEmpty(): boolean;
    get(ref: string, opts?: {
        offset?: number;
        limit?: number;
    }): ToolResultSlice | undefined;
}
/** Marker subclass for the Runner's own shared fallback instance (no injected store). prepare-task
 *  detects it and wraps a per-task {@link ScopedToolResultStore} around it — an INJECTED store is
 *  never wrapped (a multi-tenant deployment that injects a durable store owns its own scoping). */
export declare class RunnerSharedToolResultStore extends InMemoryToolResultStore {
}
/**
 * Per-task view over the Runner-shared fallback (impl-review 2026-07-03):
 * 1. **Tenant isolation**: refs are namespaced by the task's trust scope (principal), so tenant B's
 *    `ReadToolResult` can never deref tenant A's ref — the per-task-instance era made this isolation
 *    implicit; sharing one instance re-opened it. Same-scope cross-task deref (the A2 fix's whole
 *    point) still works: same scope ⇒ same namespace.
 * 2. **D-2 durable-suspend gate fidelity**: `isEmpty()` reflects only THIS task's offloads (local put
 *    count) — matching the per-task-instance semantics the gate was written against — so an unrelated
 *    prior task's offloaded entry can no longer disable durable suspend for a task that offloaded
 *    nothing (prepare-task's fail-fast reads `isEmpty()`).
 */
export declare class ScopedToolResultStore implements ToolResultStore {
    private readonly inner;
    private readonly scope;
    private localPuts;
    /** True when the underlying shared store is process-memory (volatile across replicas) — the D-2
     *  suspend gate treats a NON-empty volatile store as unsafe to durably suspend. */
    readonly volatileBacking: boolean;
    constructor(inner: ToolResultStore, scope: string);
    /** Length-prefixed namespace — unambiguous for any scope string (no delimiter-injection ambiguity).
     *  The scope segment is ENCODED (injective, and its output charset carries no path separator
     *  or control character), so a deployment whose trust scope contains one cannot compose a key the
     *  inner store must reject — the ref-safety rule then applies to the caller's ref alone. */
    private key;
    put(ref: string, content: string, provenance?: ToolResultProvenance): Promise<void> | void;
    get(ref: string, opts?: {
        offset?: number;
        limit?: number;
    }): Promise<ToolResultSlice | undefined> | ToolResultSlice | undefined;
    /** Same namespacing as {@link get}. `undefined` when the inner store predates the operation
     *  (an unowned answer, which the read face reads fail-closed — never a fabricated owner). */
    ownerOf(ref: string): Promise<ToolResultProvenance | undefined> | ToolResultProvenance | undefined;
    /** THIS task's offload count only — the D-2 gate's per-task semantics (see class doc). */
    isEmpty(): boolean;
}
/** True when a durable suspend would lose offloaded results held only in process memory (D-2 gate). */
export declare function isVolatileOffloadStore(store: ToolResultStore): boolean;
/** Reserved name of the injected large-result reader (design/108 PascalCase: `read_tool_result`→`ReadToolResult`). */
export declare const OFFLOAD_TOOL_NAME = "ReadToolResult";
/**
 * The runner's clear-with-offload persist, as ONE construction point (the closure used to
 * live inline in prepare-task). Fire-and-forget by design (the caller returns the ref synchronously).
 *
 * A failed put REPORTS the loss, it never writes under the ref. The previous arm
 * wrote a lost-marker row under the SAME ref "so the page-back face reports the true cause", and that
 * one write carried two defects:
 *   - **revival**: the put and a session purge can race. When the put loses, the marker write lands
 *     AFTER the purge — re-inserting a row into a session the deployment just deleted, alive until the
 *     backend's own TTL. A failure arm must not be able to out-write a deletion;
 *   - **retry contamination**: the per-request re-clear legitimately re-invokes this persist with the same
 *     (toolCallId, fullText) — the write-once retry path. With a marker occupying the ref, that
 *     retry's put of the REAL bytes hit the write-once no-op (same owner ⇒ keep what is there), so a
 *     transient first failure served the marker forever even though the full text was still in hand.
 * Now the failure arm writes NOTHING (a later persist call retries the real bytes against an empty
 * ref) and announces the failed write loudly instead: through the deployment's structured notice sink
 * when one is wired, else `console.warn` (the loud-bad-value announcement dialect — a silent catch
 * here would be a silent data loss). The announcement claims only what this arm can know: THIS
 * attempt stored nothing — the same (toolCallId, fullText) persist recurs per request (write-once
 * idempotent re-put), so an EARLIER attempt may already have landed the row and a rejected re-put is
 * then no loss at all; declaring "the ref is empty" here would fabricate a data-loss incident on a
 * transient outage. Page-back of a ref no attempt ever landed serves the generic miss, which is
 * honest: nothing is stored under it.
 */
export declare function createOffloadPersist(store: ToolResultStore, sessionId: string, 
/** Structured sink for the loss announcement, delivered through the shared guarded form
 *  (`deliverEngineNotice`, #170): a wired FUNCTION seat REPLACES the console line, swallow-guarded
 *  (a throwing/rejecting sink never turns an announcement into a failure); a present NON-function
 *  seat falls back to `console.warn` plus the once-per-process seat-defect announcement; absent ⇒
 *  `console.warn`. */
onNotice?: (notice: import("./types.js").EngineNotice) => void): (toolCallId: string, fullText: string) => string;
/** Backlog #119 — build the provenance a write site records, from the two coordinates every write site
 *  already has in hand. `taskId` is omitted (not `undefined`-valued) when the run declares none, so the
 *  stored shape compares equal across a durable round-trip. */
export declare function toolResultProvenanceOf(sessionId: string, taskId?: string): ToolResultProvenance;
/**
 * RB-374① — the ONE source of the "page the offloaded text back" suggestion sentence, shared by
 * {@link buildPreview} (offload preview tail) and context-edit's cleared-marker refNote. The two
 * used to hard-code the same advice in independently drifting wordings, and neither knew whether
 * the reader tool is even CALLABLE in the current session: with {@link OFFLOAD_TOOL_NAME} deferred
 * (a design/36 placeholder), the suggestion was a self-referential dead end — the model follows it
 * and the placeholder rejects with "not active yet".
 *
 * `reachableTools` is OPTIONAL: omitted ⇒ byte-identical to the historic sentences (the
 * determinism/prompt-cache contract of both call sites rests on this default — notably the
 * per-request aggregate-budget pass, which re-derives previews every query and therefore must
 * never receive a session-varying set). Passed and missing {@link OFFLOAD_TOOL_NAME} ⇒ the honest
 * two-step form (activate via ToolSearch first — wording kept close to tool-disclosure.ts's
 * placeholder hint). The `core` fragment below is deliberately shared by both forms so the two
 * faces cannot drift apart again (a wording change moves both, and both pins move with it).
 */
export declare function offloadPagebackHint(ref: string, form: "preview" | "cleared", reachableTools?: ReadonlySet<string>): string;
/** Leading marker of an offload preview (see {@link buildPreview}) — single source so the aggregate
 *  budget (design/64 §17.2) can recognize an already-offloaded result without re-deriving the string. */
export declare const PERSISTED_OUTPUT_PREFIX = "<persisted-output ref=";
/** Default per-result offload threshold (chars). ~5K tokens; configurable globally or per tool. */
export declare const DEFAULT_TOOL_RESULT_THRESHOLD_CHARS = 20000;
/** Head+tail preview of an offloaded result, pointing at {@link OFFLOAD_TOOL_NAME} via `ref`. Exported so
 *  the per-message aggregate budget (design/64 §17.2) produces the SAME preview format (deterministic →
 *  byte-identical across queries → prompt-cache safe).
 *
 *  The truncation line states WHERE the rest is, not what anyone can do with it.
 *  "read more" alone reads as a promise that the full output is retrievable, and the model relays that
 *  promise to the person it is talking to; what is actually guaranteed is narrower and exactly two
 *  things: the text is retained in this run's tool-result store, and THIS chain can page it back. A
 *  host-side read face (an HTTP route that hands the user the whole output) is a separate deployment
 *  feature that may or may not exist — so the line names it as a deployment fact instead of implying
 *  it, and the model can answer "can I get the full log?" without guessing. */
export declare function buildPreview(full: string, ref: string, sizes?: {
    head: number;
    tail: number;
}, reachableTools?: ReadonlySet<string>): string;
/**
 * Wrap an {@link AgentTool} so that an oversized text result is offloaded to `store` and replaced with a
 * preview + `ref` before it ever reaches the session/model. `ref = tr_<sessionId>~<toolCallId>` is stable and the
 * store is write-once: a **same-process** replay keeps an identical (cacheable) preview; a **cross-process**
 * wake needs a durable store (the default in-memory store loses the full text — the preview still stands).
 * Image blocks are left untouched; only text is offloaded. Forwards the `onUpdate` progress callback so a
 * wrapped (e.g. MCP) streaming tool isn't broken.
 *
 * Backlog #175 — a failing store NEVER turns the wrapped tool's success into a failure: the content face
 * falls back to the original (un-offloaded) result, and the `details` walk to a ref-free notice per lost
 * member (see the two arms for why the degrades differ). The wrapper adds a storage step to a tool that
 * has already run; it must not be able to invalidate that run.
 */
export declare function withToolResultOffload(tool: AgentTool, store: ToolResultStore, thresholdChars: number, sessionId: string, 
/** RB-374① — LIVE accessor for the session's currently CALLABLE tool set (evaluated per offload,
 *  so a mid-run ToolSearch activation is reflected by the next preview). Absent/`undefined`-returning
 *  ⇒ byte-identical historic preview. An accessor (not a snapshot) because the wrap happens at
 *  prepare time, before the deferred classification exists and before any activation can. */
reachableTools?: () => ReadonlySet<string> | undefined): AgentTool;
/** The machine-recognizable start of an offloaded-detail replacement notice. Exported for consumers
 *  that treat a `details` string as SEMANTIC INPUT (not display) — e.g. the session-continuation
 *  read-state replay — so they can tell "this member was offloaded, the bytes here are a preview"
 *  and degrade honestly instead of consuming preview bytes as the real value. */
export declare const OFFLOADED_DETAIL_NOTICE_PREFIX = "\u2026[offloaded \u2014 ";
/** True when `s` carries an offloaded-detail replacement notice (the newline-anchored prefix form the
 *  walk emits). A semantic consumer treats such a string as NOT the member's real bytes. */
export declare function isOffloadedDetailReplacement(s: string): boolean;
/** The injected `read_tool_result` tool: pages through an offloaded result. `effect:"read"` (verifier/reconcile-safe). */
export declare function createReadToolResultTool(store: ToolResultStore): AgentTool;
