import type { TaskResult } from "../core/types.js";
/**
 * Shared CUMULATIVE-STATS constructor for the multi-leg agent compositions (cascade rungs, repair-loop
 * attempts, verify legs). Each runs `runner.runTask` MORE THAN ONCE and must report the OPERATION's totals,
 * not the last leg's.
 *
 * The invariant this exists to hold in ONE place: **every field the accumulator tracks is OVERRIDDEN over
 * the base stats**. A field left un-overridden silently leaks the LAST leg's value dressed up as a
 * cumulative total (the cascade R1-m5 / R3 trap; repair-loop shipped exactly that hole on `humanReview`).
 * Independent copies of this literal is how that hole survived in one copy while the others were fixed —
 * hence one constructor.
 *
 * OWN and NESTED stay DISJOINT (the cascade R-5 convention): a consumer adds
 * `stats.X + stats.nested.X` for the true total; nested cost is never folded into `costMicroUsd`.
 */
/** Cumulative delegated-subagent (nested) totals, summed across every leg. */
export interface CumulativeNestedTotals {
    tokens: number;
    turns: number;
    tasks: number;
    costMicroUsd: number;
}
/**
 * Cumulative human-review (design/91 C2 axis) burden, summed across every leg. `gates` is the ordered
 * concatenation of each leg's gate entries — the element shape is the canonical one published on
 * {@link TaskResult}, so a leg's entry passes through with `toolName`/`toolArg` intact.
 */
export interface CumulativeHumanReviewTotals {
    count: number;
    totalWaitMs: number;
    gates: NonNullable<TaskResult["stats"]["humanReview"]>["gates"];
}
/**
 * The running totals a multi-leg composition accumulates. Every field is REQUIRED: an optional field here
 * would re-open the exact "forgot to override one" hole this module exists to close — a caller that has
 * nothing to report passes zeros.
 */
export interface CumulativeStatsAccumulator {
    tokens: number;
    turns: number;
    promptTokens: number;
    /** RB-457-a: the cache-INCLUSIVE input total — its own leg, no longer derivable from `promptTokens` (the
     *  cache-MISS sum). Also the cache-hit-rate denominator. */
    totalInputTokens: number;
    cachedTokens: number;
    outputTokens: number;
    cacheWriteTokens: number;
    cacheWriteTokensLong: number;
    /**
     * Cumulative own LLM cost (micro-USD): a plain `0` when every leg was priced and cost nothing measurable.
     *
     * Ignored entirely when {@link anyLegUnpriced} is set — see there.
     */
    costMicroUsd: number;
    /**
     * TRUE when ANY contributing leg published no `costMicroUsd`, i.e. it ran on a model with neither a
     * `RunnerDeps.pricing` entry nor a `Model.cost` declaration (`stats.costMicroUsd` is contractually
     * ABSENT there — types.ts / RB-368).
     *
     * Accumulating such a leg as `?? 0` publishes a total that is not one: the operation's real cost is
     * unknown, and reporting the priced legs' partial sum makes "no price table" indistinguishable from
     * "declared free" — the exact conflation the single-leg face refuses. So the knownness travels with the
     * money: when this is set, `costMicroUsd` AND `costBreakdown` are both omitted from the cumulative
     * stats, matching what a single unpriced `runTask` already reports. Token/turn totals are unaffected
     * (they are knowable regardless of pricing).
     */
    anyLegUnpriced: boolean;
    /** Cumulative root-agent LLM cost for the `costBreakdown` taxonomy. */
    llmRootMicroUsd: number;
    /** Cumulative within-task compaction LLM cost for the `costBreakdown` taxonomy. */
    compactionMicroUsd: number;
    /** Cumulative side-call (permission classifier, mods) LLM cost — the "of which" line of `costBreakdown`, INSIDE
     *  `llmRootMicroUsd`, never a partition member. Undefined until a leg booked one: the cumulative key is then absent too. */
    sideCallMicroUsd?: number;
    nested: CumulativeNestedTotals;
    humanReview: CumulativeHumanReviewTotals;
}
/**
 * Build the cumulative stats over a base (the FINAL leg's stats — it carries the non-accumulated fields
 * such as `model`) with every accumulated field overridden.
 *
 * `cacheHitRate` is RE-DERIVED from the cumulative cached/total-input totals rather than spread from the
 * base: a ratio is not additive, so the last leg's rate is never the operation's rate.
 */
export declare function buildCumulativeStats(base: TaskResult["stats"], acc: CumulativeStatsAccumulator): TaskResult["stats"];
/** A running {@link CumulativeStatsAccumulator} with the per-leg fold built in. */
export interface CumulativeStatsTracker {
    /** Fold ONE leg's stats into the running totals. Call once per leg, in dispatch order. */
    add(s: TaskResult["stats"]): void;
    /** Build the cumulative stats over `base` — conventionally the FINAL leg's stats (it carries the
     *  non-accumulated fields such as `model`). */
    build(base: TaskResult["stats"]): TaskResult["stats"];
    /** How many legs have been folded in. A composition that ran exactly ONE leg can use this to hand that
     *  leg's own stats back untouched: the cumulative account over one leg IS that leg, and re-deriving it
     *  would rewrite a caller's object (0 → `undefined` normalization, a re-derived `cacheHitRate`) for no
     *  gain. */
    readonly legs: number;
}
/**
 * Create a {@link CumulativeStatsTracker}.
 *
 * {@link buildCumulativeStats} made the OUTPUT literal single-source, but the INPUT fold stayed copied:
 * cascade / repair-loop each hand-maintain the same dozen `+=` lines, so a new stats field still has to be
 * remembered in three places — the same duplication one level down from the hole this module exists to
 * close. New call sites take this factory instead. Migrating the two originals onto it is a separate change
 * (their folds are equivalent to this one today, but rewriting shipped compositions is not part of a
 * cost-accounting bug fix).
 *
 * OWN and NESTED stay DISJOINT here too: `add` never folds a leg's `nested.*` into the own totals.
 */
export declare function createCumulativeStatsTracker(): CumulativeStatsTracker;
