/**
 * runCheckpoint — fault-tolerant resume primitives.
 *
 * Today's pause/resume only handles INTENTIONAL pauses (`askHuman`).
 * Errors mid-run (LLM 500s, vendor outages, tool throws, container
 * restarts) propagate all the way up and the consumer must restart
 * from scratch — losing the prior iterations' work.
 *
 * This module adds the third piece of the Reliability subsystem:
 *
 *   1. **`AgentRunCheckpoint`** — JSON-serializable snapshot of an
 *      agent run's progress. Captured automatically at each
 *      iteration boundary (the natural commit points). Survives
 *      process restart — persist to Redis / Postgres / S3 / queue.
 *
 *   2. **`RunCheckpointError`** — wraps the underlying error with
 *      the last-known-good checkpoint. Throwing this instead of the
 *      raw error lets consumers catch + persist + resume later
 *      without losing context.
 *
 *   3. **`agent.resumeOnError(checkpoint)`** — replays the agent run
 *      with the checkpointed conversation history restored. The
 *      next iteration retries the call that originally failed (with
 *      the latest provider state — circuit breaker may have closed,
 *      vendor may have recovered, etc.).
 *
 * Design tradeoff: we use a CONVERSATION-HISTORY checkpoint shape
 * rather than a full executor-state checkpoint (which would require
 * footprintjs API surface changes for mid-run snapshotting). The
 * tradeoff:
 *
 *   ✅ Survives process restart (JSON-serializable, tiny payload)
 *   ✅ Works with any LLM provider — replay starts from history
 *   ✅ No footprintjs core changes
 *   ⚠️  Loses mid-iteration partial state (acceptable — iterations
 *       are atomic; we resume from the last completed boundary)
 *   ⚠️  TOOL RE-EXECUTION (idempotency requirement): anything the
 *       failed iteration did after the last completed boundary —
 *       including tool side effects — is NOT in the checkpoint. On
 *       resume the model re-decides from the restored history and may
 *       re-issue those tool calls; they WILL execute again. There is
 *       NO built-in toolCallId-based dedup. Mutating tools (payments,
 *       emails, DB writes) must be idempotent — derive an idempotency
 *       key from stable call content, not from `ctx.toolCallId` (fresh
 *       per issued call, so a re-issued call gets a NEW id). Note the
 *       same requirement exists WITHOUT resume: a tool that performs
 *       its side effect and then throws reports the error message back
 *       to the model as the tool result, and the model typically
 *       retries the call on the next iteration.
 *
 * Pattern: Memento (GoF) — snapshot of an object's internal state
 *          for later restoration. Same shape as `FlowchartCheckpoint`
 *          but at the agent layer (one logical iteration vs. one
 *          DFS stage).
 */
import type { LLMMessage } from '../provider-contract/types.js';
import type { ReplayPreparation } from '../thinking/replay.js';
import { type ReplayRetirement } from '../thinking/replayHistory.js';
import type { MemoryIdentity } from '../memory/identity/types.js';
import type { FoldedSpan } from './agent/window/types.js';
import { type FindingsLedger } from './agent/findings/types.js';
import { type NegativeCheckpoint } from './agent/negativeClaims/checkpoint.js';
import type { ClaimVerificationState } from './agent/findings/verificationState.js';
/** Internal repair state kept separately from the human conversation.
 * Restored only when retrying the same failed request, never on a new turn. */
export interface EvidenceRecoveryCheckpoint {
    readonly revisionSpent: true;
    /** Present only when the request carrying this instruction did not complete. */
    readonly pending?: {
        readonly instruction: string;
        /** The figures dial's late line, when the pending revision has one. */
        readonly conclusion?: string;
    };
}
/**
 * JSON-serializable checkpoint of an in-progress agent run. Persist
 * to ANY durable store (Redis / Postgres / S3 / disk / queue) and
 * resume hours / days / deploys later via `agent.resumeOnError(...)`.
 *
 * **Stable shape** — the `version` field guards forward compat. v1
 * → v2 transitions will be supported via a migration helper.
 */
export interface AgentRunCheckpoint {
    /** Private admitted negative evidence; keep checkpoints on the trusted host. */
    readonly negativeClaimState?: NegativeCheckpoint['negativeClaimState'];
    readonly resultIdentities?: NegativeCheckpoint['resultIdentities'];
    /** Schema version. v1 = conversation-history-based. */
    readonly version: 1;
    /** `runId` of the FAILING run — lets the consumer correlate a
     *  persisted checkpoint back to the original run's observability.
     *  NOT reused on resume: `resumeOnError` starts a fresh run with a
     *  fresh `runId` (only the conversation history is restored). */
    readonly runId: string;
    /** Conversation history at the LAST completed iteration boundary
     *  (LLM messages). The next iteration retries from here. */
    readonly history: readonly LLMMessage[];
    /** Index of the last completed iteration in the FAILING run
     *  (diagnostic — not consumed on resume). The resumed run restores
     *  this history but re-seeds its own iteration counter at 1 with a
     *  full `maxIterations` budget. */
    readonly lastCompletedIteration: number;
    /** Original input message. Surfaces in observability + lets the
     *  consumer correlate checkpoint to the user's request. */
    readonly originalInput: {
        readonly message: string;
    };
    /** Wall-clock when the checkpoint was captured. Diagnostic only. */
    readonly checkpointedAt: number;
    /**
     * The same request's bounded evidence repair. `resumeOnError` restores this
     * budget and any unsent feedback; `run({ continueFrom })` adds a new human
     * turn and deliberately starts a fresh repair budget. Not conversation text.
     * Absent for checkpoints written before request-scoped recovery.
     */
    readonly evidenceRecovery?: EvidenceRecoveryCheckpoint;
    /**
     * Every span this conversation folded into a summary, oldest first — what
     * makes a compacted conversation still a provable one after the process
     * that compacted it is gone.
     *
     * Written by `.compaction()`; absent on a conversation that never folded,
     * and absent on one stored by a runtime older than 8.2. Under the default
     * `retain: 'conversation'` each span carries the folded messages verbatim;
     * under `retain: 'discard'` the span is still here, naming what left, and
     * only `messages` is absent.
     *
     * Join a summary in {@link history} to its span with `foldedSpanFor(...)` —
     * by content fingerprint, never by index, because a later fold moves every
     * index after it.
     *
     * **Version 1 still, deliberately.** An optional field is not a format
     * change: a runtime that has never heard of `folded` reads this checkpoint,
     * ignores it, and continues the conversation correctly — the summary is an
     * ordinary message in `history` either way. Bumping the version would make
     * an older deployment REFUSE a session it can serve perfectly well, which is
     * the opposite of what the version field is for.
     */
    readonly folded?: readonly FoldedSpan[];
    /**
     * The model's standings on its tool results (9.101.0) — the committed
     * `AgentState.findingsLedger`, carried so a continued conversation keeps
     * what the model already called a fact, left open, ruled out or dismissed,
     * instead of re-deriving it from results the store no longer serves whole.
     *
     * Written only when the run's ledger is non-empty; absent on every
     * conversation whose agent never called `.findings()` and on any stored by
     * a runtime older than 9.101.0 — the {@link folded} rule: an optional field
     * is not a format change, and a runtime that has never heard of this key
     * reads the checkpoint, ignores it, and continues correctly. The rows are
     * plain records and arrays only (the shape `recordFindings` writes), so
     * they round-trip through JSON and `structuredClone` unchanged.
     *
     * **Version 1 still**, by the same documented rule as {@link folded} /
     * {@link skillCursor}.
     */
    readonly findingsLedger?: FindingsLedger;
    /**
     * Private, opt-in exact evidence and reservation counters. Unlike historical
     * observations, this carrier can support later checks. A newest-version
     * continuation must explicitly keep verification enabled, never discard it.
     * Storage ownership/authenticity remains the checkpoint caller's obligation.
     */
    readonly claimVerification?: ClaimVerificationState;
    /**
     * The conversation TURN the stored history ends on — the stored run's
     * `AgentState.turnNumber` — carried while an honesty layer is armed.
     *
     * Every ledger row filed while a layer is armed carries its `turn`, and the
     * answer's standing reads "this turn" by that stamp, so a stamp must never
     * repeat in one conversation. The count of user messages in `history` cannot
     * promise that: a window strategy or a compaction trims the stored history,
     * and the count repeats and goes backwards — an earlier turn's verdict would
     * then be folded as a later turn's. So the next run starts from this number:
     * `run({ continueFrom })` is the turn after it, `resumeOnError` the same
     * turn again. Written by both carriers (`checkpoint()` and the crash
     * checkpoint), and only while a layer is armed — every other checkpoint
     * keeps its exact byte shape.
     *
     * **Version 1 still**, by the same documented rule as {@link folded} /
     * {@link findingsLedger}: an optional field is not a format change, and a
     * runtime that has never heard of it continues the conversation correctly.
     */
    readonly turnNumber?: number;
    /**
     * WHO this conversation belongs to — the `identity` the stored run was
     * given, carried so that continuing it lands in the same namespace it
     * started in (9.2.0).
     *
     * Before this field, continuing a conversation re-seeded identity from the
     * resuming run's own id: every continued turn wrote its memory under a
     * FRESH `conversationId`, so turn two's facts were stored somewhere turn
     * three could not read them. Nothing threw, and the only symptom was an
     * agent that kept forgetting — the same class of failure as a store that
     * silently forgot everything looking exactly like a new user.
     *
     * Absent on a conversation stored before 9.2.0, and absent when the run
     * never got an explicit identity (the default is derived from a runId, and
     * carrying THAT forward would pin a whole conversation to one run's id).
     * An explicit `identity` on the continuing call always wins.
     *
     * **Version 1 still**, on the same reasoning as {@link folded}: an optional
     * field is not a format change, and a runtime that has never heard of it
     * continues the conversation correctly.
     */
    readonly identity?: MemoryIdentity;
    /**
     * WHERE the conversation's skill graph stood when the stored turn ended —
     * the graph cursor, carried so a continued conversation can default its
     * next turn's start to it (SG-C, 9.17.0).
     *
     * Written ONLY when the recording agent's graph declared
     * `continuity: 'conversation'`; under the default `'turn'` the field is
     * never written and the byte shape of every checkpoint is unchanged. On
     * the continuing side it is honored only under the same declaration, and
     * the RouteTurn cascade still judges it against the new message — an
     * inherited cursor is a sticky DEFAULT, not a lock. An id the continuing
     * graph does not know is dropped and recorded
     * (`turn_routed.droppedResume`), never silently parked.
     *
     * **Version 1 still**, by the same documented rule as {@link folded} /
     * {@link identity} / {@link agent}: an optional field is not a format
     * change — an older runtime ignores it and continues correctly.
     */
    readonly skillCursor?: string;
    /**
     * WHICH agent recorded this conversation — present only when that agent was
     * given an explicit `Agent.create({ id })` (9.2.0).
     *
     * A conversation is a transcript, and a transcript can be replayed on any
     * agent. Usually that is the point: a deploy that adds a tool or edits a
     * prompt must still be able to continue yesterday's conversations, so the
     * runtime cannot refuse on "the agent changed". But replaying the BILLING
     * agent's conversation on the SUPPORT agent is a different mistake, and it
     * used to be accepted in silence.
     *
     * The rule is the one the embedder fingerprint already uses: **ids decide
     * only when BOTH sides named themselves.** A default id (`'agent'`) is not
     * naming yourself, so the majority of callers — who never pass one — are
     * never refused. Two sides that both chose a name and chose different ones
     * are refused, by name.
     *
     * Version 1 still, for the same reason as the two fields above.
     */
    readonly agent?: {
        readonly id: string;
    };
    /** Where the failure happened. Diagnostic — surfaces in oncall
     *  triage so you can tell "LLM 500 mid-iteration" from "tool
     *  threw" from "validation kept failing". */
    readonly failurePoint?: {
        readonly iteration: number;
        readonly phase: 'iteration' | 'tool' | 'llm' | 'unknown';
        /**
         * What was OPEN when it threw — `'call-llm'` for the model call, or the
         * declared name of the tool that was running (8.14.0).
         *
         * Absent when nothing was open (a failure between brackets), which is the
         * honest answer rather than a guess.
         *
         * **Never a URL, never a credential, never request or response content.**
         * Only the literal string `'call-llm'` or a tool name the app itself
         * declared. A checkpoint is persisted to Redis / Postgres / S3 and read by
         * whoever is on call; nothing that could carry a secret goes in it. Do not
         * "improve" this field into carrying the endpoint.
         */
        readonly stage?: string;
    };
}
/**
 * Thrown by `agent.run()` when a fault occurs mid-run. Carries the
 * underlying error AND the last-known-good checkpoint. Catch this
 * specifically to engage the resume-on-error path; let other errors
 * propagate normally.
 *
 * @example
 * ```ts
 * import { Agent, RunCheckpointError } from 'agentfootprint';
 *
 * try {
 *   const result = await agent.run({ message: 'long task' });
 * } catch (err) {
 *   if (err instanceof RunCheckpointError) {
 *     await checkpointStore.put(sessionId, err.checkpoint);
 *     // hours / restart later:
 *     const checkpoint = await checkpointStore.get(sessionId);
 *     const result = await agent.resumeOnError(checkpoint);
 *   } else {
 *     throw err; // not a recoverable error — propagate
 *   }
 * }
 * ```
 */
export declare class RunCheckpointError extends Error {
    readonly code: "ERR_RUN_CHECKPOINT";
    /** The error that triggered the checkpoint. Inspect for retry
     *  decisions ("if cause is CircuitOpenError, wait for cooldown
     *  before resuming"). */
    readonly cause: Error;
    /** The last-known-good checkpoint. Persist + pass back to
     *  `agent.resumeOnError(checkpoint)` to continue from here. */
    readonly checkpoint: AgentRunCheckpoint;
    constructor(cause: Error, checkpoint: AgentRunCheckpoint);
}
/**
 * Can `agent.resumeOnError(checkpoint)` plausibly succeed for this failure?
 *
 * `true` for the transient classes resume exists for; `false` for the ones
 * where replaying the same request reproduces the same refusal (a request too
 * large for the context window, rejected credentials, a malformed request).
 * Branch on this instead of reading the message — a retry loop that resumes a
 * non-resumable failure is an expensive infinite loop.
 *
 * @example
 * ```ts
 * import { canResume, RunCheckpointError } from 'agentfootprint';
 *
 * try {
 *   await agent.run({ message });
 * } catch (err) {
 *   if (err instanceof RunCheckpointError && canResume(err.cause)) {
 *     await agent.resumeOnError(err.checkpoint);
 *   } else {
 *     throw err; // fix the request; the checkpoint is evidence, not a retry handle
 *   }
 * }
 * ```
 */
export declare function canResume(error: unknown): boolean;
/**
 * Thrown when a stored conversation is handed to an agent that is provably
 * not the one that recorded it (9.2.0).
 *
 * Raised only when BOTH sides named themselves with an explicit
 * `Agent.create({ id })` and the two names differ — see
 * {@link AgentRunCheckpoint.agent} for why that is the whole of the rule.
 * An agent that gained a tool, changed its prompt or moved to a new model
 * since the conversation was stored is NOT this error; continuing across a
 * deploy is the ordinary case and must keep working.
 */
export declare class ConversationMismatchError extends Error {
    readonly code: "ERR_CONVERSATION_MISMATCH";
    /** The id stamped on the stored conversation. */
    readonly storedAgentId: string;
    /** The id of the agent it was handed to. */
    readonly agentId: string;
    constructor(door: string, storedAgentId: string, agentId: string);
}
/**
 * Refuse a stored conversation that provably belongs to a different agent.
 *
 * Both-sides-named-themselves, or nothing happens. `storedId` is absent on
 * every conversation written before 9.2.0 and on every agent that never chose
 * an id; `agentId` is undefined for the same reason on the reading side.
 *
 * @internal
 */
export declare function assertContinuable(checkpoint: AgentRunCheckpoint, agentId: string | undefined, door: string): void;
/**
 * Mutable state the Agent maintains during a run for checkpoint
 * capture. Keyed by `runId` so multiple in-flight runs don't
 * collide. Cleared on `turn_end` (success path).
 *
 * @internal
 */
export interface RunCheckpointTracker {
    readonly runId: string;
    readonly originalInput: {
        readonly message: string;
    };
    /** Updated on every `agentfootprint.agent.iteration_end`. */
    history: readonly LLMMessage[];
    /** Explicit prepared thinking edits only; no failed-iteration tool/output state. */
    replayRetirements?: readonly ReplayRetirement[];
    /** Updated on every `agentfootprint.agent.iteration_end`. */
    lastCompletedIteration: number;
    /** Set when an iteration begins (used to attribute the failure
     *  phase if we throw before the next iteration_end). */
    inFlightIteration?: number;
    /**
     * What is OPEN right now, from the run's own `stream.*` brackets: set on
     * `llm_start` / `tool_start`, cleared on `llm_end` / `tool_end` (8.14.0).
     *
     * OBSERVATION, which is why it exists. {@link classifyFailurePhase} guesses
     * from the error's own name and message and is wrong for every failure that
     * does not describe itself — a browser fetch failure says only
     * `TypeError: Load failed`. This says what the engine was doing, and only
     * falls back to the guess when nothing was open.
     *
     * `stage` holds `'call-llm'` or a declared tool name. Never a URL.
     */
    inFlightPhase?: {
        readonly phase: 'llm' | 'tool';
        readonly stage: string;
    };
}
/** A transport preparation is durable even if its request subsequently fails. */
export declare function retainReplayRetirements(tracker: RunCheckpointTracker, preparation: ReplayPreparation): void;
/** Read only approved replay edits onto the history each checkpoint already owns. */
export declare function checkpointReplayHistory(history: readonly LLMMessage[], tracker?: RunCheckpointTracker): readonly LLMMessage[];
/**
 * Build a JSON-serializable checkpoint from a tracker + failure
 * info. Pure function — no side effects.
 *
 * @internal
 */
export declare function buildCheckpoint(tracker: RunCheckpointTracker, failurePoint?: NonNullable<AgentRunCheckpoint['failurePoint']>, 
/**
 * Spans this run folded, read from committed state by the caller. Passed in
 * rather than tracked, because the tracker follows `history` through events
 * and a fold's span is committed state — and because the same reader then
 * serves both checkpoint carriers, so neither can lose what the other keeps.
 */
folded?: readonly FoldedSpan[], 
/**
 * Who the run was for and which agent ran it — the two fields that make a
 * stored conversation continuable rather than merely readable (9.2.0).
 * Both are absent unless the caller chose them explicitly; see
 * {@link AgentRunCheckpoint.identity} and {@link AgentRunCheckpoint.agent}.
 */
owner?: {
    readonly identity?: MemoryIdentity;
    readonly agentId?: string;
}, 
/**
 * The graph cursor at the last committed state (SG-C) — read from the same
 * snapshot `folded` comes from, by the same one-reader-two-carriers rule:
 * `Agent.continuityCursorOf` serves BOTH `checkpoint()` and this crash
 * carrier, so a conversation cannot keep its place on one path and silently
 * lose it on the other. Passed only when the graph declared
 * `continuity: 'conversation'`.
 */
skillCursor?: string, evidenceRecovery?: EvidenceRecoveryCheckpoint, 
/**
 * The model's standings on its tool results (9.101.0), read from the same
 * committed snapshot `folded` comes from, by the same one-reader-two-carriers
 * rule — so a crash checkpoint cannot lose a ledger `checkpoint()` keeps.
 * Absent unless the run's ledger is non-empty.
 */
findingsLedger?: FindingsLedger, 
/**
 * The turn the failing run was on (honesty layers), read from the same
 * committed snapshot by the same one-reader-two-carriers rule — so a crash
 * checkpoint cannot lose the turn `checkpoint()` keeps. Absent unless a
 * layer is armed; see {@link AgentRunCheckpoint.turnNumber}.
 */
turnNumber?: number, 
/** Private evidence beside the same committed ledger; absent unless explicitly opted in. */
claimVerification?: ClaimVerificationState, negativeClaims?: NegativeCheckpoint): AgentRunCheckpoint;
/**
 * Validate a checkpoint at deserialization time. Catches forward-
 * incompatible payloads (someone tries to resume a v3 checkpoint on
 * a v1 runtime, or a corrupted JSON blob).
 *
 * Returns the checkpoint typed-narrowed; throws TypeError on
 * unknown shape.
 */
export declare function validateCheckpoint(value: unknown): AgentRunCheckpoint;
/**
 * Classify a thrown error into one of the failure-point phase
 * buckets. Heuristic — uses error name / code / message inspection.
 * Fast path returns 'unknown' so unrecognized errors still produce
 * a checkpoint (the cause itself is preserved in
 * `RunCheckpointError.cause`).
 *
 * **The FALLBACK since 8.14.0.** It only runs when the tracker observed no
 * open bracket, because an error's own text is the weakest available evidence
 * about where it happened: `TypeError: Load failed` — WebKit's entire message
 * for a failed `fetch` — matches nothing here and produced `'unknown'` on a
 * real production report while the run knew the LLM call was open. Observation
 * first, this second.
 */
export declare function classifyFailurePhase(err: Error): 'iteration' | 'tool' | 'llm' | 'unknown';
