/**
 * consentRequest — the request a consent gate puts to a person: how it is
 * named, what it is bound to, and how a decision is held to it, once.
 *
 * Role:   the ONE owner of that law. A consent gate (a tool's `checkIn`, a
 *         middleware's `ask`) pauses the run; `mintConsentRequest` mints the request
 *         at the pause, and `heldDecision` + `spendDecision` hold a resume's
 *         decision to it. The dispatch loop (`agent/stages/toolCalls.ts`) is
 *         the only writer and the only place a decision is used; `Agent.resume`
 *         asks the same pure question earlier, so a mismatch is refused before
 *         anything moves.
 * Reads:  the call about to pause; at a resume, the paused run's own state
 *         (`gatedCallOf`) and the decision.
 * Writes: nothing — the dispatch loop writes what `mintConsentRequest` returns.
 *
 * ── WHY THE ID IS A COMMITMENT, NOT ONLY A NAME ─────────────────────────────
 * `id = sha256(domain ‖ nonce ‖ canonicalJson(call))`, where the call is
 * everything that runs on approval: the gate, the tool, the call id, the
 * served party, the arguments and — for an ask — the middleware, where the
 * chain resumes and the names of the rules from the asking one on (a resume
 * refuses an agent whose chain no longer runs those rules, and a rule after
 * the ask that hands back other arguments than the ones approved — the tool
 * does not run). The nonce is 128 random bits kept in the paused run's state.
 *
 *   - A random nonce makes every pause's id unique and unguessable, so a
 *     decision for one pause can never answer another — even two pauses of
 *     the same tool, with the same model-chosen call id and the same
 *     arguments.
 *   - Re-deriving the id from the state at resume binds the decision to the
 *     exact call: a checkpoint whose arguments, tool, call id, party or chain
 *     position changed after the person was asked derives a different id, and
 *     no nonce makes it derive the old one (that would be a SHA-256 preimage).
 *     The person's decision quotes the id they were shown, so a checkpoint
 *     changed after the ask cannot be made to match it.
 *   - What it does NOT stop is a hostile writer of the checkpoint store: one
 *     who rewrites the call, the nonce and the request shown together BEFORE
 *     the person decides (they then approve what they were shown), or who
 *     switches the pause into a kind whose resume takes no decision (a
 *     credential-consent pause). The binding detects drift and replay, not
 *     that writer; a host that does not trust its store signs checkpoints or
 *     keeps them server-side.
 *   - The id carries no run value. The nonce does unlock it — whoever holds
 *     the nonce can confirm a guess of the arguments — so the state key that
 *     carries it (`pausedRequest`) is named with the arguments in the
 *     conversation redaction vocabulary (`redaction/conversation.ts`).
 *
 * ── THE DOMAIN ─────────────────────────────────────────────────────────────
 * The call is fingerprinted as JSON, because JSON is what a person is shown and
 * what a stored checkpoint holds. A call JSON cannot write down faithfully (a
 * `Date`, a `Map`, `Infinity` — `lib/jsonFaithful.ts`) cannot be bound, and
 * `mintConsentRequest` says so instead of fingerprinting a value two different calls
 * would share. JSON's own equivalences hold: a property whose value is
 * `undefined` is the property absent, and `-0` is `0`.
 *
 * ── ONCE ───────────────────────────────────────────────────────────────────
 * A request is spent the moment a resume uses a decision for it, approved or
 * declined (`DecisionLedger`), and "once" holds PER LEDGER. The default is one
 * ledger in this process's memory, shared by every agent that names none — a
 * pool that rebuilds an evicted instance, a host that builds a runner per
 * request, a composition's nested agents all spend in it. It is gone after a
 * restart and no other process sees it: where more than one process (or a
 * restart) can resume the same paused run, every agent that can resume it is
 * given one ledger in a store they all reach (`Agent.create({ decisionLedger })`).
 * The process's ledger and everything below that is process-wide live on
 * `globalThis` under one versioned key, so an app that loads both builds of
 * this package (CJS and ESM) still has ONE (`processState`).
 *
 * ── WAS IT USED? ───────────────────────────────────────────────────────────
 * A host that keeps the pause asks this after a resume of it failed: the call
 * may have run only if the decision reached a ledger. `spendDecision` is the
 * one place a decision does, so it reports every attempt to whoever watches
 * that request (`watchDecisionUse`), and the ledger it went to is asked when
 * the attempt's own answer was lost. A resume this library took up and never
 * took to a ledger used nothing; one it never took up cannot be told apart
 * from a use (`hosting/answeredPause.ts`).
 *
 * ── DID IT NEVER RUN? ──────────────────────────────────────────────────────
 * A watch sees one resume. A host closing a pause no answer can reach asks a
 * stronger question — can ANY decision for it have been used, by any resume,
 * before this process started included — and says "did not run" only on
 * proof (`proveNeverRan`): the ledger the request was MINTED under (its
 * identity rides the pause, `PendingRequest.ledger`) spends the request
 * itself, atomically. `true` means no decision had reached it, and none ever
 * can now — the spend is a tombstone, not a read that a resume elsewhere
 * could overtake. Anything else is not proof.
 */
import type { FlowchartCheckpoint } from 'footprintjs';
import { type CheckInDecision } from './checkin.js';
import { type ConsentGate, type ConsentGateKind } from './pause.js';
/**
 * The call a consent gate asked a person about — what runs on approval. Read
 * loosely (`unknown`) because at a resume it comes from a checkpoint: whatever
 * the state holds is what would run, and that is what gets fingerprinted.
 */
export interface GatedCall {
    readonly gate: ConsentGateKind;
    /** The tool's name. */
    readonly tool: unknown;
    /** The model's id for the call. */
    readonly toolCallId: unknown;
    /** The party the tool resolves against (`AgentState.pausedToolParty`), when one was recorded. */
    readonly party?: unknown;
    /** The arguments the call runs with. */
    readonly args: unknown;
    /** `'ask'` only — the middleware that asked. */
    readonly middleware?: unknown;
    /** `'ask'` only — the asking link's index; the chain resumes after it. */
    readonly askIndex?: unknown;
    /**
     * `'ask'` only — the names of the asking link and of every link after it,
     * in chain order: the rules the call still passes once the person answers.
     * A position alone would let a redeploy that removed or moved a later rule
     * resume past it; the names make that a refusal (`'chain-changed'`).
     */
    readonly chain?: unknown;
}
/** A consent gate's request as the paused run's state carries it (`AgentState.pausedRequest`). */
export interface PendingRequest {
    /** The id a decision quotes — 64 hex characters. */
    readonly id: string;
    /** 128 random bits, hex — the commitment's key. Never served on the request. */
    readonly nonce: string;
    /**
     * The identity of the ledger the request was minted under — the agent's
     * `decisionLedger` (`store:` + its `storeId`) or this process's default
     * (`process:` + a per-process random id). Only that ledger can prove the
     * paused call never ran ({@link proveNeverRan}). Absent when the ledger has
     * no stable identity, and on a pause stored before it was recorded: such a
     * pause can never be proven.
     */
    readonly ledger?: string;
}
/** What {@link mintConsentRequest} hands the dispatch loop. */
export interface BoundRequest {
    readonly request: PendingRequest;
    /**
     * The detached copy of the arguments the request is bound to. The call
     * carries THIS copy from here on — on the request the person is shown, in
     * the paused run's state, and into the tool on approval — so nothing that
     * still holds the original can change what was approved.
     */
    readonly args: Readonly<Record<string, unknown>>;
}
/**
 * Where the consent requests that have been answered are recorded, so one
 * decision runs one call — once per LEDGER.
 *
 * By default every agent that names none spends in one ledger in this
 * process's memory: "once" holds across this process's agents — a pool that
 * rebuilds an evicted instance, a server that builds an agent per request —
 * and only there. A restart forgets it, and another process never saw it.
 * When more than one process can resume the same paused run, or a restart
 * must not forget, give every agent that can resume it one ledger backed by a
 * store they all reach (`Agent.create({ decisionLedger })`).
 */
export interface DecisionLedger {
    /**
     * OPTIONAL — the identity of the store this ledger records into. A host that
     * closes a pause no answer can reach asks the ledger the pause was minted
     * under — and only that one — to prove its call never ran
     * ({@link DecisionLedger.spend}); a ledger without a `storeId` never proves,
     * and the call is recorded as possibly run.
     *
     * The contract this library cannot check, and that the proof rests on:
     *
     *  - unique per BACKING STORE: one value for one table (or collection, or
     *    key space) and every process that writes it — never copied from
     *    staging to production, never shared by per-tenant tables;
     *  - permanent: the same for the life of that store;
     *  - changed on any migration to a new store — a request spent in the old
     *    store is not in the new one, and must not be read as never spent there.
     *    Pending approvals are then asked again: a decision is spent only in the
     *    ledger its request was asked under (`'ledger-changed'`).
     *
     * Spends in it are permanent too (no expiry) — see `spend`.
     */
    readonly storeId?: string;
    /**
     * Record `requestId` as answered. Resolve `true` when THIS call recorded it
     * and `false` when it was already recorded — atomically per id: of two
     * concurrent calls for one id, exactly one may see `true`. Anything other
     * than `true` refuses the decision; a throw fails the resume. Both fail
     * closed: the tool does not run.
     *
     * A recorded id stays recorded: for a ledger that is to prove a paused call
     * never ran (one with a {@link DecisionLedger.storeId}), the result is
     * permanent — no expiry. A host closing a pause spends its request here as
     * the proof (`true`: no decision had reached it, and none ever can), then
     * `closed-proven:<requestId>` to record that IT closed it; a key that expired
     * would let a spent request read as never spent.
     */
    spend(requestId: string): boolean | Promise<boolean>;
    /**
     * OPTIONAL — whether `requestId` is recorded as answered, without recording
     * it. Asked only after a {@link DecisionLedger.spend} for it failed, so a
     * host can tell a spend that never landed (the call did not run; the pause
     * stays answerable) from one that did. A ledger without it, or one that
     * throws or answers anything but `false`, counts as spent: the host stops
     * offering the pause. Asked, too, whether a host recorded closing a request
     * (`closed-proven:<requestId>`) — so a retried close keeps the verdict the
     * first one proved, and a person whose answer arrives after the close is
     * told the host closed it. Never asked whether the REQUEST is unspent as a
     * proof that its call did not run: a read can be overtaken by a resume
     * elsewhere; only `spend` proves.
     */
    isSpent?(requestId: string): boolean | Promise<boolean>;
}
/**
 * A NEW {@link DecisionLedger} in this process's memory. Grows by one id per
 * answered request.
 *
 * Agents that name no ledger already share one of these per process, so this
 * is for a ledger of their own: agents given different ledgers spend
 * separately, and a decision spent in one is not known to the others.
 */
export declare function inMemoryDecisionLedger(): DecisionLedger;
/**
 * Register the rules `agent` holds an ask's approval to — its `toolMiddleware`
 * chain by link name, read each time it is asked — for a host that keeps its
 * pauses ({@link consentRulesOf}). Called by every `Agent` it builds.
 */
export declare function registerConsentRules(agent: object, rules: () => readonly string[]): void;
/**
 * The rules `runner` holds an ask's approval to, when it is an agent this
 * library built — `undefined` for any other runner (a composition, an app's
 * own), whose nested agents' rules a host cannot see.
 */
export declare function consentRulesOf(runner: unknown): readonly string[] | undefined;
/**
 * This process's ledger — where an agent that names no `decisionLedger`
 * spends. One per process, so a decision runs once across every agent
 * instance this process builds (and every copy of this package it loaded),
 * not once per instance. It lives as long as the process and grows by one id
 * per answered request; request ids are unique per pause, so sharing it never
 * makes one agent's answer collide with another's.
 */
export declare function processDecisionLedger(): DecisionLedger;
/**
 * Register the ledger `agent` spends its decisions in — `undefined` for the
 * process's ({@link processDecisionLedger}) — so a host that closes one of its
 * pauses can ask it for the proof ({@link proveNeverRan}). Called by every
 * `Agent` it builds.
 */
export declare function registerConsentLedger(agent: object, ledger: DecisionLedger | undefined): void;
/**
 * The ledger `runner` spends decisions in, when it is an agent this library
 * built — `undefined` for any other runner (a composition, an app's own).
 */
export declare function consentLedgerOf(runner: unknown): DecisionLedger | undefined;
/**
 * The identity a pause records for the ledger its request is minted under
 * ({@link PendingRequest.ledger}), or `undefined` for one that has none:
 *
 *  - this process's default ledger: `process:` + an id drawn when the process
 *    first asked. A restart draws another, so it never answers for a pause an
 *    earlier process stored — that process took its ledger with it;
 *  - a ledger naming its store (`DecisionLedger.storeId`): `store:` + that id;
 *  - any other — an app's own `inMemoryDecisionLedger()`, a ledger without a
 *    `storeId` — none: it may be one agent's alone, and nothing tells it
 *    apart from another.
 */
export declare function ledgerIdentityOf(ledger: DecisionLedger): string | undefined;
/** What {@link proveNeverRan} needs to know about a pause being closed, and the door closing it. */
export interface NeverRanQuestion {
    /** Every id the paused request is known by — the state's and the question's. */
    readonly requestIds: readonly string[];
    /** The identity of the ledger the request was minted under ({@link PendingRequest.ledger}). */
    readonly mintedUnder: string | undefined;
    /** The runner serving the session — an agent this library built names its ledger. */
    readonly runner: unknown;
    /**
     * The door keeps its pauses in this process's memory, so no other process
     * can have answered one. Asked only of this process's default ledger.
     */
    readonly storeIsProcessLocal: boolean;
}
/**
 * The key a closer spends, after it won a request, to record that IT closed it
 * ({@link proveNeverRan}). Request ids are 64 hex characters, so no request id
 * is ever one of these.
 */
export declare function closedProvenKey(requestId: string): string;
/**
 * Prove that no decision for a paused request can ever run its call — or say
 * that it cannot be proven. Spending is the one way a decision reaches a call
 * (`spendDecision` runs before the tool, on every door), so the proof is the
 * ledger the request was MINTED under spending the request itself: `true`
 * there means no decision had reached it and — in the same atomic step — that
 * none ever can (a later resume is refused). A read (`isSpent`) of the request
 * would not do: a resume on another replica could spend and run the call
 * between the read and the close, and a ledger may expire what it reads.
 *
 * IDEMPOTENT. Having won the request, the closer spends a second key,
 * {@link closedProvenKey}, to record that it closed it. The closed pause is
 * stored only afterwards, and that can fail (the store's write, the run that
 * goes on from it); a retried close finds the request spent and reads the
 * marker (`isSpent`) — present: the verdict the first close proved; absent
 * (a crash between the two spends, a ledger without `isSpent`): not proven, the
 * safe direction. A request a DECISION spent never has the marker.
 *
 * CONSUMES the request: asked only for a pause being closed.
 *
 * Fails closed — `false` — whenever the proof cannot be had:
 *
 *  - the pause names no request, or no minting ledger (one stored before
 *    either was recorded, or minted under a ledger with no identity);
 *  - `runner` is not an agent this library built (a composition, an app's
 *    own), so its ledger is unknown;
 *  - its ledger is not the one the request was minted under: a redeploy that
 *    switched ledgers, or a restart (this process's ledger is a new one) —
 *    that ledger never saw the spends;
 *  - the request was minted under this process's default ledger and the
 *    door's session store is not this process's memory: another replica, on
 *    its own default ledger, may have spent it and run the call;
 *  - a spend throws, or answers anything but `true` with no close recorded.
 */
export declare function proveNeverRan(question: NeverRanQuestion): Promise<boolean>;
/**
 * Whether a host already closed this paused request with the proof that its
 * call never ran — read, never spent: the ledger it was minted under holds the
 * close marker for every id ({@link closedProvenKey}). Asked by a door whose
 * answer to the pause failed because the request was already spent, so a pause
 * closed elsewhere (whose closed conversation was never stored) is closed as
 * not run rather than marked as possibly run.
 */
export declare function closedByAHost(question: NeverRanQuestion): Promise<boolean>;
/**
 * Mint the request for a call about to pause, over a detached copy of its
 * arguments. `undefined` when the call cannot be bound — its arguments cannot
 * be copied, are not an object, or are not values JSON writes down faithfully
 * — and the gate must refuse the call instead of pausing.
 *
 * @param ledger the ledger a decision for it will be spent in — its identity
 *   rides the request ({@link PendingRequest.ledger}), so only that ledger can
 *   later prove the call never ran.
 */
export declare function mintConsentRequest(call: GatedCall, ledger?: DecisionLedger): BoundRequest | undefined;
/**
 * The paused call as the paused run's state records it — the one reading both
 * the resume's early check and the dispatch loop take, so they cannot disagree
 * about what the request was bound to.
 */
export declare function gatedCallOf(read: (key: string) => unknown, gate: ConsentGateKind): GatedCall;
/**
 * Hold a resume's input to the request the run is paused on. Pure: it spends
 * nothing (see {@link spendDecision}).
 *
 * @param recorded the paused run's `pausedRequest`, as read — never trusted.
 * @param call     the paused call, as read ({@link gatedCallOf}).
 * @param rules    the resuming agent's own `toolMiddleware` chain, by link
 *   name — an ask's approval holds only for the rules it was asked under.
 * @param ledger   the ledger the resuming agent spends decisions in — a
 *   request minted under a ledger that can prove is spent only there.
 * @returns a detached copy of the decision.
 * @throws DecisionRequiredError when the input is not a decision at all.
 * @throws ResumeDecisionError (`'no-request'`, `'request-not-recorded'`,
 *   `'other-request'`, `'call-changed'`, `'chain-changed'`,
 *   `'ledger-changed'`) when it cannot answer this request.
 */
export declare function heldDecision(input: unknown, gate: ConsentGate, recorded: unknown, call: GatedCall, rules: readonly string[], ledger?: DecisionLedger): CheckInDecision;
/**
 * The refusals no decision can change — a paused request that can never be
 * answered by the agent whose rules are `rules`. See {@link unanswerableReason}.
 */
export type UnanswerableReason = 'request-not-recorded' | 'call-changed' | 'chain-changed' | 'ledger-changed';
/**
 * Why the request a checkpoint's paused stage records can never be answered —
 * by any decision — or `undefined` when it can, or when that cannot be told
 * from the checkpoint (its paused state is not where it should be).
 *
 * Read by a host that keeps the pause, so a session is not held by a question
 * nobody can answer any more: one paused before requests were named
 * (`'request-not-recorded'`), one whose call changed after the ask
 * (`'call-changed'`), an ask made under rules the resuming agent no longer
 * runs (`'chain-changed'` — told only when `rules` are known), or a request
 * minted under a proving ledger the resuming agent no longer spends in
 * (`'ledger-changed'` — told only when `ledger` is known).
 */
export declare function unanswerableReason(checkpoint: FlowchartCheckpoint, gate: ConsentGateKind, rules: readonly string[] | undefined, ledger?: DecisionLedger): UnanswerableReason | undefined;
/**
 * Whether `wouldRun` are the arguments the paused request was minted over —
 * decided by re-deriving the request's id with them in place of the recorded
 * ones: the call that runs must be the call the id the person was shown
 * commits to. Key order and JSON's own equivalences do not count; a value
 * JSON cannot write down faithfully never matches.
 *
 * Held to the REQUEST, never to a copy: an ask's resume hands the rest of the
 * chain a copy of the approved arguments, and a link that edited that copy in
 * place would make any comparison against it agree with itself.
 *
 * @param recorded the paused run's `pausedRequest`, as read.
 * @param call     the paused call, as read ({@link gatedCallOf}); its own
 *   `args` are replaced by `wouldRun`.
 */
export declare function runsAsAsked(recorded: unknown, call: GatedCall, wouldRun: unknown): boolean;
/**
 * The same question as {@link heldDecision}, asked of a checkpoint before a
 * resume starts — so a mismatch is refused while nothing has moved. Reads the
 * paused stage's own state (the root's, or the innermost subflow's on the
 * pause path). A state it cannot find is left to the dispatch loop, which asks
 * again where the decision is used.
 */
export declare function checkDecisionAgainst(checkpoint: FlowchartCheckpoint, gate: ConsentGate, input: unknown, rules: readonly string[], ledger?: DecisionLedger): void;
/**
 * Spend the decision's request in `ledger` — the one place a decision reaches
 * a ledger, so every attempt is reported to whoever watches the request
 * ({@link watchDecisionUse}).
 *
 * @param signal the resumed run's signal. The engine stops waiting on a stage
 *   when its run is aborted, but the stage goes on running: a resume whose run
 *   was aborted takes nothing to a ledger, and one aborted while its ledger
 *   answered is refused after the spend — spent, and its call never run.
 * @throws ResumeDecisionError `'already-answered'` when a resume already spent
 *   it, or `'closed-by-host'` when a host closed it and recorded that its call
 *   never ran (`closedProvenKey`).
 */
export declare function spendDecision(ledger: DecisionLedger, decision: CheckInDecision, gate: ConsentGateKind, signal?: AbortSignal): Promise<void>;
/**
 * What an aborted run ended with — the signal's own reason, as the engine
 * reports it — or `undefined` while the run goes on. Asked before the spend,
 * after it, and by the dispatch loop once more at the last moment before an
 * approved call runs (`toolCalls.ts` · `resolveCredentialAndExecute`): the
 * engine stops waiting on an aborted run's stage, but the stage goes on, and
 * nothing may run for a caller who was already told the run ended.
 */
export declare function runEndedBy(signal: AbortSignal | undefined): Error | undefined;
/**
 * A resume of this library has taken up the request `requestId` names: from
 * here every use of its decision passes through {@link spendDecision}, where a
 * watch sees it. Called by every runner's resume (`RunnerBase` ·
 * `emitPauseResume`, and `Agent.resume` before its first check) and by the
 * dispatch loop where it takes the decision up.
 */
export declare function noteDecisionTakenUp(requestId: string | undefined): void;
/** What a door that keeps a pause learns, for one resume, about its request ({@link watchDecisionUse}). */
export interface DecisionUseWatch {
    /**
     * Whether the request's decision may have been used while watched — by
     * this resume, or by any other in this process:
     *
     *  - taken to a ledger that spent it, refused it (an earlier resume had)
     *    or has not answered yet → `true`;
     *  - taken to a ledger whose spend failed → asked again of that ledger
     *    (`DecisionLedger.isSpent`): `false` there means the spend never landed;
     *    no answer, a throw, or a ledger that cannot be asked → `true`;
     *  - never taken to a ledger, by a resume this library took up → `false`;
     *  - never taken up by this library at all → `true`: that cannot be told
     *    apart from a use.
     */
    mayHaveBeenUsed(): Promise<boolean>;
    /**
     * Whether a ledger SPENT the request for a decline while watched, and no
     * approval of it was taken to a ledger meanwhile: the decline won the
     * ledger, so no approval can have run the call there. Only then does a door
     * record the paused call as declined. A decline the ledger refused (an
     * earlier resume — an approval, perhaps in another process — had spent the
     * request) is not one: that call may have run.
     */
    spentAsDecline(): boolean;
    /** Stop watching. A door releases its watch once it has decided. */
    release(): void;
}
/**
 * Watch the request `requestId` names for the length of one resume — opened
 * by a door that keeps the pause, before it hands the decision over, and
 * released once it has decided what a failure means for the stored pause.
 * Keyed by the request, never by the decision object a door may copy or wrap.
 */
export declare function watchDecisionUse(requestId: string): DecisionUseWatch;
/** The id of the request a paused run's state records (`pausedRequest`), or `undefined`. */
export declare function recordedRequestId(recorded: unknown): string | undefined;
/**
 * The id of the call a checkpoint's paused stage records as paused
 * (`pausedToolCallId`), or `undefined` — never a throw, whatever the
 * checkpoint holds.
 */
export declare function pausedCallIdOf(checkpoint: FlowchartCheckpoint): string | undefined;
/**
 * The identity of the ledger the request a checkpoint's paused stage records
 * was minted under ({@link PendingRequest.ledger}), or `undefined` — never a
 * throw, whatever the checkpoint holds.
 */
export declare function pausedRequestLedgerOf(checkpoint: FlowchartCheckpoint): string | undefined;
/**
 * The id of the request a checkpoint's paused stage records, or `undefined`
 * — never a throw, whatever the checkpoint holds.
 */
export declare function pausedRequestIdOf(checkpoint: FlowchartCheckpoint): string | undefined;
