/**
 * toolResultCap — the opt-in ceiling on what ONE tool result may cost.
 *
 * Pattern: a measured replacement at the dispatch boundary. Not a transform of
 *          the tool's value (the tool still returns whatever it returns), and
 *          not a policy about tools — a last-resort net on the ONE number a
 *          context window cannot survive: characters.
 * Role:    consumed by the tool-dispatch loop (`stages/toolCalls.ts`) on every
 *          path that turns a tool result into a `role: 'tool'` message. The
 *          one `role: 'tool'` message it never measures is the batch
 *          settlement's (9.113.0): that is the library's own fixed sentence
 *          for a call that never ran, not a tool result (`stages/README.md`).
 * Emits:   nothing. The truncation is visible because the marker IS the result
 *          — `agentfootprint.stream.tool_end` carries it, and so does history.
 *
 * ── Why this is opt-in, and why it will stay opt-in ──────────────────────────
 * A default here would silently modify tool results. A tool that returns 200KB
 * of rows is doing what somebody wrote it to do, and a framework that quietly
 * replaced that with a stub the first time it ran would be lying to the app
 * about its own tool. So: no `maxToolResultChars`, no measurement, no marker,
 * byte-identical behaviour. Set it and every result on every dispatch path is
 * measured against it.
 *
 * ── What the model is handed, and why it is shaped like a lesson ─────────────
 * The failure this exists for is not "the result was big". It is "the result
 * was big, the window blew, and the model never learned that asking for less
 * was an option". A bare truncation teaches nothing — the model reads a
 * sentence that stops mid-word and assumes the data ends there. The marker says
 * what happened, how far over the cap it was, and what to do next:
 *
 *   { truncated: true,
 *     reason: "orders_export returned 812431 chars, over the 20000-char cap.
 *              Narrow the request and call again.",
 *     head: "<the first N characters, verbatim>" }
 *
 * ONE shape, always the object, for a string result and an object result alike
 * — a sink can branch on `.truncated` without parsing prose, and the model
 * reads it as JSON on the `role: 'tool'` message. `head` is verbatim: it is the
 * only part of the answer that survived, and paraphrasing it would make the
 * marker the second thing in the transcript that cannot be trusted.
 *
 * ── The head budget ─────────────────────────────────────────────────────────
 * `head` gets whatever the cap has left after the envelope that explains it —
 * so the serialized marker stays within the cap, and a bigger cap buys a
 * proportionally bigger head. When the cap is smaller than its own explanation,
 * `head` is absent and the marker is the whole result: an explanation that had
 * to be truncated to fit under a cap would teach nothing, which is the failure
 * this module exists to prevent.
 */
/**
 * The result a capped dispatch hands on — the marker IS the result.
 *
 * Reaches the model as JSON on the `role: 'tool'` message, and reaches
 * `agentfootprint.stream.tool_end` as this object.
 */
export interface TruncatedToolResult {
    /** Always `true`. The field a consumer branches on. */
    readonly truncated: true;
    /**
     * What happened, in the model's own reading order: which tool, how big, what
     * the cap was, and the one action that helps. Never carries the tool's
     * arguments or the omitted content.
     */
    readonly reason: string;
    /**
     * The first characters of the real result, verbatim. Absent when the cap is
     * too small to afford any — see the head budget note above.
     */
    readonly head?: string;
}
/** Type guard for consumers reading `tool_end.result` or a tool message. */
export declare function isTruncatedToolResult(value: unknown): value is TruncatedToolResult;
/**
 * The refusal for a cap that cannot cap anything.
 *
 * `0` and negatives are not "disable it" — omitting the option is. A cap of `0`
 * would replace every result with a marker that has no room for a head, which
 * is a working agent turned into a wall of refusals; a negative one has no
 * reading at all. Refused where it is configured, naming the value, rather than
 * at the first tool call of the first run.
 */
export declare function assertMaxToolResultChars(site: string, value: number | undefined): void;
/**
 * Measure one tool result against the cap and, when it is over, replace it.
 *
 * Returns the ORIGINAL value by reference when the cap is absent or the result
 * fits — so an under-cap dispatch is byte-identical, not a round-trip through
 * `JSON.parse(JSON.stringify(...))` that would quietly re-shape it.
 */
export declare function capToolResult(value: unknown, opts: {
    readonly toolName: string;
    readonly maxChars?: number;
}): {
    readonly value: unknown;
    readonly truncated: boolean;
};
