/**
 * window/ledgerFactPins — the results the model declared FACTS, and why the
 * window keeps them.
 *
 * Pattern: One pure function over the window (no scope, no I/O, no clock),
 *          plus the two helpers that rank a standing and give a TURN one.
 * Role:    core/ layer. Feeds the refusal engine, which is what actually
 *          keeps these turns in the window (see turns.ts, `'ledger-fact'`).
 *          Twin of lastToolResult.ts, and kept apart from it for the same
 *          reason that file is kept apart from currentRequest.ts: "what has
 *          the agent just seen" and "what does the agent say it stands on"
 *          are different questions, answered by different evidence. That pin
 *          is CONTENT-BLIND — it keeps each tool's LATEST result whatever it
 *          says. This one is content-aware by the MODEL'S claim only: it
 *          keeps the turns whose results the model itself filed as `fact` in
 *          `AgentState.findingsLedger`, and reads nothing else.
 * Emits:   N/A.
 *
 * ## Why this exists
 *
 * Measured, on `bench/findings-context.mjs`. Under `slidingWindow` (keep 6,
 * twenty calls, six planted facts) the ledger PIECE carried all six facts to
 * the answer turn, but the wire carried two of them verbatim — the rest had
 * left oldest-first with the noise around them, while noise turns younger
 * than they were stayed. The window knew nothing about standing: a result
 * the model had declared a fact and a result it had declared noise were the
 * same bytes to the refusal engine, so recency decided, every time.
 *
 * The ledger already says which is which, by the model's own hand. This pin
 * reads that: **a turn the model declared a fact stays, up to a ceiling,
 * until the model re-files it or the person asks something new.** 'Noise
 * first' then follows without a second mechanism — noise, ruled-out, open
 * and undeclared turns are unpinned and leave oldest-first exactly as today,
 * and since step 3b collapsed a judged noise turn to a ticket on the wire, a
 * noise turn that outlives a fact costs bytes the wire no longer pays.
 *
 * ## What is pinned, exactly, and why it cannot grow
 *
 * One candidate per TURN whose standing is `fact`, by the LAST row the
 * ledger holds for each result (the fold takes the last, so a fact the model
 * re-files as noise is released the moment it says so). A Turn is the
 * removal unit — an assistant's call and its results leave together — so
 * the standing of a turn is its most valuable member's ({@link
 * turnStandingOf}): a parallel batch that answered one fact beside two noise
 * results is ONE turn, one slot, and the noise stays with it. Three further
 * bounds compose on top, the same three as the sibling pin:
 *
 *   • **deduped by turn** — one pin per turn however many facts it holds;
 *   • **capped** — `keepLedgerFacts` is the ceiling, applied in
 *     `planRemoval` where the keep window is known, so a fact turn already
 *     inside `keepRecentTurns` costs nothing at all;
 *   • **anchored** — nothing at or before the CURRENT REQUEST is pinnable,
 *     so a new user turn releases the whole previous loop for free.
 *
 * And a fourth the ceiling alone cannot give: a hold that has provably been
 * blocking progress stands down, on the record (`stages/window.ts ·
 * pinIsBlocking`). A fact hold NEVER exists without its ceiling and its
 * stand-down — a window full of declared facts degrades to today's recency
 * behaviour after the stand-down, and the record says why.
 *
 * ## What it deliberately gets wrong
 *
 * The pin trusts the model. A result the model called a fact and never
 * revisits is held to the ceiling whether or not it was one; a result the
 * model never judged is `undeclared`, ranks above noise so a batch with one
 * unjudged member is never filed as noise, and is NOT held. The library does
 * not read the result's text to second-guess either — a guess about content
 * is what this library refuses to make everywhere else, and the model's
 * standing is the one content-aware signal that is a claim on the record
 * rather than an inference. A heuristic that PINS bills its error every
 * iteration, which is why the cap is small, the stand-down exists, and the
 * record names what it kept.
 */
import type { LLMMessage } from '../../../provider-contract/types.js';
import type { Standing } from '../findings/types.js';
import type { Turn } from './turns.js';
/** One turn the pin holds, and what holding it costs. */
export interface LedgerFactPin {
    /**
     * Every tool result the turn holds, in wire order — not only the facts.
     * The turn is what the pin actually holds: a noise result answered in the
     * same batch as a fact stays with it, and each result's own standing is on
     * the ledger by this id. A message the batch settlement wrote (9.113.0) is
     * no result and is never listed (`findings/offer.ts` · `isResultMessage`).
     */
    readonly toolCallIds: readonly string[];
    /**
     * The tool of the newest nameable result in the turn — the name the record
     * files under `WindowObservations.pinned`. A turn is usually one tool; a
     * batch is named for its newest member, the same way the sibling pin names
     * a turn for the latest result it holds.
     */
    readonly toolName: string;
    /** Index of the turn in this iteration's segmentation. */
    readonly turnIndex: number;
    /** Index of the turn's first message in the window. */
    readonly messageIndex: number;
    /**
     * Content characters of the WHOLE turn — the assistant's call and its
     * results leave together, so the turn is what the pin actually holds.
     */
    readonly chars: number;
}
/**
 * How much a standing is worth to the window, as a number ONLY so two can be
 * compared: `fact > open > undeclared > ruled-out > noise`. Never recorded.
 *
 * `undefined` is UNDECLARED — the model said nothing about the result — and
 * it ranks above `ruled-out` and `noise` on purpose: an absent standing is
 * never defaulted to a verdict, so a batch with one unjudged member is not
 * a noise batch. It ranks below `open` because `open` is a declaration.
 */
export declare function rankStanding(standing: Standing | undefined): number;
/**
 * The standing of a TURN: its most valuable tool result's, by
 * {@link rankStanding}. A Turn is the removal unit, so this is the one
 * question the refusal engine can ask about it.
 *
 * A turn with no tool result has no standing — `undefined`, the same answer
 * as a turn whose results the model never judged. Both are "the ledger says
 * nothing that holds this turn", which is all a caller may conclude.
 *
 * A message the batch settlement wrote (9.113.0, `LLMMessage.notDispatched`)
 * is served and is no RESULT (`findings/offer.ts` · `isResultMessage`), so it
 * takes no part in the ranking: counted, it would rank as an UNDECLARED
 * result above the noise and ruled-out results the model really judged, and
 * a batch it sits in would lose the standing its results earned — and a
 * standing a model filed on its id anyway (`unknownId: true`) would hold the
 * turn for a sentence about a call that never ran.
 *
 * @param turn           the turn to judge
 * @param standingOfCall the LAST standing the ledger holds for a result, by
 *   its `toolCallId` (`foldLedger(rows).standingOf.get(id)?.standing`), or
 *   `undefined` when it holds none
 */
export declare function turnStandingOf(turn: Turn, standingOfCall: (toolCallId: string) => Standing | undefined): Standing | undefined;
/**
 * The turns whose standing is `fact`, NEWEST FIRST.
 *
 * Newest first because that is the order the ceiling is spent in: when the
 * model has declared more facts than there are slots, the ones it declared
 * most recently keep theirs.
 *
 * @param turns      the window's turn segmentation
 * @param history    the window itself, for recovering a name from the
 *   assistant call when a result does not carry one
 * @param standingOf a turn's standing, as the stage bound it
 *   (`WindowStrategyInput.standingOf`; built with {@link turnStandingOf}).
 *   Asked once per turn, newest first, and it is the ONLY thing read about
 *   the ledger here — the pin is content-aware by the model's claim alone
 * @param after      index of the CURRENT REQUEST, or `-1` when the window
 *   holds none. Nothing at or before it is pinnable: facts gathered for an
 *   earlier request are ordinary history, and this is what makes a new user
 *   turn release the whole previous loop without a line of code
 */
export declare function ledgerFactPinsOf(turns: readonly Turn[], history: readonly LLMMessage[], standingOf: (turn: Turn) => Standing | undefined, after?: number): readonly LedgerFactPin[];
