/**
 * window/turns — where a turn boundary is, and which turns may leave.
 *
 * Pattern: Pure functions over the window (no scope, no I/O, no clock).
 * Role:    core/ layer. THE refusal engine. Every window strategy — the three
 *          that ship, and any a consumer writes — decides what leaves the
 *          window by calling these functions and nothing else, because the
 *          whole safety argument of the family lives here:
 *
 *            a removal that splits an assistant's `tool_use` from its
 *            `tool_result` produces a request the vendor rejects, and a
 *            removal that swallows an unanswered question destroys the
 *            referent of the answer that has not arrived yet.
 *
 *          Strategies never import this module. `WindowStrategyInput` hands
 *          them `planRemoval` already bound to this iteration's turns and
 *          guards, so a strategy CANNOT skip the refusal rules — that is a
 *          property of the seam, not of the documentation.
 * Emits:   N/A.
 *
 * Testable on its own — see `test/core/window-turns.test.ts`.
 */
import type { LLMMessage } from '../../../provider-contract/types.js';
import type { ToolResultPin } from './lastToolResult.js';
import type { LedgerFactPin } from './ledgerFactPins.js';
import type { WindowObservations, WindowRefusal, WindowRefusalReason } from './types.js';
/**
 * One turn: a `user` / `assistant` / `system` message plus every `tool`
 * message that answers it. Tool results belong to the assistant turn that
 * requested them — that pairing is the thing a removal must never break.
 */
export interface Turn {
    /** Index of this turn in the segmentation. */
    readonly index: number;
    /** Index of the turn's FIRST message in the window. */
    readonly start: number;
    /** Number of messages in the turn. */
    readonly length: number;
    readonly messages: readonly LLMMessage[];
}
/**
 * Segment a window into turns.
 *
 * A new turn starts at any non-`tool` message; `tool` messages join the turn
 * in progress. A leading `tool` message (only reachable from a hand-built
 * history) starts its own turn rather than being silently dropped.
 */
export declare function segmentTurns(history: readonly LLMMessage[]): readonly Turn[];
/**
 * Context a removal decision needs beyond the turn itself.
 *
 * Internal: the stage builds it from scope and binds it into the
 * `planRemoval` a strategy is handed, so no strategy has to know it exists.
 */
export interface RemovalGuards {
    /** Every `toolCallId` answered anywhere in the window. */
    readonly answeredCallIds: ReadonlySet<string>;
    /**
     * Index in the window of the CURRENT REQUEST — the message this run is
     * executing (9.55.0). The turn holding it refuses with `'current-request'`.
     *
     * Resolved by `currentRequestIndexOf`, which the stage calls with the run's
     * own `scope.userMessage`. Absent (or `-1`) when the window holds no
     * identifiable request, and then this rule simply does not apply — which is
     * how a window that never had one behaves exactly as it did before the rule
     * existed.
     */
    readonly currentRequestIndex?: number;
    /** The tool call this run is paused on, when it is paused. */
    readonly pausedToolCallId?: string;
    /** True when the pause is a check-in (human consent) rather than askHuman. */
    readonly pausedCheckIn?: boolean;
    /**
     * Candidate LAST-TOOL-RESULT pins (9.57.0), newest first, from
     * `toolResultPinsOf`. An INPUT to `planRemoval`, not to `refusalFor`: the
     * ceiling can only be spent once the keep window is known, so `planRemoval`
     * admits some of these and hands `refusalFor` the answer.
     */
    readonly toolResultPins?: readonly ToolResultPin[];
    /** The ceiling on admitted pins (`keepLastToolResults`). 0 = the pin is off. */
    readonly keepLastToolResults?: number;
    /**
     * The ADMITTED pins, by turn index — what `refusalFor` actually reads.
     * Derived by `planRemoval`; a caller building guards by hand may set it
     * directly to ask `refusalFor` about one turn.
     */
    readonly pinnedTurnIndexes?: ReadonlySet<number>;
    /**
     * Candidate LEDGER-FACT pins (9.102.0), newest first, from
     * `ledgerFactPinsOf` — the turns whose results the MODEL declared facts on
     * its findings ledger. The same contract as `toolResultPins`: an input to
     * `planRemoval`, which spends the ceiling and hands `refusalFor` the
     * answer. Present only on an agent with `.findings()` — the stage never
     * resolves them otherwise, so an unarmed agent's guards are the object
     * they always were.
     */
    readonly ledgerFactPins?: readonly LedgerFactPin[];
    /** The ceiling on admitted fact pins (`keepLedgerFacts`). 0 = the hold is off. */
    readonly keepLedgerFacts?: number;
    /**
     * The ADMITTED fact pins, by turn index — the `'ledger-fact'` twin of
     * `pinnedTurnIndexes`, kept apart because the two pins have different
     * ceilings and a reader adding up what each one cost needs them apart.
     */
    readonly factPinnedTurnIndexes?: ReadonlySet<number>;
}
/** Every tool_call id that has a matching `role: 'tool'` message. */
export declare function answeredCallIds(history: readonly LLMMessage[]): ReadonlySet<string>;
/**
 * Why this turn may NOT leave the window, or `undefined` when it may.
 *
 * Order matters only for which reason gets reported first; every check is
 * independent. `paused-tool` / `pending-check-in` are separated from
 * `unresolved-tool-call` on purpose: they are the same shape but a different
 * fact about the world, and "we are waiting on a human" is what the person
 * reading the trace needs to see.
 */
export declare function refusalFor(turn: Turn, guards: RemovalGuards): WindowRefusalReason | undefined;
/** The span a removal will take, plus every refusal it had to name to get there. */
export interface RemovalPlan {
    /** First turn index in the span; -1 when nothing may be removed. */
    readonly from: number;
    /** Last turn index in the span (inclusive); -1 when nothing may be removed. */
    readonly to: number;
    readonly refusals: readonly WindowRefusal[];
    /**
     * What the last-tool-result pin did on this plan (9.57.0) — which turns it
     * held and what the ceiling turned away. Absent when it held nothing, so a
     * window with no pinnable result plans exactly as it did before.
     */
    readonly observations?: WindowObservations;
    /**
     * What the ledger-fact pin did on this plan (9.102.0) — the same shape,
     * with `limit` = `keepLedgerFacts`. Absent when it held nothing, which on
     * an agent without `.findings()` is always.
     */
    readonly ledgerFacts?: WindowObservations;
}
/**
 * Choose the removal span: the LONGEST CONTIGUOUS run of removable candidate
 * turns, starting at the oldest removable one.
 *
 * Contiguity is not fussiness — it is what keeps the conversation in order.
 * A fold replaces its span with ONE summary message; if the span skipped over
 * an unremovable turn, that turn would end up after a summary of things that
 * happened before it. So an unremovable turn at the front is stepped over (the
 * span "takes the next oldest instead") and an unremovable turn in the middle
 * ends the span. Everything not removed keeps its position.
 *
 * The drop strategies take the same span for a second reason: it is what makes
 * a refusal reason mean the same thing under every strategy. A turn that ends
 * the span this iteration is retried the next one — by which time the tool
 * result it was waiting on has usually arrived.
 *
 * @param turns             the window's turn segmentation
 * @param keepRecent        how many trailing turns are off-limits
 * @param guards            what must not leave (unanswered calls, the pause)
 * @param isExistingSummary optional: true for a turn that is a summary a prior
 *   fold wrote. When the whole span is one such turn, the plan refuses with
 *   `only-existing-summary` — re-summarizing a summary spends an LLM call to
 *   lose detail. The drop strategies omit it: a drop spends nothing, so there
 *   is nothing to protect against.
 */
export declare function planRemoval(turns: readonly Turn[], keepRecent: number, guards: RemovalGuards, isExistingSummary?: (turn: Turn) => boolean): RemovalPlan;
/** Total characters of message content in a window. Exact; not tokens. */
export declare function windowChars(history: readonly LLMMessage[]): number;
