import type { AgentMessage } from "../internal/harness-types.js";
import { type ToolResultStore } from "./tool-result-store.js";
/**
 * Per-message aggregate tool-result budget (design/64 §17.2 / §24 — CC `MAX_TOOL_RESULTS_PER_MESSAGE_CHARS`,
 * `constants/toolLimits.ts:49`). A single turn's batch of N parallel tool results can each be under the
 * per-tool offload threshold yet SUM past the model/API limit (10×40K = 400K). This caps the aggregate of
 * one turn's tool_result batch: while over budget, the LARGEST fresh non-exempt result is offloaded to a
 * preview (largest-first), repeating until the batch fits.
 *
 * **Simpler than CC by construction (§24.2):** a request-only, non-destructive transform (returns a new
 * array; the durable session keeps full results) applied in the existing `harness.on("context")` hook —
 * the same per-query point as `clearStaleToolResults` (the mirror of CC's query.ts:379). It is DETERMINISTIC
 * (stable `ref = tr_<sessionId>~<toolCallId>` + deterministic {@link buildPreview}), so re-running it every
 * query yields byte-identical previews → prompt-cache safe **without** CC's ContentReplacementState freeze
 * machine (the determinism IS the freeze), and resume-free (the transcript holds originals; this re-applies
 * on replay). Reuses design/30's offload store + preview format; with no store it falls back to a
 * self-contained head+tail truncation preview that still bounds the request.
 */
/** CC's per-message cap (chars, not tokens — the "100K token" figure in old notes was unsourced). */
export declare const AGGREGATE_TOOL_RESULT_BUDGET_CHARS = 200000;
export interface AggregateBudgetOptions {
    /** Per-batch char budget. Default {@link AGGREGATE_TOOL_RESULT_BUDGET_CHARS}. */
    budgetChars?: number;
    /** Tool names exempt from the budget (their own caps bound them) — e.g. read_file, read_tool_result. */
    exemptTools?: ReadonlySet<string>;
    /** The wire name a result's recorded tool name resolves to on the leg's roster (an exemption is judged on the tool the
     *  call ran, not the spelling the model used). Absent ⇒ the recorded name. */
    toolNameOf?: (recorded: string) => string;
    /** design/30 store for offload+read-back. Absent ⇒ self-contained truncation preview. */
    store?: ToolResultStore;
    /** Namespaces the stable ref so an offloaded result is globally addressable. */
    sessionId: string;
    /** C8: fired once per result actually capped (offloaded OR degraded to a truncation
     *  preview). `storeFallback` = the model LOST read-back for this result (store.put failed / no store)
     *  — the silent-degradation face this callback exists for. Must not throw (caller-side contract). */
    onCapped?: (info: {
        tool?: string;
        sizeChars: number;
        storeFallback: boolean;
    }) => void;
    /** Fired once per batch that is STILL over budget after both passes — i.e. the cap did what it
     *  could and the request is going out oversized anyway. Reachable when a batch holds more results than
     *  the budget can hold even at the minimum preview size (a compact preview has a floor, and N of them
     *  cannot be smaller than N × that floor). Before this existed the shortfall was entirely silent, so a
     *  caller could not tell "capped and fits" from "capped and still 2× over". Must not throw. */
    onBudgetUnreachable?: (info: {
        batchChars: number;
        budgetChars: number;
        results: number;
    }) => void;
}
/** Cap each over-budget turn-batch (run of consecutive tool_result messages). Request-only; see file docs. */
export declare function capAggregateToolResults(messages: AgentMessage[], opts: AggregateBudgetOptions): Promise<AgentMessage[]>;
