/**
 * delivery/rules — where a delivered injection may go, and whether it is
 * already there.
 *
 * Pattern: Pure functions over the window (no scope, no I/O, no provider).
 * Role:    core/ layer. The counterpart to `window/turns.ts`: that module
 *          decides what may LEAVE the window, this one decides what may
 *          ENTER it. Both answer with a refusal that names itself, and both
 *          exist so the rule lives in exactly one place instead of being
 *          re-derived at each call site.
 * Emits:   N/A.
 *
 * ── The one rule worth arguing about ─────────────────────────────────
 * Collision is judged on the EFFECTIVE wire role, where `'tool'` counts as
 * `'user'`. That is deliberately more conservative than any single provider
 * requires: Anthropic coalesces `tool_result`s into a user turn (so a user
 * message after tool results breaks its strict alternation), while OpenAI
 * keeps `role: 'tool'` and would accept one. Judging per-provider would make
 * the SAME declaration deliver on one wire and defer on another, with
 * nothing in the recording to tell them apart — which is the exact
 * provider-dependent truth this whole feature exists to avoid. So the floor
 * is the rule, uniformly.
 *
 * The consequence is real and stated rather than hidden: inside a tool-using
 * agent loop the window ends on the user's turn (iteration 1) or on tool
 * results (every iteration after), so a `'user'`-role injection typically
 * never gets a slot. `'assistant'` does, and `'system'` does on the providers
 * that carry it.
 *
 * Testable on its own — see `test/core/delivery/delivery-rules.test.ts`.
 */
import type { LLMMessage, WireRole } from '../../../provider-contract/types.js';
import type { DeferralReason } from '../../../injection-engine/messagesSlotRefusal.js';
/**
 * The role a message OCCUPIES on the wire, for sequencing purposes.
 *
 * A `tool` message is folded into `user` because that is what the strictest
 * provider does with it. Everything else is itself.
 */
export declare function effectiveWireRole(role: LLMMessage['role']): WireRole;
/**
 * The stable identity of one deliverable message: who declared it, and what
 * it says.
 *
 * Position within the injection's own list is deliberately NOT part of the
 * key — it cannot be recovered from a message already sitting in the window,
 * and the key has to mean the same thing whether it is read from the run's
 * ledger or recovered from a marker on a restored history. Content identity
 * is what matters: a memory recall whose text changed between iterations is a
 * NEW delivery (it is new content), an always-on fact repeating verbatim is
 * the same one and is delivered once, and one injection declaring the same
 * sentence twice says it once.
 */
export declare function deliveryKey(injectionId: string, role: string, content: string): string;
/**
 * Every delivery key already present as a marker in the window.
 *
 * Unioned with the run's ledger before deciding what to deliver, and that
 * union is what makes replay safe: `agent.resumeOnError` rebuilds the
 * conversation but starts a fresh scope, so the ledger is empty while the
 * delivered messages are right there in the restored history. Reading the
 * window itself is the only way to know that.
 */
export declare function keysInWindow(history: readonly LLMMessage[]): ReadonlySet<string>;
/**
 * Why a message may NOT be appended to the end of `history` right now, or
 * `undefined` when it may.
 *
 * Two refusals, in the order a reader cares about them:
 *   • `unanswered-tool-call` — the tail is an assistant turn whose tool calls
 *     have no results yet. Appending here would sit BETWEEN a `tool_use` and
 *     its `tool_result`, which is the one split the window family refuses in
 *     the other direction too.
 *   • `role-collision` — the turn at the end already speaks in this effective
 *     role, and providers reject two in a row.
 */
export declare function refusalForPlacement(history: readonly LLMMessage[], role: WireRole): DeferralReason | undefined;
/** The effective role of the turn a delivery would land behind, for the note. */
export declare function tailWireRole(history: readonly LLMMessage[]): WireRole | undefined;
