/**
 * Event payload types — the payload interfaces behind the registered events
 * (never a hardcoded count here: the registry's counts are pinned by
 * test/events/unit/registry.test.ts).
 *
 * Pattern: Discriminated Union (Gang of Four inspired, TS-native).
 * Role:    Contract layer of Event-Driven Hexagonal Architecture.
 * Emits:   (types only).
 */
import type { CompositionKind, ContextLifetime, ContextRecency, ContextRole, ContextSlot, ContextSource, LLMProviderName, ToolProtocol, ToolCallEventIdentity } from './types.js';
import type { LLMMessage, PermissionCapability } from '../provider-contract/types.js';
import type { MemoryFlavor, MemoryStrategyKind, MemoryType } from '../memory/define.types.js';
import type { ArtifactOp, ArtifactRefusalReason } from '../artifacts/capability.js';
import type { ArtifactOrigin, ArtifactSweepReason } from '../artifacts/types.js';
import type { DatasetTimeAxis } from '../time/axis.js';
import type { ThinkingBlock } from '../thinking/types.js';
import type { LoopMoment } from '../lib/moments.js';
import type { ScriptsVerdict, TurnEndScripts } from '../lib/scriptRecord.js';
import type { InstructionDeliveryLease, ToolResultStatus } from '../semantics/toolEffects.js';
import type { ToolSemantics } from '../semantics/types.js';
import type { Basis, Expect, Standing } from '../core/agent/findings/types.js';
import type { ArgumentAsked, ArgumentCheckFailed, ArgumentClaim, ArgumentSource } from '../core/agent/arguments/rows.js';
import type { AssessmentCheck, AssessmentReason, HonestyLayer } from '../core/agent/assessment/types.js';
export interface CompositionEnterPayload {
    readonly kind: CompositionKind;
    readonly id: string;
    readonly name: string;
    readonly childCount: number;
}
export interface CompositionExitPayload {
    readonly kind: CompositionKind;
    readonly id: string;
    /** Display name supplied at composition build time (e.g., the
     *  `Sequence.create({ name: 'IntakePipeline' })` arg). Mirrors the
     *  `name` field on `CompositionEnterPayload` so consumers narrating
     *  the exit moment can reference the same human-readable identity
     *  used at entry — no name-cache required across the start/stop
     *  pair. Optional for back-compat with pre-v2.14.5 emitters. */
    readonly name?: string;
    readonly status: 'ok' | 'err' | 'break' | 'budget_exhausted';
    readonly durationMs: number;
}
export interface ParallelForkStartPayload {
    readonly parentId: string;
    readonly branches: readonly {
        id: string;
        name: string;
    }[];
}
export interface ParallelBranchCompletePayload {
    readonly parentId: string;
    readonly branchId: string;
    readonly status: 'ok' | 'err';
    readonly durationMs: number;
}
export interface ParallelMergeEndPayload {
    readonly parentId: string;
    /**
     * Which merge strategy ran. `'fn'` = `mergeWithFn` (strict, plain
     * results map). `'llm'` = `mergeWithLLM` (strict, LLM synthesis).
     * `'outcomes-fn'` = `mergeOutcomesWithFn` (tolerant, full
     * `BranchOutcome` map). Distinct values so consumers can render
     * tolerant vs strict merges differently in dashboards.
     */
    readonly strategy: 'llm' | 'fn' | 'outcomes-fn';
    readonly resultSummary: string;
    /** Number of branches whose result FED the merge — i.e., succeeded
     *  (or, in tolerant mode, those the merge fn actually consumed as
     *  `{ok: true}`). Failing branches are counted in `totalBranchCount
     *  - mergedBranchCount`. */
    readonly mergedBranchCount: number;
    /** Total number of branches declared on the Parallel — equals
     *  `mergedBranchCount` on all-success runs, larger on partial. */
    readonly totalBranchCount: number;
}
export interface ConditionalRouteDecidedPayload {
    readonly conditionalId: string;
    readonly chosen: string;
    readonly rationale?: string;
    readonly evidence?: unknown;
}
export interface LoopIterationStartPayload {
    readonly loopId: string;
    readonly iteration: number;
}
export interface LoopIterationExitPayload {
    readonly loopId: string;
    readonly iteration: number;
    readonly reason: 'budget' | 'guard_false' | 'break' | 'body_complete';
}
export interface AgentTurnStartPayload {
    readonly turnIndex: number;
    readonly userPrompt: string;
}
export interface AgentTurnEndPayload {
    readonly turnIndex: number;
    readonly finalContent: string;
    readonly totalInputTokens: number;
    readonly totalOutputTokens: number;
    readonly iterationCount: number;
    readonly durationMs: number;
    /**
     * Why the loop stopped before the model said it was done (9.56.0) — present
     * ONLY on a turn a limit cut short, absent on every normal finish.
     *
     * It rides `turn_end` because that is the event a consumer already reads to
     * render an outcome: the moment the answer exists. Reading `finalContent`
     * without it, a dashboard cannot tell a finished answer from the fragment a
     * loop stopped in the middle of — which is exactly how a half-sentence gets
     * shown to a person under a green tick.
     *
     * `wrappedUp` says which of the two this is. `true` = the run spent one more
     * call with the tools withheld and `finalContent` is that summary; absent =
     * the turn ended on whatever the last call produced. Mirrors the committed
     * `stoppedEarly` record (`agent.stoppedEarly()`), never a second source of
     * it.
     */
    readonly stoppedEarly?: {
        readonly reason: 'max-iterations' | 'cost-budget';
        readonly iteration: number;
        readonly pendingToolCalls: number;
        readonly wrappedUp?: true;
    };
    /**
     * The limits of a TYPED answer, as data — present ONLY when the agent has an
     * output schema and `.limitsTravelWithTheAnswer()`, and the run's tools
     * declared something; absent on every other turn.
     *
     * A prose answer carries its limits as a block appended to `finalContent`.
     * A typed answer cannot: it is JSON, and JSON followed by prose is not JSON.
     * So `finalContent` stays the model's answer and the same limits ride here —
     * what the tools `checked`, did not check and can never cover, folded across
     * the run (duplicates said once, every entry kept). Mirrors the committed
     * `answerCoverage` record (`agent.answerCoverage()`), never a second source
     * of it.
     */
    readonly answerCoverage?: {
        readonly checked: readonly CoverageItemPayload[];
        readonly notChecked: readonly CoverageItemPayload[];
        readonly cannotCover: readonly CoverageItemPayload[];
        /**
         * The periods the calls' results declared (honesty step 7b) — one per
         * declaring call, as declared; present only when one did. The data twin of
         * a prose answer's `Period:` lines.
         */
        readonly periods?: readonly (PeriodPayload & {
            readonly toolName: string;
            readonly toolCallId?: string;
        })[];
        /**
         * What the calls' reads found still running — its outcome not known yet —
         * one entry per declaring call, as declared; present only when one did.
         * The data twin of a prose answer's "In progress" lines.
         */
        readonly inProgress?: readonly {
            readonly toolName: string;
            readonly toolCallId?: string;
            readonly items: readonly InProgressItemPayload[];
        }[];
        /**
         * The values a tool's `assume` rule filled this turn (the inputs layer,
         * honesty layer 2) — the SAME rows, read the same way, that the prose
         * answer's "Assumed" block prints (`arguments/serve.ts` ·
         * `assumedLinesFor`): one entry per distinct (tool, argument, value), a
         * row a before-tool rewrite superseded left out. `value` is the tool's
         * own argument view; `'REDACTED'` with `hidden` when that view hides it.
         * Present only when a value was assumed.
         */
        readonly assumed?: readonly {
            readonly toolName: string;
            readonly argument: string;
            readonly value: string;
            readonly hidden: boolean;
        }[];
    };
    /**
     * The answer's standing, as data — present ONLY on an agent with
     * `.answerLayer()` (honesty layer 4), and then on every turn that ends in
     * an answer. The same projection `agentfootprint.answer.assessed` carries
     * ({@link AnswerAssessmentPayload}): the value, its rendering, the reason
     * kinds and the checks that ran; never a value or a quote. It rides
     * `turn_end` because that is the event a consumer already reads to render
     * an outcome — including a composition that mounts the agent, whose
     * dispatcher carries the agent's `turn_end` but not the `answer.*` domain.
     */
    readonly answerAssessment?: AnswerAssessmentPayload;
    /**
     * The app's words the appended standing line named calls in — registered
     * tool name → the tool's declared `displayName`. Present ONLY when the line
     * (`.answerLayer({ standingLine: true })`) named a reason's calls by a
     * declared name, so every other turn emits the exact payload it always did.
     * The words are the app's own, already in `finalContent`; the record keeps
     * them so a reader rebuilds the very line from the record.
     */
    readonly standingLineNames?: Readonly<Record<string, string>>;
    /** Runtime-composed typed negatives, with verification and delivery kept separate. */
    readonly answerNegatives?: AnswerNegativesPayload;
    /**
     * The scripts the library attached to the answer — present ONLY under
     * `.scriptTravelsWithTheAnswer()`, on every answer. Each `shown` entry is
     * the program a call SUBMITTED to a declared code-running tool, read from
     * the run record (never retyped by the model), with the call's label read
     * from its execution record: `exitCode` only when the runner supplied one,
     * `truncated` when the text is cut at `maxBytes`. `omitted` counts the
     * calls not attached (the cap, a program over the bound, a call past the
     * per-turn rows). A prose answer also carries them as text after its `---`
     * separator; a typed answer carries them here only. `text` is a
     * model-written string: under any redaction policy it is served as the
     * placeholder, and the label and the counts survive.
     */
    readonly scripts?: TurnEndScripts;
    /**
     * The answer was REFUSED — present ONLY then. Under the evidence gate's
     * `'rails'` posture (`.namesAndNumbersFromEvidence()`), an answer stating
     * values no tool result carried is not delivered: `run()` raises
     * `UnsupportedValuesError`. `finalContent` stays for the record (the answer
     * account explains the refusal from it), but it is NOT the agent's answer —
     * a consumer rendering an outcome shows the refusal, never this text.
     */
    readonly refused?: {
        readonly by: 'evidence-rails';
    };
}
/**
 * A turn's budget ran out while the model was still working (9.56.0).
 *
 * Fires ONCE per turn, from the Route decider, at the boundary that refused to
 * run the tool calls the model just asked for — the same moment
 * `cost.limit_hit { kind: 'max_iterations' }` fires, and beside it rather than
 * instead of it: that event reports a LIMIT being crossed, this one reports
 * what the run then DID about it.
 *
 * `action` is the whole point. `'wrapped-up'` means one more call went out with
 * the tools withheld and its answer is the turn's answer; `'cut-short'` means
 * the turn ended on whatever text rode the last call — a fragment, mid-task,
 * more often than not. A consumer that only ever sees `'wrapped-up'` is
 * looking at a healthy default; one seeing `'cut-short'` is looking at a turn
 * whose answer nobody should trust as complete.
 */
export interface AgentBudgetExhaustedPayload {
    /** Which budget ran out. Only `'max-iterations'` is ever wrapped up. */
    readonly reason: 'max-iterations' | 'cost-budget';
    /** The iteration the budget ran out on. */
    readonly iteration: number;
    /**
     * The budget itself — present ONLY for `'max-iterations'`, where it is
     * `maxIterations`.
     *
     * Absent for `'cost-budget'`, deliberately: the run's committed state carries
     * the cumulative SPEND, not the cap, and reporting spend as `limit` is the
     * exact mistake `cost.limit_hit` documents and avoids. The real USD cap rides
     * that event, emitted the moment the budget was crossed.
     */
    readonly limit?: number;
    /** How many tool calls the model asked for that will never run. */
    readonly pendingToolCalls: number;
    /** What the run did about it. */
    readonly action: 'wrapped-up' | 'cut-short';
}
export interface AgentIterationStartPayload {
    readonly turnIndex: number;
    readonly iterIndex: number;
}
export interface AgentIterationEndPayload {
    readonly turnIndex: number;
    readonly iterIndex: number;
    /**
     * On the main loop, the calls the turn proposed. On a resume leg (9.113.0),
     * the brackets THAT leg closed: the paused call plus each sibling the batch
     * settlement answered — so a batch of three paused on its middle call
     * reports 2, and the calls before the pause are counted by no
     * `iteration_end` (their brackets closed on the leg that paused). For the
     * batch's size, read `tool_start.parallelCount`. See `src/core/README.md` ·
     * "A batch that pauses settles its un-dispatched siblings".
     */
    readonly toolCallCount: number;
    /** Conversation history (LLM messages) at the END of this
     *  iteration. Captured by `agent.run()` for fault-tolerant
     *  resume — `RunCheckpointError.checkpoint` snapshots this so
     *  `agent.resumeOnError(...)` can replay from the last good
     *  iteration. Optional for back-compat with v2.x recorders that
     *  subscribed without expecting this field. Typed as the messages it
     *  holds, so its field names and roles are the library's own words on a
     *  redacted record (`redaction/knownStrings.ts`). */
    readonly history?: ReadonlyArray<LLMMessage>;
}
/**
 * The `route_decided` rationales that are FIXED sentences — the library's own
 * words, so a redacted record keeps them (`redaction/knownStrings.ts`). A
 * rationale composed per turn (a count of calls, a skill's name) is a free
 * string there, kept out under a policy like any other.
 */
export type FixedRouteRationale = 'LLM produced no tool calls — final answer' | 'maxIterations reached — forcing final' | 'costBudget reached (onExceed: halt) — forcing final' | 'tools are off after the evidence revision (answer validation enforces) — forcing final';
/**
 * The `pause.request` reason the library writes when the pause carries none —
 * the library's own words (`redaction/knownStrings.ts`). A reason the pause
 * carries is a free string.
 */
export type FixedPauseReason = 'stage requested pause';
export interface AgentRouteDecidedPayload {
    readonly turnIndex: number;
    readonly iterIndex: number;
    /**
     * The branch the turn took. `'output-retry'` (7.26) appears only on an
     * agent built with `.outputSchema(parser, { retries })`, and only on a
     * turn whose answer failed the schema with retries left — the loop is
     * about to ask again rather than finish. `'step-nudge'` (9.18.0) appears
     * only on an agent with a stepped skill, and only on a turn whose
     * would-be-final answer left declared steps unrun with the one teaching
     * nudge still unspent. `'evidence-recheck'` (9.35.0) appears only on an
     * agent built with `.namesAndNumbersFromEvidence({ posture: 'guard' |
     * 'rails' })`, and only on a turn whose would-be-final answer stated values
     * no tool result carried, with the one revision still unspent. `'wrap-up'`
     * (9.56.0) appears only on a turn whose `maxIterations` ran out while the
     * model was still asking for tools, on an agent that did not set
     * `wrapUpAtMaxIterations: false` — the loop is about to spend one last call
     * with the tools withheld so the turn ends with a summary.
     */
    readonly chosen: 'tool-calls' | 'final' | 'output-retry' | 'step-nudge' | 'evidence-recheck' | 'negative-recheck' | 'wrap-up';
    readonly rationale?: FixedRouteRationale | (string & Record<never, never>);
}
export interface AgentHandoffPayload {
    readonly fromAgentId: string;
    readonly toAgentId: string;
    readonly reason?: string;
    readonly viaProtocol?: 'native' | 'mcp' | 'http';
}
export interface LLMStartPayload {
    readonly iteration: number;
    readonly provider: LLMProviderName;
    readonly model: string;
    readonly systemPromptChars: number;
    /**
     * The ASSEMBLED system prompt, verbatim as sent to the provider (9.50.0) —
     * every injection piece joined exactly the way the wire saw it. **OPT-IN,
     * default OFF**: present only when the run was built with
     * `recordSystemPrompt: true` (`AgentOptions` / `LLMCallOptions`).
     *
     * PRIVACY: the assembled prompt routinely carries what its pieces carry —
     * skill bodies, RAG passages, memory recalls, per-user instructions. With
     * the dial on, all of it lands in every event sink and every recording
     * (`recordRun` → `persistRecording`), so treat those artifacts as being as
     * sensitive as the prompt itself. Absent, the event keeps its exact prior
     * bytes: only `systemPromptChars` (the length) is recorded, and the
     * assembled string is honestly NOT in the recording.
     */
    readonly systemPromptText?: string;
    readonly messagesCount: number;
    readonly toolsCount: number;
    /**
     * The tool CATALOG the model saw for this call — what was at its disposal when
     * it chose (the menu behind its tool-selection reasoning). One `{ name,
     * description }` per tool sent to the provider, in request order. Absent when
     * the call had no tools. The structured "what the model saw" payload: pair it
     * with the iteration's reasoning to debug WHY a tool was (or wasn't) picked.
     * Names + descriptions only — full input schemas live in the snapshot.
     */
    readonly tools?: readonly {
        readonly name: string;
        readonly description?: string;
    }[];
    readonly estimatedPromptTokens?: number;
    readonly temperature?: number;
    readonly providerRequestRef?: string;
    /**
     * WHICH BRAIN answered, when a brain rung won (9.19.0 — "the cursor picks
     * the brain"). `via: 'skill'` = the active skill's declared brain served
     * this call (`skillId` names the tenure); `via: 'escalation'` = the turn
     * crossed its declared refusal threshold and the escalation brain is
     * serving the rest of it. ABSENT whenever the agent's own configured /
     * default model answered — an agent without brains keeps its exact prior
     * event bytes. `provider` + `model` above always report what was actually
     * called, whichever rung supplied them.
     */
    readonly brain?: {
        readonly via: 'skill' | 'escalation';
        readonly skillId?: string;
    };
}
export interface LLMEndPayload {
    readonly iteration: number;
    /** Draft text, or an empty string when contentWithheld is true. */
    readonly content: string;
    /** An output policy withheld draft text. This does not mean the model returned no text. */
    readonly contentWithheld?: true;
    readonly toolCallCount: number;
    readonly usage: {
        readonly input: number;
        readonly output: number;
        readonly cacheRead?: number;
        readonly cacheWrite?: number;
        /**
         * 9.29.0 — reasoning tokens the model spent on this call, when the
         * provider reports them (`LLMResponse.usage.thinking`).
         *
         * This field is the fix for a gap a field trial found by walking into it:
         * a 256-token Gemini stream produced ONE visible chunk, and the run only
         * made sense once the numbers were read — `input 21, output 9,
         * thinking 243`. The provider had been reporting that third number all
         * along and this payload's type dropped it, so the event that a consumer
         * actually subscribes to could not explain its own run.
         *
         * **Not inside `output`.** On Gemini `candidatesTokenCount` counts visible
         * tokens only; thinking is a separate, billed line item. A cost estimate
         * built from `input + output` under-counts a thinking model by whatever it
         * thought. `cost.tick` deliberately does NOT fold it in — `PricingTable`
         * prices four kinds and thinking is not one of them, and inventing a rate
         * would be this library guessing at somebody's invoice.
         *
         * Undefined when the provider does not report thinking tokens, which is
         * most calls.
         */
        readonly thinking?: number;
    };
    readonly stopReason: string;
    readonly durationMs: number;
    readonly providerResponseRef?: string;
}
export interface LLMTokenPayload {
    readonly iteration: number;
    readonly tokenIndex: number;
    readonly content: string;
}
export interface ToolStartPayload {
    readonly toolName: string;
    readonly toolCallId: string;
    readonly args: Readonly<Record<string, unknown>>;
    readonly parallelCount?: number;
    readonly protocol?: ToolProtocol;
    /**
     * 9.113.0 — present only on the bracket of a call that was NEVER
     * DISPATCHED: a batch paused on an earlier call, and the resume answered
     * this one with a fixed sentence instead of running it
     * (`core/agent/stages/toolCalls.ts` · `bracketSettled`, the one stamping
     * site). `pausedCall` names the call in the same batch the run paused on.
     * No gate judged this call and no tool ran; `args` are the ones it would
     * have run with.
     *
     * The same fact, the same shape, as the history message's marker — typed off
     * `LLMMessage.notDispatched`, so there is one definition. Read it rather than
     * inferring anything from the bracket's `durationMs: 0` or its sentence.
     *
     * Absent on every other bracket.
     */
    readonly notDispatched?: LLMMessage['notDispatched'];
}
/**
 * One progress report from inside a still-running tool call (9.52.0) — filed
 * by `ctx.progress(payload)` from within `tool.execute`, in call order, always
 * BETWEEN this call's `stream.tool_start` and its `stream.tool_end`.
 *
 * The split is deliberate: the three identity fields are stamped by the
 * FRAMEWORK from the dispatch it is already holding (a tool cannot claim to be
 * another call, another tool, or another iteration), and `payload` is the tool
 * author's data forwarded verbatim. Nothing here is ever shown to the model —
 * this is the telemetry channel, not the result channel.
 *
 * Absent entirely for every tool that never reports: a run whose tools call
 * nothing files no `tool_progress` events at all.
 */
export interface ToolProgressPayload {
    /** The invocation this report belongs to — matches `stream.tool_start.toolCallId`. */
    readonly toolCallId: string;
    /** The tool that reported. */
    readonly toolName: string;
    /** The ReAct iteration this call was dispatched on (`0` where there is no loop). */
    readonly iteration: number;
    /**
     * The tool author's payload, verbatim — whatever shape they chose
     * (`{ done: 3, total: 12 }`, a status string, a partial row). The library
     * neither reads it nor normalizes it; it must survive `structuredClone`,
     * because it lands in every event sink and every recording.
     */
    readonly payload: unknown;
}
export interface ToolEndPayload {
    readonly toolCallId: string;
    readonly result: unknown;
    readonly error?: boolean;
    readonly durationMs: number;
    /**
     * The tool's OWN declared outcome (9.19.0) — present only when the tool
     * returned a result envelope carrying `status`. Normalized vocabulary
     * (`'denied'` must never read like `'success'`); route edges key on it
     * via `onToolStatus`. Absent for every tool that never opted in.
     */
    readonly status?: ToolResultStatus;
    /**
     * 9.113.0 — present only when this call was NEVER DISPATCHED: the batch
     * settlement closed its bracket (`core/agent/stages/toolCalls.ts` ·
     * `bracketSettled`). `result` is then the library's fixed sentence (the one
     * the model read), `durationMs` is `0`, and `error` is ABSENT.
     *
     * `error` says a call FAILED — the tool threw or reported a failure, or the
     * call could not run as written (an args-validation rejection, a `wants`
     * block, a consent the tool needed and did not get, an unknown tool). A
     * settled call did not fail: the library decided not to dispatch it, the
     * way it decides for a call a permission policy denies or halts, whose
     * brackets carry no `error` either. This field is the owner of WHY there is
     * no result, and a reader that counts outcomes reads it: a bracket that
     * carries it is neither a success nor a failure of the tool.
     *
     * Same shape as `ToolStartPayload.notDispatched` and as the history
     * message's marker (typed off `LLMMessage.notDispatched`). Absent on every
     * other bracket.
     */
    readonly notDispatched?: LLMMessage['notDispatched'];
    /**
     * What the MODEL read for this call, when it differs from `result`: an
     * `onToolResult` link rewrote or denied it, the `maxToolResultChars` cap
     * cut the model's channel alone, or the library removed the record-only
     * coverage fields (`short`, `kind` — see `CoverageItem`) from a coverage
     * envelope before serving it. `result` stays the tool's own answer — this
     * is the other half of that split, which until now lived only in history.
     * Absent when the model read `result` itself (the common case; the two
     * channels are then one reference). Stamped on all five dispatch paths.
     *
     * Cost, stated: a call whose tool declares `short` or `kind` carries its
     * envelope twice here (`result` + `modelResult`), so the extra bytes are
     * the size of that envelope — about 4 KB for a typical absence, and as
     * large as the payload a ledger wraps. Under result PLACEMENT a difference
     * that is only this strip counts as one value: both channels carry the one
     * ticket and nothing is stamped. A tool that declares neither gets no stamp
     * and no new byte.
     */
    readonly modelResult?: unknown;
    /**
     * The NAMES of the argument keys whose value the tool ran with differs from
     * the proposal `stream.tool_start` carried: set, rewritten or removed by an
     * `onToolCall` link, or hidden by the tool's own redaction policy
     * (`flowchartAsTool({ redact })`, `runbookAsTool({ redact })` —
     * `core/toolShownArgs.ts` · `shownArgsOf`). Names only, never values: a
     * link may ADD a value (a server-side key) the model never saw, and an event
     * goes to every sink attached, each serializing what it gets. Taken BEFORE
     * the tool runs, so nothing the tool writes into its arguments reaches it.
     * A reader exporting the call's arguments shows these keys' values as
     * withheld. Absent when the tool ran with the proposal as it was.
     *
     * Stamped on the batch-dispatch bracket only — the one whose `tool_start`
     * precedes it in the same run.
     */
    readonly changedArgKeys?: readonly string[];
    /**
     * A configured RULE refused the call, so the tool never ran: a permission
     * policy denied or halted it, or an `onToolCall` link denied it. `result` is
     * then the refusal the model read, and `error` is absent — this field is
     * what tells such a bracket from a tool that ran and answered.
     *
     * NOT a general "never ran" marker, and its absence proves nothing: a call
     * that could not run with no rule involved (an unknown tool name, an args
     * rejection, a `wants` or credential block) carries `error: true` instead,
     * a settled bracket carries `notDispatched`, and a resumed leg's bracket
     * carries neither. Batch-dispatch bracket only.
     */
    readonly notExecuted?: true;
}
export interface ContextInjectedPayload {
    readonly contentSummary: string;
    readonly contentHash: string;
    readonly rawContent?: string;
    readonly slot: ContextSlot;
    readonly asRole?: ContextRole;
    readonly asRecency?: ContextRecency;
    readonly position?: number;
    readonly sectionTag?: string;
    readonly source: ContextSource;
    readonly sourceId?: string;
    readonly upstreamRef?: string;
    readonly reason: string;
    readonly retrievalScore?: number;
    readonly rankPosition?: number;
    readonly threshold?: number;
    readonly budgetSpent?: {
        readonly tokens: number;
        readonly fractionOfCap: number;
    };
    readonly expiresAfter?: ContextLifetime;
}
export interface ContextEvictedPayload {
    readonly slot: ContextSlot;
    readonly contentHash: string;
    readonly reason: 'budget' | 'stale' | 'low_score' | 'policy' | 'user_revoked';
    readonly survivalMs: number;
}
export interface ContextSlotComposedPayload {
    readonly slot: ContextSlot;
    readonly iteration: number;
    readonly budget: {
        readonly cap: number;
        readonly used: number;
        readonly headroomChars: number;
    };
    readonly sourceBreakdown: Readonly<Partial<Record<ContextSource, {
        readonly chars: number;
        readonly count: number;
    }>>>;
    readonly orderingStrategy?: string;
    readonly droppedCount: number;
    readonly droppedSummaries: readonly string[];
}
/**
 * Fired when a budget is exceeded — by a context SLOT composing over its
 * `budgetCap`, or by a window STRATEGY measuring the window over its
 * `thresholdTokens`.
 *
 * ## Read `unit` before you read a number (8.14.0)
 *
 * Two emitters share this event name and the `slot: 'messages'` value, and
 * **they do not count in the same unit**:
 *
 * | emitter | what it measures | `unit` |
 * |---|---|---|
 * | the three context slots (`contextBudget`) | `String.length` of what it composed | `'chars'` |
 * | a window strategy (`.window()` / `.compaction()`) | the provider's reported input tokens | `'tokens'` |
 *
 * Since `contextBudget` is on by default, one subscriber routinely receives
 * both — and before 8.14.0 nothing in the payload told them apart, so
 * "cap 200, projected 258" could be 258 characters or 258 tokens. `unit` is
 * the answer; `cap` and `projected` are the same two numbers under names that
 * do not assert an untrue one.
 *
 * `planAction: 'none'` means no mitigation was performed — nothing was
 * evicted or truncated and the full content still went to the LLM. That
 * is what the built-in slots report: they never truncate, so the event is
 * the signal that the budget is not being respected.
 */
export interface ContextBudgetPressurePayload {
    readonly slot: ContextSlot;
    /** How far over the cap, in {@link unit}. Never carried a unit in its name. */
    readonly overflowBy: number;
    readonly planAction: 'evict' | 'summarize' | 'abort' | 'none';
    /**
     * What {@link cap}, {@link projected} and {@link overflowBy} are counted in
     * (8.14.0). `'chars'` from a context slot, `'tokens'` from a window
     * strategy. Branch on this before comparing any of the three numbers to
     * anything.
     */
    readonly unit: 'chars' | 'tokens';
    /**
     * The budget that was exceeded, in {@link unit}.
     *
     * Called `capTokens` until 9.0.0 — a name that asserted tokens on a channel
     * that is CHARS half the time, since `contextBudget` is on by default and a
     * slot counts `String.length`. Both spellings shipped through 8.x with the
     * identical value; 9.0.0 keeps only this one.
     */
    readonly cap: number;
    /** What was measured, in {@link unit}. Called `projectedTokens` until 9.0.0
     *  — see {@link cap}. */
    readonly projected: number;
}
/**
 * Fired once per iteration by the Injection Engine after it evaluates every
 * Injection's trigger — BEFORE the Context fork routes the survivors into the
 * three slots. This is the "what was considered, what won, what was skipped
 * and why" signal; `context.slot_composed` is its downstream counterpart
 * ("what actually landed in each slot"). Pure observability — no flow stage
 * reads it.
 */
/** A guard threshold value as the record carries it — plain data only. */
export type SkillGuardValueRecord = string | number | boolean | null | ReadonlyArray<string | number | boolean | null>;
/**
 * One route-edge guard evaluation, as `cursorMove.guard` / `guardsClosed`
 * carry it (9.51.0): the guarded edge, the tool result it judged, the
 * verdict, and per-condition evidence — each declared condition
 * (`key`/`op`/`value`), the summarized value it was judged against (bounded
 * to 80 chars — evidence, not a transcript), and whether it passed. The
 * operator vocabulary deliberately mirrors footprintjs's `WhereFilter`
 * (`eq/ne/gt/gte/lt/lte/in/notIn`).
 */
export interface SkillGuardEvaluationRecord {
    readonly from: string;
    readonly to: string;
    readonly toolName: string;
    readonly toolCallId?: string;
    readonly verdict: boolean;
    readonly conditions: ReadonlyArray<{
        readonly key: string;
        readonly op: string;
        readonly value: SkillGuardValueRecord;
        readonly actualSummary: string;
        readonly passed: boolean;
    }>;
}
export interface ContextEvaluatedPayload {
    readonly iteration: number;
    /** Number of injections active this iteration. */
    readonly activeCount: number;
    /** Number skipped (predicate false counts as neither — only errors/unknown land here). */
    readonly skippedCount: number;
    /** Total injections evaluated (the full declared list). */
    readonly evaluatedTotal: number;
    /** Ids of the active injections, in evaluation order. */
    readonly activeIds: readonly string[];
    /** Why each skipped injection was skipped (errors / unknown trigger kinds). */
    readonly skippedDetails: readonly {
        readonly id: string;
        /** `'unknown-fact'` (9.57.0): a templated instruction named a run-time
         *  fact this evaluation could not supply, so it was skipped whole.
         *  `'parked'` (9.58.0): a member of a map the mount kernel parked —
         *  served without corroboration for the grace window; the map's own
         *  cursor is untouched and evidence re-engages it.
         *  `'hidden'`: a skill the caller's role may not see (`skill_read`),
         *  refused whatever tried to activate it. */
        readonly reason: 'predicate-threw' | 'unknown-trigger-kind' | 'unknown-fact' | 'parked' | 'hidden';
        readonly error?: string;
    }[];
    /** Count of active injections by trigger kind (always / rule / on-tool-return / llm-activated). */
    readonly triggerKindCounts: Readonly<Record<string, number>>;
    /**
     * The Skill CATALOG the LLM was offered this turn — every registered Skill's
     * `id` + `description` (the same text that lands in the `read_skill` tool
     * description). Lets observers pair "what was offered" against "what the LLM
     * chose" (`read_skill` → `activatedInjectionIds`) when debugging a missed or
     * wrong activation. Empty when no Skills are registered. Static across turns.
     */
    readonly skillCatalog: readonly {
        readonly id: string;
        readonly description: string;
    }[];
    /**
     * Routing PROVENANCE for the active injections that came from a `skillGraph()`
     * — *why* each was reached. One entry per active skill-graph injection (a
     * decision-tree leaf, a flat entry, or a route edge); absent when no active
     * injection carries skill-graph metadata. The structured counterpart to the
     * `context.routed` commentary line — lets the lens show the decision path, the
     * matched predicate, and the tools a route unlocked. Structural shape (mirrors
     * `SkillRouting` from the injection engine; events stay decoupled from it).
     */
    readonly routing?: readonly {
        readonly injectionId: string;
        readonly flavor: string;
        /** `'tree' | 'entry' | 'route' | 'model'`. */
        readonly via: string;
        /** Decision path (tree only): predicates root→leaf + branch taken. */
        readonly path?: readonly {
            readonly label: string;
            readonly branch: string;
        }[];
        /** Entry/route edge caption. */
        readonly label?: string;
        /** Source skill id (route only). */
        readonly from?: string;
        /** Compiled trigger kind for a route. */
        readonly triggerKind?: string;
        /** Tool names this injection unlocked. */
        readonly tools?: readonly string[];
    }[];
    /**
     * How the skill-graph CURSOR moved on THIS iteration — the winning clause of the
     * graph's one cursor resolver, reported by it (8.5.0). Present only for an agent
     * built with `.skillGraph()` (and a graph new enough to explain itself).
     *
     * Distinct from `routing[]` above, and the distinction is the point: `routing[]`
     * is per-SKILL build-time provenance ("how is this skill reachable"), while this
     * is per-HOP runtime truth ("what moved us, this turn"). `by: 'model-pick'` is the
     * case the two disagree on — a `read_skill` pick the gate accepted, into a skill
     * that also has a declared edge whose predicate never fired.
     */
    readonly cursorMove?: {
        readonly from?: string;
        readonly to?: string;
        /** `'entry' | 'route' | 'model-pick' | 'intent' | 'continuity' | 'stay' | 'none'`. */
        readonly by: string;
        /** The turn-start MENU this model pick resolved (SG-C) — present only on
         *  the iteration an accepted pick closed an outstanding menu. */
        readonly offered?: readonly string[];
        /** The accepted pick was reachable but NOT on the offered menu — the
         *  model's divergence, on the record (data under `'assist'`, a refusal
         *  under `'guard'`/`'rails'`, which never reach here). */
        readonly declinedOffer?: boolean;
        /** WHAT the message said that routed this hop (9.28.0) — present only for a
         *  `by: 'entry'` move a DATA matcher (`match:` — RegExp / `{ keywords }` /
         *  `{ all }`) decided. `text` is the matched substring of the USER message,
         *  whitespace-collapsed and bounded to 80 characters (ellipsis included);
         *  `keyword` names WHICH declared keyword hit, for the keywords arm.
         *  Absent for `when` predicates (opaque code), unconditional entries, and
         *  every scorer verdict (whose evidence is its scores). */
        readonly witness?: {
            readonly text: string;
            readonly keyword?: string;
        };
        /**
         * The EVIDENCE the winning data GUARD routed on (9.51.0) — present only
         * for a `by: 'route'` move whose firing edge declared a `guard:`. The
         * full per-condition evaluation (verdict `true`): which edge, which tool
         * result it judged, and every declared condition with the summarized
         * value it saw. Structural shape (mirrors the graph's `GuardEvaluation`;
         * events stay decoupled from it).
         */
        readonly guard?: SkillGuardEvaluationRecord;
        /**
         * The data guards that REFUSED this iteration (9.51.0) — guarded edges
         * out of the cursor whose other declared conditions a tool result met
         * and whose guard said no (verdict `false`; at most one record per
         * edge). Rides whatever move resulted — including a `'stay'`, where it
         * answers "why didn't my guarded edge fire?" with the conditions and
         * values that closed it. Absent when no guard decided anything.
         */
        readonly guardsClosed?: readonly SkillGuardEvaluationRecord[];
        /**
         * The REACHABLE set from the cursor this move landed on (9.50.0) — the
         * skill ids the `read_skill` gate will admit on THIS iteration: declared
         * hops out of the landed cursor plus the open skills (`llm-activated`
         * skills the graph wires no edge to). It is the same set, from the same
         * two resolvers, that builds the `read_skill` offer prose and any
         * `skill.rejected.allowed` list this iteration — as DATA, so no consumer
         * has to parse the menu sentence back into ids. `[]` is a fact (a dead
         * end: no hop and no open skill is admissible); ABSENT means the graph
         * predates `reachableSkills` and the set was not on the record.
         */
        readonly reachable?: readonly string[];
    };
    /**
     * Skill-graph entries whose own `when` matched this iteration and which the cursor
     * law kept OFF the wire (8.15.0). A conditional entry is active exactly while the
     * cursor is on it — `when` says where a turn STARTS, not what stays loaded — so an
     * entry whose rule still matches while the graph is elsewhere is suppressed.
     *
     * Read it beside `cursorMove`, which names where the graph went instead: together
     * they answer "why isn't my entry loading?" without re-running a predicate to
     * guess. Absent when nothing was suppressed, and for every non-skill-graph run.
     *
     * Distinct from `agentfootprint.skill.reroute_superseded`, which reports a DISCRETE
     * broken promise (a `read_skill` pick the gate accepted and a declared edge
     * outranked). This is a CONTINUOUS condition — an entry whose rule stays true while
     * the cursor is parked elsewhere is suppressed on every iteration — so it rides the
     * per-iteration event.
     */
    readonly supersededIds?: readonly string[];
}
export interface ErrorFatalPayload {
    readonly error: string;
    readonly stage: string;
    readonly scope: string;
}
export interface PauseRequestPayload {
    readonly reason: FixedPauseReason | (string & Record<never, never>);
    readonly questionPayload: Readonly<Record<string, unknown>>;
}
export interface PauseResumePayload {
    /**
     * The reply the run resumed with. For the inputs layer's own ask
     * (`awaitingInput.context.agentfootprint.ask === 'arguments'`) every
     * answered value reads `'REDACTED'` — the reply's shape (request id, field
     * ids) travels, the person's values do not, because they fill arguments a
     * tool's view may hide; the `answered` argument rows carry what each tool's
     * view allows.
     */
    readonly resumeInput: Readonly<Record<string, unknown>>;
    readonly pausedDurationMs: number;
    /** The registered component the paused ask nominated to collect its answer
     *  (9.24.0) — read from the checkpoint's own pause payload, whichever pause
     *  kind carried it. Absent when the ask was prose-only. */
    readonly componentId?: string;
}
/** A tool declared `checkIn` and it tripped — the run paused to ask a human,
 *  with the evidence pack riding the ask. Emitted BEFORE the tool executes. */
export interface CheckInRequestPayload {
    readonly toolName: string;
    readonly toolCallId: string;
    readonly iteration: number;
    /** The typed ask + evidence pack (`CheckInRequest`). JSON/clone-safe. */
    readonly request: Readonly<Record<string, unknown>>;
}
/** A human answered a pending check-in. Emitted on resume, BEFORE the tool
 *  runs (approve) or the decline result lands. */
export interface CheckInDecisionPayload {
    readonly toolName: string;
    readonly toolCallId: string;
    readonly iteration: number;
    /** The request the decision answered — `CheckInRequest.id` of the ask it
     *  closes, so the record joins each decision to the one request it was for. */
    readonly requestId: string;
    readonly approved: boolean;
    readonly by: string;
    readonly note?: string;
    /** The registered component that COLLECTED this decision (9.24.0) — the
     *  `componentId` the ask carried. Absent when the ask was prose-only. The
     *  decision itself is unchanged: a structured fact, never parsed from the
     *  words a screen rendered. */
    readonly componentId?: string;
}
/**
 * One link of a `toolMiddleware` / `messageMiddleware` chain answered.
 *
 * Its own domain rather than a `permission.check` with a different label:
 * a middleware decision is not a permission check, and reporting it as one
 * would make every consumer reading that channel believe a checker they
 * never configured had fired.
 *
 * Deliberately carries no `before` / `after` — the values live in the
 * committed `middlewareDecisions` ledger, under whatever redaction the run
 * configured. An event stream is a fan-out to sinks we do not control, and
 * a scrubbed value should not leave the run through it.
 */
export interface MiddlewareDecisionPayload {
    readonly middleware: string;
    /** Where in the loop this happened — the vocabulary `.act()` is keyed on. */
    readonly moment: LoopMoment;
    /** The 7.18 spelling of the same fact. Narrow on `moment`. */
    readonly at: 'tool' | 'message';
    readonly phase?: 'input' | 'output';
    readonly toolName?: string;
    readonly toolCallId?: string;
    readonly iteration: number;
    readonly outcome: 'allow' | 'deny' | 'ask';
    /** True when this link changed the value the chain carries forward. */
    readonly changed: boolean;
    /** The transform's `why`, the denial's `reason`, or the ask's `question`. */
    readonly why?: string;
    /** The registered component that COLLECTED this decision (9.24.0). Present
     *  only on the resume-side rows of an `ask` that carried one. */
    readonly componentId?: string;
}
/**
 * The model declared a BASIS for one tool call — `_findings.basis` peeled
 * off the call's arguments before anything else read them (9.101.0).
 *
 * Identities, enums and counts ONLY, by the `MiddlewareDecisionPayload` law
 * one screen up: the row itself lives in the committed `findingsLedger`
 * key under whatever redaction the run configured, and an event stream is
 * a fan-out to sinks we do not control. `malformed` counts the entries
 * `splitFindings` dropped from this call's declaration — never coerced,
 * never inferred — so a sink can see that the model tried and missed
 * without seeing what it wrote.
 *
 * Fired by `recordFindings`, one per basis row, BEFORE `stream.tool_start`
 * for the same `toolCallId`. Never fired on an agent without `.findings()`.
 */
export interface FindingsDeclaredPayload {
    readonly toolName: string;
    /** The provider's id for the call — what a reader joins to `tool_start`. */
    readonly toolCallId: string;
    readonly iteration: number;
    readonly basis: Basis;
    readonly expect?: Expect;
    /**
     * The call declared a `proposition` (what it tests) — a FLAG, never the
     * text, by the same law as `malformed`: the line itself lives on the
     * `BasisRow` in the committed key. Absent when none was declared.
     */
    readonly hasProposition?: true;
    /** Malformed `_findings` entries dropped from this call's declaration. */
    readonly malformed?: number;
}
/**
 * The model declared a STANDING for one PREVIOUS tool result — `fact`,
 * `open`, `ruled-out` or `noise` — on its next tool call or as a top-level
 * `_findings.previous` on a JSON answer (9.101.0).
 *
 * `toolCallId` names the RESULT being stood on, not the call that carried
 * the declaration; `declaredOn` says which door the declaration came
 * through. `assertionCount` is the number of assertions the row carries
 * (always 0 for `noise`); the assertions themselves, `settles`, `line` and
 * every other word the model wrote stay in the committed key — the same
 * law as `FindingsDeclaredPayload`. `conflictKeys` lists the assertion keys
 * whose conflict row THIS write created; a key that already had one is not
 * repeated. `unknownId` marks a standing for an id the previous batch did
 * not contain — recorded, never resolved.
 *
 * `agrees` (9.104.0, `.findings({ judge })`) compares this standing with the
 * JUDGE's current judgment of the same result and is present exactly when
 * a `JudgmentRow` exists for it: the judge files before the model call
 * that declares, so this event is the one moment both readings exist. A
 * comparison made for the sink, never written to the record, where the two
 * sources stay two rows. Absent without a judge, after a failed judgment,
 * and for an `unknownId`.
 *
 * Fired by `recordFindings`, one per standing row, in declaration order.
 */
export interface FindingsStandingPayload {
    /** The id of the tool RESULT this standing is about. */
    readonly toolCallId: string;
    /** Absent when `unknownId` — the batch had no result to name. */
    readonly toolName?: string;
    /** The DECLARING iteration; the result's own stage is derivable from the
     *  commit log by `toolCallId`, never guessed here. */
    readonly iteration: number;
    readonly standing: Standing;
    readonly declaredOn: 'tool-call' | 'answer';
    readonly assertionCount: number;
    /** Assertion keys whose conflict row this write created. */
    readonly conflictKeys?: readonly string[];
    /** The named id was not in the previous batch. */
    readonly unknownId?: true;
    /** Whether this standing equals the judge's current one for the result; present only when one exists. */
    readonly agrees?: boolean;
}
/**
 * A SECOND SOURCE judged one tool result (9.104.0, `.findings({ judge })`):
 * the calibrated classifier's standing for the result against the call's
 * declared proposition — or the user's question when none was declared —
 * with its confidence and cost. The row (`JudgmentRow`) holds the whole
 * distribution and the provider's model string in the committed
 * `findingsLedger`; this payload carries identities, enums and NUMBERS only,
 * never the state the judge read.
 *
 * No `agrees` here: the judge files BEFORE the model call that could
 * declare a standing on this result, so at this event the model's reading
 * does not exist yet. The comparison rides `FindingsStandingPayload.agrees`
 * instead, the one moment both readings exist. Fired by `recordFindings`,
 * one per judgment row, once the result's `toolResults` entry exists and
 * before the next model call. Never fired without a configured judge.
 */
export interface FindingsJudgedPayload {
    readonly toolCallId: string;
    readonly toolName: string;
    readonly iteration: number;
    readonly against: 'proposition' | 'question';
    readonly standing: Standing;
    readonly confidence: number;
    readonly latencyMs: number;
    readonly inputTokens?: number;
    readonly outputTokens?: number;
}
/**
 * The judge was asked about one tool result and produced no answer
 * (9.104.0): the provider's HTTP status when there was one, and the latency
 * spent. The error text lives on the `JudgmentErrorRow`; no standing is
 * guessed, and the run continues — the judge is advisory.
 */
export interface FindingsJudgeFailedPayload {
    readonly toolCallId: string;
    readonly toolName: string;
    readonly iteration: number;
    readonly status?: number;
    readonly latencyMs: number;
}
/**
 * The model USED a value that came only from results it had itself declared
 * `open`, `noise` or `ruled-out` (9.110.0, `.findings()` beside
 * `.namesAndNumbersFromEvidence()`): in its answer (`declaredOn: 'answer'`)
 * or as an argument of a later tool call (`'tool-call'`, with the call's
 * `toolCallId`). Declared standings plus the evidence corpus's provenance —
 * nothing inferred, no judge. The row (`ContingentRow`) holds the value and
 * every carrier with its standing in the committed `findingsLedger`; this
 * payload carries the moment, how many results carried the value, the
 * DISTINCT standings among them and the value's length — identities, enums
 * and numbers only, never the token. Fired by `recordFindings`, one per
 * contingent row: at the answer, after the evidence gate's verdict; at
 * dispatch, after the call's basis row and before `stream.tool_start`.
 * Never fired unless BOTH doors are armed.
 */
export interface FindingsContingentPayload {
    readonly iteration: number;
    readonly declaredOn: 'tool-call' | 'answer';
    /** The dispatching call, when the value rode its arguments; absent for the answer. */
    readonly toolCallId?: string;
    /** How many results carried the value — every one judged non-fact. */
    readonly carriers: number;
    /** The distinct standings among the carriers, in carrier order. */
    readonly standings: readonly Standing[];
    /** The length of the value as the row holds it. */
    readonly valueChars: number;
}
/**
 * The inputs layer's verdict on ONE ruled argument of ONE tool call (honesty
 * layer 2) — fired once per `argument` row the `sf-inputs` subflow files,
 * before the call dispatches. This version files `source: 'default'` (the
 * library filled the tool's declared `assume` value, or the model sent that
 * same value) and `source: 'model'` (the model sent another value the record
 * does not trace). Names, enums and counts only: never the value, a quote or
 * the model's proposal — those stay on the committed row, in the tool's own
 * argument view — and no `valueChars` when that view hides the argument. A
 * host counts "1 assumed" from these events. The standing fold never reads
 * them (it reads the committed rows).
 */
export interface FindingsArgumentPayload {
    readonly toolCallId: string;
    readonly toolName: string;
    readonly iteration: number;
    /** The conversation turn the row was filed in. */
    readonly turn: number;
    readonly argument: string;
    readonly rule?: 'ask' | 'assume';
    readonly period?: true;
    readonly source?: ArgumentSource;
    readonly asked?: ArgumentAsked;
    readonly claimed?: ArgumentClaim;
    readonly matched?: 'quote' | 'phrase' | 'spelling' | 'mention';
    readonly reading?: true;
    readonly earlier?: true;
    readonly setAside?: 'open' | 'noise' | 'ruled-out';
    readonly argumentsFrom?: 'listed' | 'unlisted';
    readonly coincides?: 'person' | 'result' | 'app';
    readonly free?: true;
    readonly failed?: ArgumentCheckFailed;
    readonly malformed?: number;
    /** The length of the value as the row holds it — absent when the tool's view hides it. */
    readonly valueChars?: number;
}
/**
 * The results layer's verdict on ONE call's period (honesty layer 3, step 7b)
 * — fired once per `period` row the `sf-results` subflow files at the loop
 * head, after the call ran. The tool's name, the call's id, the stamps and the
 * verdict word ONLY: never an instant (the declared period stays on the
 * committed coverage row). `undeclared` means the tool declares a period
 * argument and this result said nothing about what its read covered. A host
 * counts "1 read the store did not hold" from these events; the standing fold
 * never reads them (it reads the committed rows).
 */
export interface FindingsPeriodPayload {
    readonly toolCallId: string;
    readonly toolName: string;
    readonly iteration: number;
    /** The conversation turn the row was filed in. */
    readonly turn: number;
    readonly verdict: 'covered' | 'partly-held' | 'not-held' | 'unknown' | 'undeclared';
    /**
     * The time layer's result checks that hold on the row (step T8, under
     * `.time()` only) — their names, never a range: `period-differs-from-asked`,
     * `period-shifted`, `period-beyond-retention`, `partly-beyond-retention`.
     * Absent when none holds, and on every unarmed run.
     */
    readonly timeChecks?: readonly ('period-differs-from-asked' | 'period-shifted' | 'period-beyond-retention' | 'partly-beyond-retention')[];
}
/**
 * One observe-only prose finding, filed with the answer: a detected claim or
 * a unit the bounded grammar could not judge. This is a warning, never an
 * enforcement decision or proof that the remaining prose is true.
 *
 * Only locations, checker codes, phrase-pack identities and native result
 * references leave the ledger. No answer text, candidate entity labels,
 * tool values or stale-read dates are included. Codes are owned by the
 * checker, not duplicated in this low-level event contract.
 */
interface FindingsNegativeClaimLocation {
    readonly iteration: number;
    /** The conversation turn when stamped by the runtime. */
    readonly turn?: number;
    /** JSON pointer into the answer; at most 4,096 UTF-16 code units. */
    readonly field: string;
    readonly span: {
        readonly start: number;
        readonly end: number;
    };
    /** At most 128 UTF-16 code units. */
    readonly packVersion: string;
    /** At most 128 UTF-16 code units. */
    readonly phraseId?: string;
    /** Some metadata was shortened or omitted; references are not exhaustive. */
    readonly truncated?: true;
}
export type FindingsNegativeClaimPayload = FindingsNegativeClaimLocation & ({
    readonly kind: 'negative-claim';
    readonly claim: 'none' | 'complete';
    /** At most 32 checker codes, each at most 128 UTF-16 code units. */
    readonly notes: readonly string[];
    /** At most 32 native result ids, each at most 256 UTF-16 code units. */
    readonly results: readonly string[];
} | {
    readonly kind: 'negative-claim-note';
    /** The checker's unread-unit code, at most 128 UTF-16 code units. */
    readonly code: string;
});
/**
 * A classifier picked a tool for one model call (9.105.0, `.toolChoice()`):
 * the provider's pick among the OFFERED tools (the merged wire minus the
 * always-served doors), its confidence, how many were offered and how many
 * were served, whether the served list was narrowed to the top-N, and the
 * cost. The row (`ToolChoiceRow`) holds the whole distribution and the
 * provider's model string in the committed `toolChoices`; this payload
 * carries identities (tool names), enums, numbers and a boolean — never the
 * descriptions the classifier read, never the user's message. Fired by
 * `recordToolChoice` from the tools slot, BEFORE the model call.
 */
export interface ToolChoicePickedPayload {
    readonly iteration: number;
    readonly chosen?: string;
    readonly confidence: number;
    readonly offered: number;
    readonly served: number;
    readonly narrowed: boolean;
    readonly narrowedSkipped?: 'unavailable' | 'too-few' | 'after-miss' | 'wrap-up' | 'evidence-revision';
    readonly latencyMs: number;
    readonly inputTokens?: number;
    readonly outputTokens?: number;
}
/**
 * What the model DID on the call the pick was made for (9.105.0): the tools
 * it called in order (empty on an answer), whether its first call is the
 * classifier's pick (`firstAgrees`, absent when either side is absent) and
 * the names it asked for outside a NARROWED served list (`missed`). Fired by
 * `recordToolChoice` from `callLLM`, after the reply.
 */
export interface ToolChoiceOutcomePayload {
    readonly iteration: number;
    readonly called: readonly string[];
    readonly firstAgrees?: boolean;
    readonly missed?: readonly string[];
}
/**
 * The classifier was asked which tool and produced no answer (9.105.0): the
 * provider's HTTP status when there was one and the latency spent. The
 * error text lives on the `ToolChoiceErrorRow`; the full tool set was served
 * (fail open, never fail narrow) and the run continues.
 */
export interface ToolChoiceFailedPayload {
    readonly iteration: number;
    readonly status?: number;
    readonly latencyMs: number;
}
/**
 * The declared ontology was served on one model call (9.106.0,
 * `.ontology()`): the map's identities (`id`, `version`, the declaration's
 * `hash`) and its three counts. The map itself is the run constant
 * `AgentState.ontology`; the served text is on the receipt and rebuilt by
 * `servedAt`. This payload carries identities and numbers only — never a
 * meaning, a coverage sentence or a node name. Fired by `callLLM` at
 * request assembly, once per call that served the piece.
 */
export interface OntologyServedPayload {
    readonly iteration: number;
    readonly id: string;
    readonly version: string;
    readonly hash: string;
    readonly nodes: number;
    readonly sources: number;
    readonly edges: number;
}
/**
 * The answer's standing as data — the SAME projection on
 * `agentfootprint.answer.assessed` and on `turn_end.answerAssessment`, built
 * by one function (`assessment/compose.ts` · `assessmentDataOf`) from the one
 * fold (`assessAnswer`) the answer layer runs at the head of the final
 * branch. Names, enums and counts only: the value, its rendering, the reason
 * KINDS and the checks that ran — never a value from the answer, a quote, a
 * witness pointer or the support's digest (those stay on the committed rows
 * `agent.assessment()` folds, with their pointers).
 */
export interface AnswerAssessmentPayload {
    /** The value: `known | unrefuted | unknown | not-applicable`. */
    readonly assessment: 'known' | 'unrefuted' | 'unknown' | 'not-applicable';
    /** The owner's words for it: known · consistent · not sure · ask · not assessed. */
    readonly standing: 'known' | 'consistent' | 'not-sure' | 'ask' | 'not-assessed';
    /** Every reason that fired, by kind, in the fold's order (the ask first, then by layer). */
    readonly reasons: readonly AssessmentReason[];
    /** What actually ran this turn, printed as it is — never a fixed list. */
    readonly checked: readonly {
        readonly layer: HonestyLayer;
        readonly check: AssessmentCheck;
        readonly ran: number;
        readonly of: number;
    }[];
}
/**
 * The answer layer assessed the turn's answer (honesty layer 4,
 * `.answerLayer()`): ONE event per answer, fired at the head of the final
 * branch — after the Route decider's checks filed their verdicts and before
 * the turn's payload is captured. It carries the standing as data
 * ({@link AnswerAssessmentPayload}) with the conversation turn and the
 * answer's iteration. The fold it reports reads committed rows only, so the
 * same recording read afterwards with `assessAnswer` folds the same value
 * (without app declarations — `assessAnswer(record, declarations)` with
 * `rowsAt` can say more than the run did).
 *
 * Where it reaches, and what it is about:
 * - the agent's own listeners only — a composition that mounts the agent
 *   bridges the agent domain, not `answer.*`, so read
 *   `turn_end.answerAssessment` there;
 * - the answer the run COMPOSED: under the evidence gate's `rails` posture a
 *   refused answer is assessed too, and then the run throws (no answer is
 *   delivered, and `agent.assessment()` returns `undefined`).
 */
export interface AnswerAssessedPayload extends AnswerAssessmentPayload {
    /** `AgentState.turnNumber` — the conversation turn. */
    readonly turn: number;
    /** The iteration whose answer was assessed. */
    readonly iteration: number;
}
export interface MemoryStrategyAppliedPayload {
    readonly strategyId: string;
    readonly strategyKind: 'sliding-window' | 'summarizing' | 'semantic' | 'fact-extraction' | 'hybrid';
    readonly reason: string;
    readonly scoreEvidence?: Readonly<Record<string, unknown>>;
    readonly inputMemoryCount: number;
    readonly outputMemoryCount: number;
    readonly droppedIds: readonly string[];
    readonly addedIds: readonly string[];
}
/**
 * One piece of stored content reached the prompt.
 *
 * Emitted once per admitted chunk by the memory formatter (8.8.0 — the
 * payload was declared in 2.x and nothing emitted it until then).
 * `memoryId` is the STORE ENTRY's id, which for an indexed corpus is the
 * chunk id you can cite. Which retriever produced it is
 * `meta.runtimeStageId` (`sf-memory-read-<id>/format-default#N`) — the
 * house rule is that events correlate by runtime stage id, not by
 * duplicating the owner into every payload.
 */
export interface MemoryAttachedPayload {
    readonly memoryId: string;
    readonly contentSummary: string;
    readonly score?: number;
    readonly rank?: number;
    readonly source: 'store' | 'auto-extract' | 'manual';
    readonly retriever?: 'pinecone' | 'weaviate' | 'qdrant' | 'chroma' | 'custom';
}
/** One candidate a retrieval considered, admitted or not. */
export interface RetrievedCandidatePayload {
    readonly id: string;
    readonly score: number;
    readonly rank: number;
    readonly admitted: boolean;
    /**
     * Why it did not reach the prompt. `'over-char-budget'` (8.19.0) means it
     * cleared the score floor and the count rule, and the retriever's
     * `maxChars` budget was already spent by better-ranked passages.
     */
    readonly reason?: 'below-threshold' | 'over-budget' | 'over-max-entries' | 'over-char-budget';
    readonly docUri?: string;
    readonly page?: number;
    readonly heading?: string;
}
/**
 * A retrieval happened — here is everything it considered (8.8.0).
 *
 * The event that makes "why did the agent NOT read that passage"
 * answerable. Until 8.8.0 the quality floor was applied inside the store,
 * so a rejected candidate never came back and a retrieval that injected
 * nothing left no trace of what it nearly injected.
 *
 * `candidates` absent means the store could not tell us (see
 * `candidatesOmittedReason`) — it never means there were none. That case
 * is `candidates: []` with `consideredCount: 0` and `corpusEmpty: true`.
 */
export interface MemoryRetrievedPayload {
    /**
     * Which rule ruled — the `RetrievalStrategy.name` that produced the verdicts
     * below, e.g. `'top-k'`. The port promises its name "appears in the
     * recording"; this is where. Without it a shipped `topK` and a consumer's own
     * re-ranker leave records that are identical except for the numbers they
     * disagree about.
     */
    readonly strategy: string;
    /** Stable hash of the query text. The text itself is already in the recording once. */
    readonly queryHash: string;
    /** How many chunks the retriever was willing to admit. */
    readonly k: number;
    /** The quality floor, when the retriever set one. */
    readonly threshold?: number;
    /**
     * The character budget the admitted passages were spent against (8.19.0),
     * when the retriever set one. A count bound (`k`) is not a size bound;
     * this is the size bound, and the candidates it dropped carry
     * `reason: 'over-char-budget'`.
     */
    readonly maxChars?: number;
    /**
     * Passage characters the admitted set spends. Present exactly when
     * `maxChars` is. Passage text only — the `<source …>` wrapper and the
     * block header are added later by the formatter.
     */
    readonly charsUsed?: number;
    readonly embedderId?: string;
    /** Length of the query vector. */
    readonly dimensions?: number;
    readonly consideredCount: number;
    readonly admittedCount: number;
    readonly rejectedCount: number;
    readonly candidates?: readonly RetrievedCandidatePayload[];
    /**
     * Whether `candidates` is every candidate that existed, or only as far
     * as the requested pool reached. `false` never weakens the ADMITTED
     * set — only the rejected list is then a sample.
     */
    readonly candidatesComplete: boolean;
    readonly candidatesOmittedReason?: string;
    /**
     * The namespace held nothing at all. Almost always means the corpus was
     * indexed under a different identity than the one being queried.
     */
    readonly corpusEmpty: boolean;
    /** The namespace that was searched. */
    readonly namespace?: string;
}
export interface MemoryDetachedPayload {
    readonly memoryId: string;
    readonly reason: 'stale' | 'budget' | 'score_low' | 'policy';
}
export interface MemoryWrittenPayload {
    readonly memoryId: string;
    readonly contentSummary: string;
    readonly source: 'auto' | 'manual';
    readonly actor?: string;
}
export interface ToolsOfferedPayload {
    readonly availableIds: readonly string[];
    readonly withheldIds: readonly string[];
    readonly withheldReasons: Readonly<Record<string, 'permission' | 'skill_inactive' | 'gated' | 'cost_guard'>>;
    readonly reason: string;
}
export interface ToolsActivatedPayload {
    readonly toolId: string;
    readonly reason: 'skill_activated' | 'autoActivate' | 'permission_granted';
    readonly source?: string;
}
export interface ToolsDeactivatedPayload {
    readonly toolId: string;
    readonly reason: 'skill_deactivated' | 'permission_revoked';
}
/**
 * Emitted at the start of a `ToolProvider.list(ctx)` call inside the
 * Discover stage. Pairs with `tools.discovery_completed` (success) or
 * `tools.discovery_failed` (error). Use the pair to measure async-
 * provider latency per iteration without joining stages by hand.
 */
export interface ToolsDiscoveryStartedPayload {
    readonly providerId: string | undefined;
    readonly iteration: number;
}
/**
 * Emitted when `ToolProvider.list(ctx)` resolves successfully. The
 * `durationMs` is the wall-clock between `tools.discovery_started` and
 * resolution; `toolCount` is the size of the returned tool list. For
 * sync providers `durationMs` is ~0; for async hub-backed providers
 * this is your observability hook for catalog-fetch latency.
 */
export interface ToolsDiscoveryCompletedPayload {
    readonly providerId: string | undefined;
    readonly iteration: number;
    readonly durationMs: number;
    readonly toolCount: number;
}
/**
 * Emitted when a custom `ToolProvider.list(ctx)` throws or rejects.
 * The iteration is aborted; a configured `reliability` rule decides
 * whether to retry, fall back, or fail-fast. `providerId` lets
 * consumers route alerts to the right hub adapter (rube / mcp /
 * custom-discovery). `durationMs` measures how long the failed call
 * spent before throwing, so timeouts vs immediate rejections are
 * distinguishable.
 */
export interface ToolsDiscoveryFailedPayload {
    readonly providerId: string | undefined;
    readonly error: string;
    readonly errorName: string;
    readonly iteration: number;
    readonly durationMs: number;
}
/**
 * The parties that can put a tool name on the wire or answer to one — the
 * vocabulary `tools.shadowed` and `tools.claim_swallowed` name them in (9.92.0).
 *
 *   • `registry`  — a static `.tool()` registration;
 *   • `provider`  — a `ToolProvider` (`.toolProvider()`, MCP through `staticTools`,
 *                   `.selfExplain()`'s trace pack — its id says which);
 *   • `skill`     — a skill's `tools:[]`, always-visible or scoped;
 *   • `framework` — an auto-attach the framework owns: `read_skill`, `present`,
 *                   `skip_step`.
 */
export type ToolNameChannel = 'provider' | 'registry' | 'skill' | 'framework';
/**
 * Emitted, per iteration, for a tool name that two parties BOTH put forward
 * for the wire and only one could win (8.7.0; re-subjected 9.92.0).
 *
 * Until 9.92.0 the two halves of a call could come from different parties:
 * the tools slot merges `[static, provider, skill, step]` first-wins while the
 * dispatcher resolved a build-time map first, so the model read one contract
 * and called another implementation. Since 9.92.0 dispatch FOLLOWS THE OFFER —
 * the party whose contract is on the wire is the party that answers — so
 * `schemaFrom` and `dispatchTo` name the same party, and both are the wire's.
 * The event still fires because the collision is still real: the losing
 * party's contract was hidden behind the winner's on this iteration, and a
 * rename is still the fix. The loser is named by the companion
 * `tools.claim_swallowed`, which fires for every party that lost a name whether
 * or not its contract competed this iteration.
 *
 * Not an error event: the run continues and a tool really executes. It fires
 * once per contested name PER ITERATION, because a provider list is resolved
 * per iteration and a collision can begin mid-run.
 *
 * Carries names only — never args, never results, never a description body.
 */
export interface ToolsShadowedPayload {
    /** The contested tool name. */
    readonly toolName: string;
    readonly iteration: number;
    /** Which party's CONTRACT the model was shown — the wire's party. */
    readonly schemaFrom: ToolNameChannel;
    /** That party's id — the `ToolProvider.id` or the skill id, when it has one. */
    readonly schemaFromId?: string;
    /** Which party's IMPLEMENTATION answers a call to this name — since 9.92.0
     *  always the wire's party, so it equals `schemaFrom`. Kept because a
     *  consumer that branched on it keeps working and the sentence stays true. */
    readonly dispatchTo: ToolNameChannel;
    /** That party's id, when it has one. */
    readonly dispatchToId?: string;
}
/**
 * Emitted, per iteration, for a party whose claim to a tool name is DEAD on
 * that iteration: another party holds the name on the wire, and since dispatch
 * follows the offer the loser's implementation cannot be reached by any call
 * (9.92.0 — the `claim-swallowed` family of the divergence walk).
 *
 * It fires whether or not the loser's contract competed for the wire this
 * iteration — a provider tool whose name a static `.tool()` already holds, a
 * skill tool named after a framework auto-attach, a never-activated scoped
 * skill's tool whose name a provider serves. Silence about a dead claim is the
 * defect this event exists to end: the party registered a tool the model can
 * never reach, and the trace said nothing.
 *
 * Carries names only — never args, never results, never a description body.
 */
export interface ToolsClaimSwallowedPayload {
    /** The contested tool name. */
    readonly toolName: string;
    readonly iteration: number;
    /** The party whose claim is dead this iteration. */
    readonly lostBy: ToolNameChannel;
    /** That party's id — the `ToolProvider.id` or the skill id, when it has one. */
    readonly lostById?: string;
    /** The party holding the name on the wire — and therefore answering it. */
    readonly wonBy: ToolNameChannel;
    /** That party's id, when it has one. */
    readonly wonById?: string;
}
/**
 * Emitted when the dispatcher hands a call to a tool whose name was NOT on
 * this iteration's wire (9.92.0).
 *
 * The capability law keeps a registry-routed tool dispatchable after a
 * narrowing takes it off the offer — a later step's tool, a parked map's tool,
 * a scoped skill's tool named from a restored transcript — and that is
 * deliberate. What was missing was the record: a call answered by a tool the
 * model was not shown on THIS call looked exactly like one that was. This is
 * that record, once per such call. It fires ONLY when the answering party is
 * the one the model last read this name under (or the name's only claimant
 * when it was never served); a name whose last-served contract belonged to a
 * party that can no longer answer is refused as a recorded tool result
 * instead (`toolCalls.ts` · `notServedResult`), never handed to somebody else.
 *
 * Carries names only — never args, never results.
 */
export interface ToolsAnsweredOffWirePayload {
    readonly toolName: string;
    readonly toolCallId: string;
    readonly iteration: number;
    /** The party whose implementation answered. */
    readonly answeredBy: ToolNameChannel;
    /** That party's id — the skill id or the `ToolProvider.id`, when it has one. */
    readonly answeredById?: string;
}
/** Shared shape of the four `tools.session_*` payloads. */
interface ToolSessionPayloadBase {
    /** The tool that registered the cleanup. */
    readonly tool: string;
    /** Which scope it asked for — how long it expected to live. */
    readonly scope: 'call' | 'run' | 'session' | 'shutdown';
    /** Digest of the isolation key. SHA-256 (12 hex chars) where `node:crypto`
     *  resolves, FNV-1a in a browser bundle. NEVER the key. */
    readonly keyHash: string;
    /** The adapter holding the resource — `CodeRunner.id`, say. */
    readonly runnerId?: string;
    /** One fact the tool chose to state about what was opened (the language, the
     *  browser profile). Never user data. */
    readonly label?: string;
}
/** A tool opened something and registered its cleanup. */
export type ToolsSessionStartedPayload = ToolSessionPayloadBase;
/**
 * A later call reused the session already held under this key.
 *
 * This is the payoff being measured: `calls` is how many dispatches have shared
 * one start-up. It is also the liveness signal the idle sweep and the LRU bound
 * read, which is why a tool re-registers on every execute rather than only once.
 */
export interface ToolsSessionReusedPayload extends ToolSessionPayloadBase {
    /** How many calls have now shared this session, including this one. */
    readonly calls: number;
}
/** The cleanup ran. `reason` says which firing site ran it. */
export interface ToolsSessionClosedPayload extends ToolSessionPayloadBase {
    readonly reason: 'call-end' | 'run-end' | 'session-end' | 'shutdown' | 'idle' | 'evicted';
    /** Wall-clock from registration to close. */
    readonly durationMs: number;
}
/**
 * The cleanup threw, or outran `toolTeardownTimeoutMs`.
 *
 * Teardown never throws into the run — but it is never SILENT either, and this
 * event is the difference. A vendor `Stop` that fails leaves a live sandbox
 * somebody is still paying for; the run has no way to say so, so this does.
 * `errorClass` mirrors `credential.failed`'s 9.4.0 addition: routable without
 * parsing prose (`'ToolTeardownTimeoutError'` for the budget case).
 */
export interface ToolsSessionCloseFailedPayload extends ToolSessionPayloadBase {
    readonly reason: 'call-end' | 'run-end' | 'session-end' | 'shutdown' | 'idle' | 'evicted';
    readonly durationMs: number;
    readonly error: string;
    readonly errorClass?: string;
}
/**
 * Emitted when LLM-produced tool args fail validation against the tool's
 * declared `inputSchema` (backlog #9). Fires for BOTH modes that validate:
 * `enforced: true` means the call was rejected before dispatch and the
 * model received a structured retry message as the tool result;
 * `enforced: false` ('warn' mode) means the tool executed anyway.
 * `issues` name paths, expectations, and received TYPES — never the
 * supplied values (they can carry PII / injection payloads).
 */
export interface ValidationArgsInvalidPayload {
    readonly toolName: string;
    readonly toolCallId: string;
    readonly iteration: number;
    readonly issues: ReadonlyArray<{
        readonly path: string;
        readonly expected: string;
        readonly got: string;
        /**
         * A capped excerpt of the offending STRING — present on string-shape
         * issues (`pattern` / `minLength` / `maxLength`) only, where the
         * complaint IS the value's shape. Structural issues (type, enum,
         * required, additionalProperties) still carry no value at all.
         */
        readonly value?: string;
        /** The parameter's own schema `description`, when it declares one. */
        readonly hint?: string;
    }>;
    readonly enforced: boolean;
}
/**
 * The turn-start routing verdict (SG-C) — one per turn on skill-graph agents
 * whose graph ran the cascade (`classify` configured, or
 * `continuity: 'conversation'` with something to decide). Fired by the
 * RouteTurn stage DURING traversal (collect-during-traversal; never rebuilt),
 * BEFORE iteration 1, so it precedes every `context.evaluated` of its turn.
 *
 * The LOSERS are on the record: `scores` ranks every candidate the tier-2
 * scorer judged (role-hidden skills are excluded before scoring — a hidden
 * capability is never named, not even to observers of this run); `offered`
 * names the menu the model was handed; `policy` stamps the exact thresholds
 * that judged the hop, so no observer has to guess which numbers decided.
 */
export interface SkillTurnRoutedPayload {
    /** The tier that decided the turn's start:
     *  `'entry'` — a tier-1 rule (the recorded vocabulary for rule-won starts);
     *  `'intent'` — the tier-2 scorer was decisive;
     *  `'continuity'` — the inherited cursor held (incumbent won / near-tie /
     *  sticky default / the decider picked STAY — `decider` says which);
     *  `'menu'` — a menu was put in-band (near-tie or unmatched);
     *  `'decider'` — the configured tier-3 decider resolved the menu
     *  out-of-band (9.19.0; `decider` carries the pick);
     *  `'none'` — the cascade decided nothing the loop may act on (a rails
     *  menu — the offer is still recorded here — or a dropped resume). */
    readonly by: 'entry' | 'intent' | 'continuity' | 'menu' | 'decider' | 'none';
    /** The inherited cursor (continuity), when one existed. */
    readonly from?: string;
    /** Where the turn starts. Absent = menu pending / none. */
    readonly to?: string;
    /** `IntentScorer.name` / entry-scorer name, when tier 2 ran. */
    readonly scorer?: string;
    /** EVERY candidate, ranked best-first — the losers, with the numbers that
     *  lost. `score` is raw (strategy-specific); `relevance` is the
     *  full-softmax share. */
    readonly scores?: ReadonlyArray<{
        readonly id: string;
        readonly score: number;
        readonly relevance: number;
    }>;
    /** The top-vs-2nd PAIRWISE gap actually judged (count-independent — see
     *  routingPolicy.ts; NOT the difference of two `relevance` shares). */
    readonly runnerUp?: {
        readonly id: string;
        readonly gap: number;
    };
    /** Whether tier 2 cleared the margin + floor. */
    readonly decisive?: boolean;
    /** WHAT the message said that won tier 1 (9.28.0) — present only on a
     *  `by: 'entry'` verdict a DATA matcher (`match:` — RegExp / `{ keywords }` /
     *  `{ all }`) decided. `text` is the matched substring of the USER message
     *  (never tool or system text), whitespace-collapsed and bounded to 80
     *  characters, ellipsis included; `keyword` names WHICH declared keyword hit
     *  (keywords arm only). Absent for `when` predicates — opaque code the
     *  library cannot quote — and for intent/scorer verdicts, whose evidence is
     *  the `scores` above. */
    readonly witness?: {
        readonly text: string;
        readonly keyword?: string;
    };
    /** The tier-3 menu (near-tie cluster, or every entry when unmatched). */
    readonly offered?: readonly string[];
    /** STAY was a first-class option in that menu (mid-conversation). */
    readonly stayOffered?: boolean;
    /** The thresholds that judged this turn, verbatim. `floor` is the EFFECTIVE
     *  floor (`policy.floor ?? scorer.floor`); absent = no floor, so
     *  `unmatched` was unreachable and near-tie governed. */
    readonly policy: {
        readonly nearTieMargin: number;
        readonly menuSize: number;
        readonly floor?: number;
    };
    /** Prior conversational turns the scorer saw (its declared window). */
    readonly window?: number;
    /** A continuity cursor that could not be honored — the stored id is not a
     *  node of the currently mounted graph (a deploy changed it). The turn
     *  started cold instead, and says so. */
    readonly droppedResume?: {
        readonly id: string;
        readonly reason: 'unknown-skill';
    };
    /**
     * The tier-3 DECIDER's involvement (9.19.0) — present whenever one was
     * configured AND a menu verdict reached it. `picked` is the constrained
     * pick that resolved the menu (an offered id, or `'stay'`); ABSENT when
     * the decider declined (`'none'` / parse failure / a throw) and the
     * in-band envelope stood. Recorded on every outcome so the record always
     * says the decider was consulted — a resolver that ran and said nothing
     * is still a fact.
     */
    readonly decider?: {
        readonly provider: string;
        readonly model?: string;
        readonly picked?: string;
    };
}
export interface SkillActivatedPayload {
    readonly skillId: string;
    readonly reason: 'autoActivate' | 'read_skill_result' | 'manual';
    readonly injectedTools?: readonly string[];
    readonly injectedSystemPromptChars?: number;
}
export interface SkillDeactivatedPayload {
    readonly skillId: string;
    readonly reason: string;
}
/**
 * Fired by the skill-graph read_skill GATE when the model tries to `read_skill`
 * a skill that is NOT reachable from the current cursor. The jump is rejected
 * (cursor/activations unchanged); the model gets a synthetic re-prompt naming
 * `allowed`. Powers the lens / Why-panel "it tried to leave the graph here".
 */
/**
 * Fired when a `read_skill` pick the gate ACCEPTED did not end up active — the
 * model was told "Skill 'X' activated for the next iteration" and something else
 * won. There is exactly one way to get here: a declared route edge fired on the
 * same turn (the model emitted a domain tool AND `read_skill` in one message, and
 * the tool's result matched the edge). The author's declared edge wins by design
 * (`D1 > D2`), so the pick is superseded — and reported here rather than silently
 * dropped. The next iteration shows the model where it actually is.
 */
export interface SkillRerouteSupersededPayload {
    /** The skill the model picked with `read_skill` (accepted, then superseded). */
    readonly volunteeredId: string;
    /** The skill the declared edge routed to instead (the cursor that won). */
    readonly wonId?: string;
    /** The cursor the hop started from. */
    readonly fromSkillId?: string;
    /** The ReAct iteration whose evaluation dropped the pick. */
    readonly iteration: number;
    /**
     * WHO volunteered the superseded move (9.19.0). Absent = the model's
     * `read_skill` pick, exactly as this event has always meant;
     * `'tool-proposal'` = an accepted `propose-transition` effect that a
     * same-batch declared edge outran (D1 wins over the proposal exactly as
     * it wins over a pick — the author's determinism is never overridden).
     */
    readonly source?: 'tool-proposal';
}
/**
 * The DECLARED skill map, as the author drew it (9.50.0) — fired ONCE per run,
 * right after `agent.run_configured`, for every agent whose `.skillGraph()`
 * mount could state its map. Nodes and edges come from the BUILT graph
 * (`graph.nodes` / `graph.edges`), never inferred from runtime hops — so a
 * recording carries the complete authored topology, not the lower bound that
 * per-hop `routing[]` provenance names (an edge appears there only once it
 * FIRES). A consumer drawing the graph from a recording no longer has to say
 * "partial" or ask the caller to pass the built graph in.
 *
 * ABSENT (no event) when no graph is mounted, or when a structurally-typed
 * graph carries no `nodes` — the map is then honestly not on the record,
 * never guessed.
 */
export interface SkillGraphDeclaredPayload {
    /** The drawn nodes: `kind: 'skill'` boxes, plus `'predicate'` diamonds for a
     *  decision `tree()`. `description` is the skill's catalog description,
     *  verbatim (the text the model reads) — absent when the skill declares
     *  none. `label` is the drawn caption (predicate nodes). */
    readonly nodes: ReadonlyArray<{
        readonly id: string;
        /** `'skill' | 'predicate'` (reported verbatim from the graph). */
        readonly kind: string;
        readonly description?: string;
        readonly label?: string;
        /** The skill's declared plain name for a person (`defineSkill({ title })`)
         *  — "array estate report" for `array-inventory`. Absent when the skill
         *  declares none (the label a graph draws for a skill node is its id, so
         *  a reader wanting a plain name reads this, never `label`). The model
         *  never reads it. */
        readonly title?: string;
    }>;
    /** The author's edges, verbatim. `from: null` is the synthetic START (an
     *  entry edge — the lens's declared-edge consumers filter on
     *  `from !== null`). `kind` is the declared `SkillEdgeKind`
     *  (`'entry' | 'predicate' | 'on-tool-return' | 'on-tool-status' | 'guard'
     *  | 'model'`). `guard` (9.51.0) is the edge's declared DATA guard,
     *  verbatim — conditions in declaration order, all ANDed, the operator
     *  vocabulary mirroring footprintjs's `WhereFilter` — so a recording's
     *  SkillMap shows its guard conditions without anyone re-reading source. */
    readonly edges: ReadonlyArray<{
        readonly from: string | null;
        readonly to: string;
        readonly kind: string;
        readonly label?: string;
        readonly guard?: {
            readonly conditions: ReadonlyArray<{
                readonly key: string;
                readonly op: string;
                readonly value: SkillGuardValueRecord;
            }>;
        };
    }>;
}
export interface SkillRejectedPayload {
    /** The skill id the model requested via `read_skill`. */
    readonly requestedId: string;
    /** The cursor it was at (undefined = cold start, before any entry resolved). */
    readonly currentSkillId?: string;
    /**
     * What the model was TOLD it could reach — role-filtered (9.86.0).
     *
     * No longer the set the call was bounded to: admission still reads the
     * graph's raw set, and this is the SPEAKING set, so a skill the caller's
     * `PermissionChecker` hides never appears here. Empty is therefore two
     * different facts — nothing was reachable, or nothing reachable may be named
     * — and a reader that needs them apart wants the run's graph, not this row.
     */
    readonly allowed: readonly string[];
    /** The ReAct iteration the rejection fired on. */
    readonly iteration: number;
    /** Present when a POSTURE, not reachability, refused (SG-C `strictness`):
     *  `'guard'` — the pick was reachable but off the outstanding menu (or no
     *  menu was outstanding); `'rails'` — routing picks are refused outright
     *  (rules/scorer route turn starts; declared routes handle transitions).
     *  Absent = today's reachability refusal, unchanged. */
    readonly posture?: 'guard' | 'rails';
    /** WHICH ARM of the gate answered (9.84.0) — optional, so every consumer
     *  written against the older shape keeps working unchanged:
     *
     *   • `'self-call'` — `requestedId === currentSkillId`. The model asked for
     *     the skill it is already in; the cursor did not move because there was
     *     nowhere to move it, and the tool result is an acknowledgement naming
     *     the tools it can call, not a decline. Consumers that count refusals as
     *     routing failures should read this one as a NAMING failure instead.
     *   • `'unreachable'` — a genuine hop the cursor cannot make.
     *   • `'posture'` — a reachable hop a declared `strictness` declined; pairs
     *     with {@link SkillRejectedPayload.posture}, which names which one.
     *
     *  Before this, telling a self-call from an unreachable hop meant comparing
     *  `requestedId` against `currentSkillId` and knowing why that mattered. */
    readonly reason?: 'self-call' | 'unreachable' | 'posture';
}
/**
 * Fired when two or more tool results of ONE parallel batch matched skill-graph
 * edges to DIFFERENT targets (9.16.0). The first match in call order wins the
 * cursor move; every later result matching another target is suppressed — and
 * reported here so the record explains the hop the run did not take. Before
 * 9.16.0 there was no conflict to report because there was no batch: only the
 * LAST call of a batch was consulted, and the earlier calls' routing
 * implications were silently dropped.
 *
 * Not fired for same-target matches (they all asked for the move that
 * happened), nor for batches where only one result matched. Distinct from
 * `skill.reroute_superseded`, which reports a declared edge outranking a
 * `read_skill` pick — this one is edge-vs-edge, inside one batch.
 */
export interface SkillRouteConflictPayload {
    /** The ReAct iteration whose evaluation resolved the batch. */
    readonly iteration: number;
    /** The cursor the winning hop started from. */
    readonly fromSkillId?: string;
    /** The call-order-first match — the one that moved the cursor. */
    readonly winner: {
        /** The provider's tool_use id for the winning call, when known. */
        readonly toolCallId?: string;
        readonly toolName: string;
        /** The skill the cursor moved to. */
        readonly target: string;
    };
    /** Later matches to other targets, in call order — the suppressed hops. */
    readonly losers: ReadonlyArray<{
        readonly toolCallId?: string;
        readonly toolName: string;
        /** The skill this result would have routed to. */
        readonly target: string;
    }>;
    /**
     * WHAT conflicted (9.19.0). Absent = declared route edges, exactly as
     * this event has always meant; `'tool-proposal'` = two or more accepted
     * `propose-transition` effects of one batch named different targets —
     * first accepted in call order wins, the rest are suppressed and
     * reported here under the same law.
     */
    readonly source?: 'tool-proposal';
}
/**
 * A declared step completed: its tool returned (non-error) while it was
 * current (9.18.0). Fired at the tool-return boundary, in batch call order —
 * two adjacent steps naming the same tool advance twice in one batch, each
 * on the record. A skip is NOT a completion: a `skip_step` that moved the
 * pointer fires `step_skipped { policy: 'advance' }` alone.
 */
export interface SkillStepAdvancedPayload {
    readonly skillId: string;
    /** The step that just completed (or was skipped past). */
    readonly step: {
        readonly index: number;
        readonly total: number;
        readonly tool: string;
        readonly note: string;
    };
    readonly iteration: number;
    /** The provider's tool_use id for the completing call, when known. */
    readonly toolCallId?: string;
    /** Present on the LAST step: the procedure finished. */
    readonly completed?: true;
}
/**
 * The model declined a step, with its reason — the integrity condition, not
 * polish (9.18.0). The framework moved on or held per the skill's declared
 * `onSkip`; both facts are here. An empty reason never gets this far: the
 * tool-calls stage answers it with a teaching result and no event.
 */
export interface SkillStepSkippedPayload {
    readonly skillId: string;
    readonly step: SkillStepAdvancedPayload['step'];
    /** The model's own words. */
    readonly reason: string;
    readonly policy: 'advance' | 'hold';
    readonly iteration: number;
    readonly toolCallId?: string;
}
/**
 * The turn is ending with steps unrun (9.18.0). `'nudged'` = the one
 * teaching re-ask went back (at most once per turn); `'accepted'` = the
 * model stopped again and the framework honored it — never a forced
 * continue; `'cut-short'` = a limit (max-iterations / cost-budget) ended
 * the turn, and a nudge would have spent an iteration the limit refused.
 */
export interface SkillStepsUnfinishedPayload {
    readonly skillId: string;
    readonly remaining: ReadonlyArray<{
        readonly index: number;
        readonly tool: string;
        readonly note: string;
    }>;
    readonly total: number;
    readonly action: 'nudged' | 'accepted' | 'cut-short';
    readonly iteration: number;
}
/**
 * The turn's refusal loop crossed the declared threshold and the rest of the
 * turn runs on the escalation brain (9.19.0). Fired ONCE per turn, at the
 * flip, from the gate that counted the refusals — escalation is always on
 * recorded evidence (`skill.rejected` — reachability, posture or a self-call,
 * all three refusal arms), never on
 * vibes. De-escalation is the next turn's seed; no event marks it.
 */
export interface SkillEscalatedPayload {
    /** The ReAct iteration whose refusal tripped the threshold. */
    readonly iteration: number;
    /** The declared threshold (`escalation.afterRefusals`). */
    readonly afterRefusals: number;
    /** The observed refusal count that tripped it (== afterRefusals at the
     *  flip; the counter keeps counting but the event fires once). */
    readonly refusals: number;
    /** The brain that WAS serving — resolved by the same precedence chain
     *  callLLM applies (skill brain > configured > default). */
    readonly from: {
        readonly provider: string;
        readonly model: string;
    };
    /** The declared escalation brain. `model` absent = it inherits down the
     *  chain (same-provider escalations only; enforced at build). */
    readonly to: {
        readonly provider: string;
        readonly model?: string;
    };
}
/**
 * One typed tool effect was judged (9.19.0) — the effects channel's own
 * record, fired in batch call order from the stage that judged it.
 *
 *   • `'accepted'` — a `propose-transition` passed the graph's reachability
 *     law (the cursor moves at the next evaluation unless a same-batch
 *     declared edge wins — that suppression is `skill.reroute_superseded
 *     { source: 'tool-proposal' }`), or a `require-instruction` named a
 *     registered instruction and its lease was granted.
 *   • `'refused'` — the teaching refusal: unreachable target, no graph to
 *     route, unknown instruction id, a body whose declared channel cannot
 *     be pushed, or a malformed effect. `refusalReason` says which.
 *   • `'superseded'` — an accepted transition proposal lost the batch to an
 *     EARLIER accepted proposal naming a different target
 *     (`skill.route_conflict { source: 'tool-proposal' }` reports the same
 *     batch once, winner + losers).
 *
 * No effects = this event never fires (zero-cost-when-unused).
 */
export interface ToolEffectPayload {
    readonly kind: 'propose-transition' | 'require-instruction';
    readonly outcome: 'accepted' | 'refused' | 'superseded';
    /**
     * The accepted `propose-transition` named the skill the cursor was ALREADY
     * in — a STAY, accepted as a no-op (9.86.0).
     *
     * Nothing moved and nothing activated, because the run is already in the
     * state the proposal asked for; no `pendingToolTransition` was written and
     * the tool's result carries no refusal suffix. Before this the reachable set
     * — which filters the cursor out of its own successor set, correctly, for a
     * MOVE — made a stay look unreachable and the proposal was refused.
     *
     * An ADDITIVE optional field rather than a fourth `outcome`: a consumer
     * switching exhaustively over `outcome` must keep compiling. Read it to tell
     * a no-op stay from a hop that will actually move the cursor.
     *
     * A stay COMPETES for the batch's one transition slot like any other
     * accepted proposal (9.86.1): judged first, it holds the slot, and a later
     * sibling proposing a hop is `'superseded'` with the batch's
     * `skill.route_conflict` naming the stay as the winner; judged after an
     * accepted hop, the stay is the one superseded. 9.86.0 let a stay through
     * without claiming the slot, so two `'accepted'` events could land in one
     * batch while the cursor moved.
     */
    readonly stay?: true;
    /** The tool whose result carried the effect. */
    readonly toolName: string;
    readonly toolCallId?: string;
    readonly iteration: number;
    /** propose-transition: the proposed cursor target. */
    readonly targetSkillId?: string;
    /** propose-transition: the effect's own declared reason. */
    readonly reason?: string;
    /** require-instruction: the registered injection id it pushes. */
    readonly instructionId?: string;
    /** require-instruction: the granted (or asked-for) lease. */
    readonly deliveryLease?: InstructionDeliveryLease;
    /** outcome `'refused'`: the teaching sentence, verbatim. */
    readonly refusalReason?: string;
    /** outcome `'superseded'`: what outran it. */
    readonly supersededBy?: 'earlier-proposal';
}
/**
 * A tool's result was refused for exceeding the tool's own declared
 * `resultCeiling` (9.20.0) — the record of the size the content channels never
 * carry. The model read a teaching refusal ("No data was returned", plus how
 * to narrow); the oversized payload entered NO channel — not history, not
 * `stream.tool_end`, not this event. The delivered result carries status
 * `'invalid'`, so `onToolStatus` edges can route the overflow.
 *
 * No `resultCeiling` on the tool = this event never fires
 * (zero-cost-when-unused).
 */
export interface ToolResultRefusedPayload {
    readonly toolName: string;
    readonly toolCallId: string;
    readonly iteration: number;
    /** The stringified result's TRUE length — the fact the refusal replaced. */
    readonly sizeChars: number;
    /** The tool's declared ceiling it exceeded. */
    readonly maxChars: number;
    /** The declared parameter names the refusal suggested narrowing by. */
    readonly narrowBy?: readonly string[];
    /** When the refused result was an effects envelope: the status the tool
     *  DECLARED before delivery was refused (its declared effects were still
     *  judged — a proposed transition does not die with an oversized payload). */
    readonly declaredStatus?: ToolResultStatus;
}
/**
 * A tool returned exactly what it returned before, for exactly the same
 * arguments (9.26.0) — and the model was told so.
 *
 * A NOTE was appended to that result; the call ran, the result is unchanged
 * beside the note, and nothing was refused. Payloads never ride this event:
 * `argsFingerprint` and `resultFingerprint` are short non-cryptographic
 * digests, which answer "is this the same?" and nothing else. See
 * `core/agent/repeatedCall.ts` for why the fingerprints and not the values.
 */
export interface ToolRepeatedCallPayload {
    readonly toolName: string;
    readonly toolCallId: string;
    readonly iteration: number;
    /** How many times this exact call+result has landed this turn, including
     *  this one. The note fires on the threshold landing only. */
    readonly occurrences: number;
    /** Digest of the arguments — never the arguments. */
    readonly argsFingerprint: string;
    /** Digest of the result — never the result. */
    readonly resultFingerprint: string;
    /**
     * Which fingerprint decided the match, when it was not the usual pair
     * (9.62.0). `'arguments'` means this landing matched on the tool's
     * arguments ALONE — the tool declared `repeatedWhen: 'arguments'`
     * (`tools/tools.ts`), so `resultFingerprint` above is still a true digest
     * of what came back but played no part in deciding this was a repeat.
     *
     * Additive and default-omitted: every event this library emitted before
     * 9.62.0, and every event for a tool that has not opted in, carries no
     * `mode` key at all — not `mode: undefined` written out, the key simply
     * absent — so an existing payload-key-set assertion anywhere does not see
     * a new field appear under it.
     */
    readonly mode?: 'arguments';
}
/**
 * A tool looked and found nothing, and said so with `absent(…)`.
 *
 * The event exists because the RECORD is where "we looked here and there was
 * nothing" has to survive: read off the answer alone, a clean run and a run
 * that searched an empty collector look identical. Carries the coverage the
 * tool declared — never the arguments the model passed, which are the model's
 * own words about what it wanted, not the tool's about what it covered.
 */
export interface ToolAbsentPayload {
    readonly toolName: string;
    readonly toolCallId: string;
    readonly iteration: number;
    /** What the tool says it was looking for (its own prose). */
    readonly lookedFor?: string;
    /** Ground the search DID cover. Non-empty by construction — `absent()`
     *  refuses a declaration that names no coverage. */
    readonly checked: readonly CoverageItemPayload[];
    /** Ground this search did not reach — the absence proves nothing there. */
    readonly notChecked?: readonly CoverageItemPayload[];
    /** Ground no call to this tool can reach. No retry changes it. */
    readonly cannotCover?: readonly CoverageItemPayload[];
    /**
     * Where the tool said to go instead — its `tryInstead` sentence, trimmed as
     * `absent()` mints it (9.113.0; an envelope minted elsewhere may have
     * handed the model a padded copy). Prose for the model: never parse a tool
     * name out of it (a name found inside prose is an inference); read
     * {@link ToolAbsentPayload.tryInsteadTool}.
     *
     * Before 9.113.0 the suggestion was never on this event: it rode only
     * inside the raw tool result (`agentfootprint.stream.tool_end`'s `result`,
     * the tool turn in history), where a reader had to recognize the envelope
     * again to find it.
     *
     * Additive and default-omitted: an absence that suggests nothing carries no
     * `tryInstead` key at all (the `ToolRepeatedCallPayload.mode` precedent).
     */
    readonly tryInstead?: string;
    /**
     * The other TOOL the suggestion points at, typed (9.113.0) — the tool's
     * `tryInsteadTool` as declared, copied. The name is not looked up: the tool
     * may be served by a provider, or never have been offered on this run.
     * Default-omitted like {@link ToolAbsentPayload.tryInstead}; either may
     * come without the other.
     */
    readonly tryInsteadTool?: TryInsteadToolPayload;
    /**
     * Where the search looked and when that source was measured, as the tool
     * declared it (honesty step 7b) — `absent({ …, provenance })`, in the
     * record's camelCase form. Default-omitted: an absence that declares no
     * source carries no key, and one whose source the rule set refuses (an
     * envelope minted elsewhere) carries none either (dev-warned).
     */
    readonly provenance?: ProvenancePayload;
    /**
     * What the search's READ covered in time, as the tool declared it (honesty
     * step 7b) — instants verbatim, camelCase like `lookedFor`. Default-omitted.
     */
    readonly period?: PeriodPayload;
    /**
     * What the LIBRARY composed for a tool that declares its `search` — the
     * facets by reference, the gaps, `complete`. Present only for such a tool;
     * every other absence carries no key. Composed VALUES ride no event in any
     * mode: they live on the committed `coverageDeclared` row, under the run's
     * redaction policy. For a `checked: 'declared'` absence, `checked` above
     * holds the declaration's constants only (the source, the fixed
     * qualifiers) and may be empty.
     */
    readonly composition?: CoverageCompositionPayload;
    /**
     * In place of `lookedFor` while `.negativeClaims()` is armed — a reference
     * to the committed row's field. Unarmed, `lookedFor` is today's bytes.
     */
    readonly lookedForRef?: CoverageFieldReference;
}
/** Where a value an event leaves out lives: the row the result identity addresses. */
export interface CoverageFieldReference {
    /** The row's address — `claim-result/<namespace>/<runId>/<ordinal>`. */
    readonly resultId: string;
    readonly field: 'lookedFor';
}
/**
 * One composed facet as it rides an event — a REFERENCE, never its value:
 * `{ resultId, argument }` names the committed row and the argument whose
 * value lives there.
 */
export interface CoverageFacetReferencePayload {
    readonly facet: 'source' | 'subject' | 'population' | 'filter' | 'period' | 'names';
    readonly from: 'call' | 'default' | 'host' | 'result';
    readonly ref: {
        readonly resultId: string;
        readonly argument?: string;
    };
    /** Omission narrowed nothing (`whenOmitted: { any: true }`) — no value to keep out. */
    readonly any?: true;
}
/** One typed gap as it rides an event — the words name arguments and verdicts, never a value. */
export interface CoverageGapPayload {
    readonly kind: 'argument-unsent' | 'cap-reached' | 'paged' | 'period' | 'partial' | 'credential-limited' | 'match' | 'ranking' | 'scope' | 'prose';
    readonly what: string;
    readonly why?: string;
    readonly bears?: 'source' | 'population' | 'period' | 'completeness' | {
        readonly filter: string;
        readonly values?: readonly (string | number | boolean)[];
    };
    readonly argument?: string;
}
/** What the library composed for one call of a `search` tool, as an event carries it. */
export interface CoverageCompositionPayload {
    /** `'remote'` — a declaration over MCP or from a ToolProvider; it establishes nothing. */
    readonly origin: 'local' | 'remote';
    /** The committed row's address. */
    readonly resultId: string;
    readonly complete: boolean;
    /** Each facet that came from the call, a default, the host or the result — by reference. */
    readonly facets: readonly CoverageFacetReferencePayload[];
    readonly gaps: readonly CoverageGapPayload[];
}
/** An absence's source and time as it rides an event — detached plain data. */
export interface ProvenancePayload {
    readonly measuredAt: string;
    readonly source: string;
    readonly ageSeconds?: number;
    readonly sourceExportDate?: string;
}
/**
 * A declared period as it rides an event (honesty step 7b) — detached plain
 * data, the instants the tool declared, never parsed or reformatted.
 */
export interface PeriodPayload {
    readonly queried: {
        readonly from: string;
        readonly to: string;
    };
    /** What the store holds, or `'unknown'` — the tool said it cannot vouch for it. */
    readonly held: {
        readonly from: string;
        readonly to: string;
    } | 'unknown';
    readonly readAt?: string;
}
/** A suggestion to try another tool, as it rides an event: detached plain
 *  data, copied out of the tool's own declaration. */
export interface TryInsteadToolPayload {
    readonly tool: string;
    readonly why?: string;
}
/**
 * A tool returned a verdict WITH its own boundary — `coverage(result, …)`.
 *
 * The sibling of the evidence gate on the record: the gate's events say which
 * VALUES an answer could not support, this one says which LIMITS it did not
 * state. Fires per declaring call, whatever `.limitsTravelWithTheAnswer()`
 * is set to — the recording half is unconditional, only the appending half is
 * opt-in.
 */
/**
 * A code-runner tool ran a program the model wrote.
 *
 * **The code itself is deliberately NOT here.** Generated code quotes the data
 * it was given — a row, a serial, an address — so shipping it to every attached
 * exporter would put customer data on a wire nobody audited for it. This is the
 * same rule the session events already follow with `keyHash`, never the key.
 *
 * What travels instead is `shapeHash`: the program with its string literals,
 * numbers and identifier names stripped, leaving the CALL SHAPE — which
 * operations, in what order. Two runs that compute the same thing over
 * different data share a shape hash, which is exactly the grouping that makes
 * this useful: rank the shapes by how often they recur and the top of that list
 * is a backlog of tools somebody keeps having to write by hand.
 */
export interface ToolsCodeRunPayload {
    /** The name the model CALLED the code runner by, since an app may mount
     *  more than one — the registered name, which differs from the runner's
     *  own option name when a runner is re-exposed under another schema name. */
    readonly tool: string;
    /** The language the runner was configured for. */
    readonly language: string;
    /** How many declared artifact inputs were staged as files for this run. */
    readonly stagedInputs: number;
    /** Characters of stdout returned, AFTER the runner's cap. */
    readonly outputChars: number;
    /** True when the output hit `maxOutputChars` and was cut. */
    readonly truncated: boolean;
    /**
     * Whether the program ran to completion, as the code runner REPORTED it.
     * ABSENT when the runner reported neither an exit status nor an error —
     * the outcome is unknown, and an absent `ok` is never success. A `false`
     * is told apart by the three fields below.
     */
    readonly ok?: boolean;
    /** The program's exit status — exactly when the code runner reported one;
     *  never inferred, never defaulted. */
    readonly exitCode?: number;
    /** `true`: the code runner flagged an error and reported no exit status, so
     *  whether the code ran is NOT confirmed — not a failed program. */
    readonly serviceError?: true;
    /** The signal that ended the process before it exited, when the code runner
     *  knows it (`'SIGKILL'`): no exit status exists, and none is recorded. */
    readonly terminatedBy?: string;
    /** A stable hash of the normalized call shape. Never the code. */
    readonly shapeHash: string;
}
/**
 * One use of an `mcpClientPool` in a run — a listing (`at: 'list'`, filed by the
 * tools slot after `list(ctx)`) or a tool call (`at: 'call'`, filed by the
 * dispatch loop for that call). Ids, digests, evidence and outcomes only: never
 * an identity value, a remote session id or handle, a header value, an
 * argument, a result, a definition or a server's error text. Rides the
 * `agentfootprint.tools.` prefix: a new domain would need a new bridge and
 * wildcard (see the `tools.session_*` note above).
 */
export interface ToolsMcpClientPayload {
    /** The pool's name, which `Tool.source` also carries. */
    readonly server: string;
    readonly at: 'list' | 'call';
    /** At `'call'`. */
    readonly toolCallId?: string;
    /**
     * The tool-session digest of the conversation's key (`hashSessionKey`):
     * enough to join two rows, not enough to name anyone. Absent when the caller
     * named no identity, so there was no key.
     */
    readonly keyHash?: string;
    /** The pool's own random id for the client touched; absent when none was. */
    readonly clientId?: string;
    /** 1 = the key's first client, 2 = its first replacement, … */
    readonly generation?: number;
    /** What happened to the conversation's client. */
    readonly client: 'opened' | 'joined' | 'reused' | 'not-needed' | 'refused' | 'expired' | 'handle-lost';
    /** Why the key's previous client left, when this use opened its replacement. */
    readonly previous?: 'idle' | 'evicted' | 'expired' | 'handle-lost' | 'terminated';
    /** With `'expired'` or `'handle-lost'`: what proves it. */
    readonly evidence?: 'server-404' | 'transport-forgot-handle';
    readonly refusal?: 'pool-full' | 'principal-full' | 'ending-full' | 'init-timeout' | 'init-failed' | 'no-identity' | 'incomplete-identity' | 'identity-mismatch' | 'stale-tools' | 'binding-changed' | 'request-unmatched' | 'lease-expired' | 'operations-full' | 'closed';
    /** Opened or joined: how long the open took. */
    readonly initMs?: number;
    /** At `'list'`: a shared entry, or the conversation's own client's list. */
    readonly definitions?: 'shared' | 'listed';
    /** When shared: the entry's age. */
    readonly definitionsAgeMs?: number;
    /** At `'call'`: the call met a replacement client and was checked against its fresh list. */
    readonly revalidated?: 'kept' | 'refused';
}
/**
 * What one attempt of an MCP request came to.
 *
 *   - `'answered'` — an HTTP response arrived and no retry rule refused it. It
 *     is delivered as it came, WHATEVER its status: a 503 is `answered` with
 *     `status: 503`. A response received is not an operation that succeeded,
 *     so nothing here is called success; read `status`. Nor is it the MCP
 *     call's answer: it records the HTTP exchange. A `202 Accepted`, or a
 *     stream that closes before the call's result, is `answered`, and the
 *     JSON-RPC answer may still never come — the end's `mayHaveRun` says when
 *     the call failed that way.
 *   - `'refused'` — a response the retry policy recognises as a refusal
 *     (`kind`): HTTP 429, or AgentCore Runtime's session-busy reply. It records
 *     the response the server sent, not proof that nothing ran: a 429 says the
 *     request was rate-limited, nothing more (RFC 6585 §4). `effect` says what
 *     is known; `permission` says what allowed asking again.
 *   - `'failed'` — the request threw before an answer arrived (a dropped
 *     connection, a DNS failure). The server may have run it, so it is asked
 *     again only when the app declared that for this tool (`replay`).
 *   - `'timed-out'` — the call's deadline (`maxElapsedMs`) cut the request
 *     while it was in flight.
 *   - `'cut'` — the call was cancelled while the request was in flight: the
 *     run's or the client's signal, the MCP SDK giving it up (its own timeout),
 *     or the call settling first. The request WAS sent; the server may already
 *     have received it and run it. A cut is never a "not run".
 *
 * The last three never had a response, so they carry `remote: 'unknown'`.
 *
 * Every request the series sent is a row, or is counted in `omitted`: the rows
 * and `omitted` together are the requests the series sent. A request that was
 * never sent — the next attempt after a cancelled wait, or anything after a
 * signal that was already aborted — has no row.
 */
export type ToolRequestAttemptOutcome = 'answered' | 'refused' | 'failed' | 'timed-out' | 'cut';
/**
 * How an MCP request ended: its last attempt's outcome — or `'cancelled'`, when
 * the call was cancelled with NO request in flight: while it waited to ask
 * again, or before its first request. Nothing more was sent; every request
 * that was sent has its own row and its own outcome.
 */
export type ToolRequestOutcome = ToolRequestAttemptOutcome | 'cancelled';
/**
 * Whether the server received and ran a request, when that is not known: the
 * attempt ended without a response (`failed`, `timed-out`, `cut`), so the
 * request may have reached the server and run there, or not. The library never
 * records such a request as not run.
 */
export type ToolRequestRemote = 'unknown';
/**
 * Why the request went on after an attempt, or ended with it.
 *
 *   - `'retryable'` — another attempt follows after `waitMs`; `permission`
 *     says what allowed it. The only reason that is not an end.
 *   - `'answered'` — answered; the response is delivered.
 *   - `'exhausted'` — the request has made every attempt it may.
 *   - `'wait-limit'` — the wait would pass the call's total waiting budget
 *     (`maxWaitMs`). A stated `Retry-After` is never shortened to fit: the
 *     refusal is delivered instead.
 *   - `'no-time-left'` — the wait would pass the call's deadline; or the
 *     deadline cut a request or a wait.
 *   - `'not-replayable'` — the request's body cannot be sent twice.
 *   - `'not-retryable'` — the attempt failed without an answer, and nothing
 *     permits asking again after that failure: the app declared no repeat of
 *     this tool on it (`replay`), or it is not a kind the series repeats.
 *   - `'not-permitted'` — refused with a 429, and the policy in force does not
 *     permit asking this tool again on it. Under `'app-authorized'`, only the
 *     app's declaration does: a generic 429 does not prove the server ran
 *     nothing.
 *   - `'changes-without-key'` — the app declared this tool as making changes,
 *     and the call carries no key the server is declared to deduplicate by. It
 *     is never asked again automatically, under any policy.
 *   - `'cancelled'` — the caller's signal ended the request.
 */
export type ToolRequestReason = 'retryable' | 'answered' | 'exhausted' | 'wait-limit' | 'no-time-left' | 'not-replayable' | 'not-retryable' | 'not-permitted' | 'changes-without-key' | 'cancelled';
/** The refusals a retry rule recognises: HTTP 429, and AgentCore Runtime's session-busy reply. */
export type ToolRequestRefusalKind = 'throttle' | 'session-busy';
/**
 * What is known about whether an attempt took effect on the server —
 * footprintjs's effect law (`AttemptEffect`), read for an MCP request:
 *
 *   - `'none'` — it ran nothing, and that is established: the request was
 *     never sent (an `end` whose `attempts` is `0`). No reply establishes it.
 *     A 429 says the request was rate-limited, and AgentCore's session-busy
 *     body is matched by its code and words — a server behind the runtime can
 *     answer with the same error after it ran the call.
 *   - `'unknown'` — anything sent that did not complete: a 429, a session-busy
 *     reply, a lost connection or a timeout after the request was sent, a cut,
 *     a 5xx, a JSON-RPC error, a tool error (one can follow a write that
 *     succeeded).
 *   - `'applied'` — the call completed: the server answered it, and not with
 *     an error. It is never proof that anything was written.
 */
export type ToolRequestEffect = 'none' | 'unknown' | 'applied';
/** The failure kinds the app may declare a tool repeatable on (`replay.tools[name].on`). */
export type ToolRequestReplayKind = 'throttle' | 'network';
/**
 * What allowed the next attempt — present exactly when `reason` is
 * `'retryable'`. Permission and effect are separate facts: neither is ever
 * read from the other.
 *
 *   - `by: 'declaration'` — the app's entry for this tool names the failure
 *     kind (`on`).
 *   - `by: 'adapter'` — the adapter the app chose retries this refusal as its
 *     own policy: AgentCore Runtime's session-busy reply, which AWS documents
 *     as a retryable conflict while a session starts or stops. It says what
 *     it assumes in doing so (`assumes`).
 *   - `by: 'compatibility'` — the compatibility policy, today's default: it
 *     repeats a 429 for every tool, and says what it assumes in doing so
 *     (`assumes`).
 *
 * An assumption is a policy, never evidence: the row's `effect` stays
 * `'unknown'`.
 */
export interface ToolRequestPermission {
    readonly by: 'declaration' | 'adapter' | 'compatibility';
    /** `declaration` only — the failure kind the app's entry names. */
    readonly on?: ToolRequestReplayKind;
    /** `adapter` and `compatibility` only — what the policy assumes of the refusal it repeats. */
    readonly assumes?: 'refused-before-execution';
    /**
     * The app declared this tool as making changes, so it was asked again only
     * because the server is declared to deduplicate by a key this call carries.
     * The key protects only if the server uses it.
     */
    readonly deduplicated?: true;
}
/**
 * footprintjs's failure categories (`AttemptCategory`) — the one fixed set
 * every attempt record uses, so a saved request groups with every other.
 */
export type ToolRequestFailureCategory = 'timeout' | 'rate-limited' | 'server' | 'network' | 'auth' | 'permanent' | 'unknown';
/**
 * A failure as the record keeps it: a fixed sentence and a code. The thrown
 * error's own message, name and fields are NEVER kept — a network or SDK
 * error's text can carry a URL with a token in it, a host's internal name or
 * a server's reply, and none of that is the library's to store.
 */
export interface ToolRequestError {
    readonly message: 'The request failed before an answer arrived. Its error is not kept.' | 'The call ran out of time before an answer arrived.';
    /** The failure's category — the same word as `failure`. */
    readonly code: ToolRequestFailureCategory;
}
/** One attempt of an MCP request, as its retry series recorded it. */
export interface ToolRequestAttempt {
    /** 1-based. */
    readonly attempt: number;
    readonly outcome: ToolRequestAttemptOutcome;
    readonly reason: ToolRequestReason;
    /** The response's HTTP status — present when one arrived (`answered`, `refused`). */
    readonly status?: number;
    /** Which refusal a retry rule recognised — `refused` only. */
    readonly kind?: ToolRequestRefusalKind;
    /**
     * The failure's category (footprintjs's `AttemptCategory`) — absent for
     * `answered`. Named `failure`, not `category`: that key belongs to a retired
     * contract the redaction rule no longer treats as the library's own.
     */
    readonly failure?: ToolRequestFailureCategory;
    /** The wait before the next attempt, in ms — present exactly when `reason` is `'retryable'`. */
    readonly waitMs?: number;
    /**
     * The wait the server stated in `Retry-After`, in ms — when it stated one.
     * Absent beside a `waitMs` means that wait was the library's own backoff.
     */
    readonly retryAfterMs?: number;
    /** `failed` and `timed-out` only. */
    readonly error?: ToolRequestError;
    /**
     * `'unknown'` when the attempt ended without a response (`failed`,
     * `timed-out`, `cut`): it was sent, and whether the server received and ran
     * it is not known. Absent when a response arrived: the record keeps what the
     * server answered (`status`) — for a `refused` attempt that is the server's
     * own refusal, not proof that nothing ran.
     */
    readonly remote?: ToolRequestRemote;
    /**
     * What is known about whether this attempt took effect (see
     * {@link ToolRequestEffect}). On every attempt the library files.
     */
    readonly effect?: ToolRequestEffect;
    /** What allowed the next attempt — present exactly when `reason` is `'retryable'`. */
    readonly permission?: ToolRequestPermission;
    /** How long the attempt took, in ms, from sending to its end. On every attempt the library files. */
    readonly durationMs?: number;
}
/** How an MCP request ended. */
export interface ToolRequestEnd {
    readonly outcome: ToolRequestOutcome;
    readonly reason: ToolRequestReason;
    /** The delivered response's HTTP status — `answered` and `refused`. */
    readonly status?: number;
    /** The refusal that was delivered — `refused` only. */
    readonly kind?: ToolRequestRefusalKind;
    /** The failure's category, as on an attempt. */
    readonly failure?: ToolRequestFailureCategory;
    /**
     * Present when the request was CUT, and says what it was doing then.
     * `'request'` — its LAST attempt was in flight (`cut`, `timed-out`): that
     * attempt was sent and is the last row, or, past 100 attempts, the last one
     * counted in `omitted`; whether the server ran it is unknown. `'wait'` — it
     * was waiting to ask again, so no request was in flight and the next one was
     * never sent: a cancellation cut the wait (`cancelled`), or the deadline left
     * no time to finish it and the last refusal was delivered (`refused`,
     * `no-time-left`). Absent when nothing was cut — and when the request was
     * cancelled before its first attempt, so that nothing was sent.
     */
    readonly during?: 'request' | 'wait';
    /** `failed` and `timed-out` only. */
    readonly error?: ToolRequestError;
    /**
     * `'unknown'` when the request ended on an attempt with no response
     * (`failed`, `timed-out`, `cut`): whether the server ran that request is not
     * known.
     */
    readonly remote?: ToolRequestRemote;
    /**
     * How many requests the series sent: `attempts.length + omitted`. On every
     * end the library files.
     */
    readonly attempts?: number;
    /**
     * What is known about the series' effect as a whole. Uncertainty survives:
     * once any attempt's effect is `'unknown'`, so is the series' — a later
     * proven refusal does not resolve it, and a later success does not prove the
     * operation happened only once. `'applied'` only when the call completed and
     * no attempt's effect is unknown; `'none'` only when every attempt is proven
     * to have run nothing (or none was sent). On every end the library files.
     */
    readonly effect?: ToolRequestEffect;
    /**
     * The call failed as "may have run" (footprintjs's `UnknownEffectError`, its
     * final error — a later refusal's, or the timeout — as the `cause`): under
     * the `'app-authorized'` policy, the call genuinely lacked an answer
     * somewhere and did not complete. Lacking an answer is not an unknown
     * effect: it is an attempt that got no response at all (`remote:
     * 'unknown'`), or a request accepted over HTTP (an `answered` 2xx) whose
     * JSON-RPC answer never came. A received refusal or error IS an answer —
     * its effect unknown, delivered as the server sent it — so a call whose
     * every attempt received one has no `mayHaveRun`. Absent under
     * `'compatibility'`, where the caller sees the error it always did, and
     * for a call its caller cancelled, which ends with the caller's reason.
     */
    readonly mayHaveRun?: true;
}
/**
 * What one MCP request's retry series did: every attempt it made, why it went
 * on or stopped after each, how long it waited, and how the request ended.
 *
 * Filed by `mcpClient`'s retry series for a call the agent dispatched, ONCE per
 * request, when the call settles — so the last attempt's `effect` can say
 * whether the call completed — and at the moment of a cancellation, so a
 * run cancelled during a wait still carries it when it is saved. A call that
 * settles before its series (the MCP SDK's own timeout, say) cancels the series
 * there and then, inside the call, so it is never filed after its run, and no
 * request is sent after it. The framework stamps `toolCallId`, `toolName` and
 * `iteration` from the dispatch, so two calls, or two runs, sharing one MCP
 * connection never mix their attempts.
 *
 * ALLOW-LISTED, field by field: numbers, the library's own words, the
 * endpoint label the app declared, or else the endpoint's origin. Never the
 * URL's path, query or credentials, the call's arguments, the server's answer
 * or an error's text.
 *
 * Only a transport `mcpClient` builds runs a series that files this; with
 * retry off (`retryOnThrottle: false` on a plain transport) there is no series
 * and no event.
 */
export interface ToolsRequestAttemptsPayload {
    readonly toolCallId: string;
    readonly toolName: string;
    readonly iteration: number;
    /** The `endpointLabel` the app declared for this endpoint, when it declared one. */
    readonly endpoint?: string;
    /** Otherwise the endpoint's ORIGIN — scheme, host and port, nothing else. */
    readonly origin?: string;
    /**
     * The attempts, in order — at most 100. Every request the series sent is
     * here or counted in `omitted`, a cut one included: `attempts.length +
     * omitted` is the number of requests the series sent. A request it never
     * sent has no row.
     */
    readonly attempts: readonly ToolRequestAttempt[];
    /** Attempts made past the first 100, counted and not listed. `0` when every attempt is listed. */
    readonly omitted: number;
    readonly end: ToolRequestEnd;
}
export interface ToolCoverageDeclaredPayload {
    readonly toolName: string;
    readonly toolCallId: string;
    readonly iteration: number;
    readonly checked?: readonly CoverageItemPayload[];
    readonly notChecked?: readonly CoverageItemPayload[];
    readonly cannotCover?: readonly CoverageItemPayload[];
    /**
     * What the read behind the value covered in time (honesty step 7b) — a
     * `coverage()`'s `period`, or a described result's (whose boundary this
     * event carries). Default-omitted.
     */
    readonly period?: PeriodPayload;
    /**
     * What a `coverage()`'s read found still running — its outcome not known
     * yet — as the tool declared it (a copy). Default-omitted: present only when
     * the ledger declared a well-formed, non-empty `in_progress`.
     */
    readonly inProgress?: readonly InProgressItemPayload[];
    /** What the library composed for a `search` tool — see {@link ToolAbsentPayload.composition}. */
    readonly composition?: CoverageCompositionPayload;
}
/** One thing a read found still running, as it rides an event: detached plain
 *  data, copied out of the tool's own declaration. */
export interface InProgressItemPayload {
    readonly what: string;
    readonly why?: string;
    /** RECORD-ONLY short form — the model was served the envelope without it. */
    readonly short?: string;
    /** How many things this entry stands for; absent = one. Served. */
    readonly count?: number;
}
/** One piece of declared ground, as it rides an event: detached plain data,
 *  copied out of the tool's own declaration. */
export interface CoverageItemPayload {
    readonly what: string;
    readonly why?: string;
    /** The item's short plain form, as the tool declared it — RECORD-ONLY: the
     *  model was served the envelope without it. Absent when not declared (or
     *  declared invalid by an envelope minted outside `absent()` / `coverage()`,
     *  which is read, never repaired). */
    readonly short?: string;
    /** What kind of ground the item is (`'existence' | 'scope'`), on
     *  `notChecked` / `cannotCover` only — RECORD-ONLY, like `short`. Absent =
     *  not declared; never inferred from `what`. */
    readonly kind?: 'existence' | 'scope';
}
/**
 * A tool returned a semantic envelope (`describedResult(…)`) — typed
 * series/facts/edges with the caveats that make them honest (grain,
 * provenance, coverage) as data.
 *
 * The event carries the FULL envelope — render hints, the three-list
 * coverage detail, the marker, everything — because this is the channel
 * recordings and UIs read; the MODEL received only the compact
 * rendering-free projection (`semanticsForModel`). Emitted BEFORE the
 * result ceiling is measured, so grain and provenance survive to the record
 * even when the content itself was refused as oversized. Detached plain
 * data (structuredClone), never a live reference into the tool's own value.
 */
export interface ToolSemanticsDeclaredPayload {
    readonly toolName: string;
    readonly toolCallId: string;
    readonly iteration: number;
    readonly semantics: ToolSemantics;
}
export interface PermissionCheckPayload extends Partial<ToolCallEventIdentity> {
    /** 9.11.0 — the shared vocabulary, so the event cannot drift from the
     *  request. `'skill_read'` joined it when skill activation became something
     *  a policy can refuse. */
    readonly capability: PermissionCapability;
    readonly actor: string;
    readonly target?: string;
    readonly result: 'allow' | 'deny' | 'halt' | 'gate_open';
    readonly policyEngine?: 'opa' | 'cerbos' | 'custom';
    readonly policyRuleId?: string;
    readonly rationale?: string;
    /** v2.12 — telemetry tag carried through from PermissionDecision.reason. */
    readonly reason?: string;
}
export interface PermissionGateOpenedPayload {
    readonly gateId: string;
    readonly openedBy: string;
    readonly expiresAt?: number;
}
/** A tool's declared credential is being resolved before invocation. */
export interface CredentialRequestedPayload extends Partial<ToolCallEventIdentity> {
    readonly service: string;
    readonly mode?: 'machine' | 'user';
}
/** A credential was issued. Carries the `kind` only — NEVER the token/secret. */
export interface CredentialAcquiredPayload extends Partial<ToolCallEventIdentity> {
    readonly service: string;
    readonly kind: string;
    readonly expiresAt?: number;
}
/** 3-legged consent is required (the tool is not run until the user authorizes).
 *  Carries `sessionId` for correlation, NOT the authorization URL. */
export interface CredentialAuthorizationRequiredPayload extends Partial<ToolCallEventIdentity> {
    readonly service: string;
    readonly sessionId: string;
}
/**
 * Credential resolution failed — the provider threw, so the tool is not run
 * (fail-closed: never half-authed).
 *
 * **Never carries the credential, the token, or the authorization URL.** See
 * the security contract on `CredentialProvider`: `reason` is the provider's own
 * thrown message, so a provider must scrub secrets before throwing.
 *
 * `tool` and `errorClass` were added in 9.4.0, for the operator watching a
 * dashboard rather than reading a transcript. Without the tool name this event
 * says a service failed but not what stopped working; without the class every
 * distinct failure is one undifferentiated string. Both are optional because
 * neither is always knowable: `tool` is absent when the resolution was not made
 * on a tool's behalf, and `errorClass` is absent when what was thrown was not
 * an `Error`.
 */
export interface CredentialFailedPayload extends Partial<ToolCallEventIdentity> {
    /** The service id the tool declared (or asked for), never the credential. */
    readonly service: string;
    /** The provider's thrown message. */
    readonly reason: string;
    /** The tool whose call needed it — the thing that actually stopped working. */
    readonly tool?: string;
    /** Constructor name of what was thrown (e.g. `'TypeError'`, `'TimeoutError'`)
     *  — the routable half of the failure, so alerts do not have to parse prose. */
    readonly errorClass?: string;
}
export interface PermissionGateClosedPayload {
    readonly gateId: string;
    readonly reason: string;
}
/**
 * Emitted (v2.12) when a `PermissionChecker.check()` returns
 * `{ result: 'halt', ... }`. Pairs with the typed `PolicyHaltError`
 * thrown by `Agent.run()` — the event is the OBSERVABILITY signal,
 * the error is the RUNTIME signal. Both carry the same `reason` for
 * routing (e.g. `'security:exfiltration'` → PagerDuty).
 *
 * Fires AFTER the synthetic tool_result has been written to scope.history
 * but BEFORE the run terminates, so observability adapters see the
 * halt while the conversation history is consistent for downstream
 * audit/replay.
 */
export interface PermissionHaltPayload extends Partial<ToolCallEventIdentity> {
    readonly checkerId?: string;
    readonly target: string;
    readonly reason: string;
    readonly tellLLM?: string;
    readonly iteration: number;
    readonly sequenceLength: number;
}
export interface FallbackTriggeredPayload {
    readonly kind: 'provider' | 'tool' | 'skill';
    readonly primary: string;
    readonly fallback: string;
    readonly reason: string;
}
export interface CostTickPayload {
    readonly scope: 'iteration' | 'turn' | 'run';
    /**
     * The model this money was spent on — the one `pricingTable.pricePerToken`
     * was asked about.
     *
     * Always present: a tick exists because a priced call happened, and a call
     * has a model. Without it the event said what was spent and not on what, so
     * a run that switched models mid-loop (a `'retry-other'` failover, a
     * summarizer on a cheaper model) produced a bill nothing could break down.
     */
    readonly model: string;
    /**
     * The provider that billed it, by `LLMProvider.name` — the other half of
     * attribution, since one model name can be served by several providers
     * (Bedrock and Anthropic both answer to a Claude model id, at different
     * prices).
     *
     * Optional for ONE honest reason: a window/compaction strategy reports its
     * summarizer spend through `WindowStrategyResult.spend`, and the provider is
     * read from that strategy's `billing` declaration, which the seam leaves
     * optional. Every shipped strategy declares it. Absent therefore means "the
     * strategy that billed this did not say", never "the agent's provider".
     */
    readonly provider?: string;
    readonly tokensInput: number;
    readonly tokensOutput: number;
    readonly estimatedUsd: number;
    readonly cumulative: {
        readonly tokensInput: number;
        readonly tokensOutput: number;
        readonly estimatedUsd: number;
    };
}
export interface CostLimitHitPayload {
    readonly kind: 'max_tokens' | 'max_cost' | 'max_iterations' | 'max_wallclock';
    readonly limit: number;
    readonly actual: number;
    readonly action: 'abort' | 'warn' | 'degrade';
}
export interface EvalScorePayload {
    readonly metricId: string;
    readonly value: number;
    readonly threshold?: number;
    readonly target: 'iteration' | 'turn' | 'run' | 'toolCall';
    /**
     * What the score is about: the run id (`event.meta.runId`) for `'run'` /
     * `'turn'`, the iteration index for `'iteration'`, the provider's call id
     * for `'toolCall'`. A NAME, not a capability: run ids are minted in
     * sequence and anyone can write one, so a host filing a score from user
     * input (a feedback button) checks the run is that user's before it emits.
     * `otelObservability` parents a score to a run only when the score's own
     * meta places it in that run's session; otherwise it rides a span of its
     * own that names the ref.
     */
    readonly targetRef: string;
    readonly evaluator?: 'llm' | 'fn' | 'heuristic';
    readonly evidence?: Readonly<Record<string, unknown>>;
    /**
     * The evaluator's own reading of the score, in a small vocabulary it
     * documents — `'pass'` / `'fail'`, `'relevant'` / `'not_relevant'`.
     * Optional and never derived: a `threshold` does not say which
     * side of it passes, so no label is ever computed from one.
     * `otelObservability` exports it BY DEFAULT as
     * `gen_ai.evaluation.score.label` (the spec's "SHOULD have low
     * cardinality"), so it must be a word from that vocabulary — never a
     * judge's sentence, which belongs in {@link EvalScorePayload.explanation}.
     */
    readonly label?: string;
    /**
     * The evaluator's free-form reason for the score. It is CONTENT —
     * a judge's reason can quote the answer it graded — so
     * `otelObservability` exports it (`gen_ai.evaluation.explanation`) only
     * under `captureContent`.
     */
    readonly explanation?: string;
}
export interface EvalThresholdCrossedPayload {
    readonly metricId: string;
    readonly direction: 'above' | 'below';
    readonly value: number;
    readonly threshold: number;
    readonly actionTaken?: string;
}
/**
 * Emitted (v2.13) when the agent's final answer fails the agent's
 * configured `outputSchema` (the parser passed to
 * `Agent.create({...}).outputSchema(parser)`).
 *
 * Scope: ONLY agent-level final-answer validation. Tool-input validation
 * (`LLMToolSchema.inputSchema`) is a different concern handled by
 * provider-side type checks; this event does NOT fire for tool-arg
 * validation failures.
 *
 * Lives in the `agent.*` domain (parallel to `agent.turn_end`) because
 * final-answer validation is a turn-level concern, not a generic
 * evaluation metric.
 *
 * Pairs with `agentfootprint.error.retried` (when a reliability rule
 * routes the failure to retry with feedback) or
 * `agentfootprint.reliability.fail_fast` (when retries are exhausted).
 *
 * The event is the OBSERVABILITY signal — it fires on EVERY validation
 * failure, regardless of whether retries are configured. Use the
 * `attempt` + `cumulativeRetries` fields to drive operator dashboards
 * for retry-rate trending (a leading indicator for model drift).
 *
 * Fires BEFORE PostDecide rules evaluate, so observability sees the
 * failure even if a buggy rule routes to fail-fast or swallows it.
 */
/**
 * Emitted (7.26) once per failed final answer that the run is about to
 * ASK AGAIN about, on an agent built with
 * `.outputSchema(parser, { retries })`.
 *
 * Its sibling `agent.output_schema_validation_failed` reports a failure the
 * reliability gate is handling INSIDE one `call-llm` stage. This one reports
 * a failure the LOOP is handling: the corrective message named here joins the
 * conversation, the ReAct loop re-enters, and the next attempt arrives with
 * its own `stream.llm_start` / `stream.llm_end` bracket and its own
 * `cost.tick`. Subscribe to it to see how often a model needs a second ask —
 * a leading indicator of drift, and of a schema that is harder to hit than
 * its author thinks.
 *
 * Fires from the retry branch, after the corrective turn is committed, so a
 * consumer that reads `snapshot.sharedState.outputAttempts` finds the
 * matching row (joined by `correctiveMessageHash`) already there.
 */
/**
 * The run's answer does NOT satisfy its own `outputSchema` (8.18.0).
 *
 * Fires once, from the Route decider, on the run that hands back an answer the
 * contract rejects — including the default `retries: 0` case, where the first
 * answer is the only one. It is the event that was missing: `run()` returned
 * the failing string, `runTyped()` threw at a boundary the caller may not have
 * used, and nothing in between said a contract had been missed.
 *
 * Alert on it. A rise in `stage: 'json-parse'` is a model that stopped
 * honouring the instruction; a rise in `'schema-validate'` is drift against
 * the shape; any `brokenBy` at all is one of your own output rules.
 */
export interface AgentOutputContractUnmetPayload {
    /** Which half of validation failed. */
    readonly stage: 'json-parse' | 'schema-validate';
    /** The validator's own message, verbatim. DATA, not narrative. */
    readonly error: string;
    /** Failing field path when the parser exposes one (Zod-style issues). */
    readonly path?: string;
    /** Answers judged in this run, the first included. `1` under `retries: 0`. */
    readonly attempts: number;
    /** Corrective re-asks the run paid for. `0` under `retries: 0`. */
    readonly retriesSpent: number;
    /** True when `.outputFallback()` is configured — a tier `runTyped()` reaches
     *  and `run()` does not. */
    readonly fallbackConfigured: boolean;
    /** The ReAct iteration that produced the answer. */
    readonly iteration: number;
    /** Present when an `act({ output })` middleware rewrote an answer that had
     *  PASSED into one that fails — the name of that middleware. The run stops
     *  re-asking when this is set: the model's answer was already right. */
    readonly brokenBy?: string;
}
export interface AgentOutputSchemaRetryPayload {
    /** 1-based attempt that just failed. `1` is the first answer. */
    readonly attempt: number;
    /** Corrective asks left AFTER this one. `0` means this is the last. */
    readonly retriesRemaining: number;
    /** The ReAct iteration the failed answer came from. A retry consumes an
     *  iteration, so the next attempt reports `iteration + 1`. */
    readonly iteration: number;
    /** Which half of validation failed — `'json-parse'` (the model emitted
     *  prose) vs `'schema-validate'` (JSON, wrong shape). They trend
     *  differently under model drift. */
    readonly stage: 'json-parse' | 'schema-validate';
    /** The validator's own message, verbatim. DATA, not narrative: it is
     *  quoted into the corrective message after an authored frame, and it is
     *  quoted here the same way. */
    readonly error: string;
    /** Failing field path when the parser exposes one (Zod-style issues). */
    readonly path?: string;
    /** `fnv1a` of the corrective message that went back to the model — the
     *  join to the message in `history` and to the `outputAttempts` row. */
    readonly correctiveMessageHash: string;
}
/**
 * One verdict from the evidence gate (`.namesAndNumbersFromEvidence()`,
 * 9.35.0) — fired once per would-be-final answer the gate judged, whatever
 * the posture and whatever the outcome.
 *
 * It rides the emit channel rather than a CommitBundle because it is a fact
 * about ONE attempt, not conversation state; the only thing that reaches the
 * commit log is the terminal verdict (`unsupportedValues`), which the boundary
 * needs.
 *
 * **What this event does NOT claim:** the check catches values that appear in
 * no tool result. It cannot see a false statement assembled from real values,
 * so `action: 'grounded'` means "every name and number was read from
 * somewhere", never "the answer is true".
 */
export interface AgentEvidenceCheckedPayload {
    /** The ReAct iteration that produced the judged answer. */
    readonly iteration: number;
    /** The posture in force — `'assist'` records only, `'guard'` may revise
     *  once, `'rails'` may withhold the answer. */
    readonly posture: 'assist' | 'guard' | 'rails';
    /** How many distinct values the answer had to ground. `0` means the answer
     *  asserted nothing the extractor treats as data. INCLUDES exempt values —
     *  see {@link lookedUp}. */
    readonly candidates: number;
    /**
     * How many of `candidates` the gate actually looked up in the tool results:
     * the values found plus the values not found, before `unsupported` is
     * sliced. The rest were EXEMPT — the person's message, the conversation or
     * the app's own instructions (recalled memory included) already held them —
     * and were never looked up. Present on every row from the release that
     * added it; absent on a recording made before it, which therefore cannot
     * say how many values were looked up.
     */
    readonly lookedUp?: number;
    /** The values that appear in no tool result, truncated to a readable list.
     *  Empty when `action` is `'grounded'`. Under `code: 'match'` each says
     *  where it sat: `'code'` when every occurrence was inside a fenced block
     *  that matched no submission, `'prose'` otherwise. */
    readonly unsupported: readonly {
        readonly value: string;
        readonly shape: string;
        readonly region?: 'prose' | 'code';
    }[];
    /**
     *   • `'grounded'`       — every value was found; the answer stands.
     *   • `'revision-asked'` — the values went back to the model for one more
     *                          turn (emitted by the EvidenceRecheck branch).
     *   • `'flagged'`        — the answer ships carrying them, on the record.
     *   • `'refused'`        — `'rails'`: the answer was withheld and the
     *                          boundary raises `UnsupportedValuesError`.
     */
    readonly action: 'grounded' | 'flagged' | 'revision-asked' | 'refused';
    /** True when this judgement ran on an answer a revision already corrected —
     *  which is how a reader tells "the revision fixed it" (`grounded`, after
     *  revision) from "it did not" (`flagged`/`refused`, after revision). */
    readonly afterRevision: boolean;
    /** Set when the evidence index hit its ceiling and could not hold every
     *  tool result. The gate then records its verdict WITHOUT acting on it —
     *  a partial corpus can call a grounded value fabricated. */
    readonly evidenceTruncated?: boolean;
    /**
     * WHICH RESULTS OF THIS TURN THE ANSWER'S VALUES WERE READ FROM — on the
     * verdicts about an answer that ships (`'grounded'`, `'flagged'`,
     * `'refused'`), never on `'revision-asked'` (a draft). One entry per
     * `role: 'tool'` result of this turn that carried at least one of the
     * answer's names and numbers: `values` = how many, `only` = how many of
     * those no other result of this turn carried. A result of this turn that
     * is NOT listed carried none of them — a fact about tokens, not meaning:
     * read it as "the answer cites nothing from it", never "unused" (an answer
     * that states no value can still rest on a result). `[]` is a fact too:
     * no result of this turn carried any of them (an answer drawn from an
     * earlier turn, or one that states no value — `lookedUp` tells which).
     *
     * ABSENT when the list cannot be whole: the evidence index hit its ceiling,
     * or one value was carried by more results than the index keeps. Absent on
     * recordings made before the field existed. Identities and counts only,
     * never a value (`evidence/answerCarriers.ts`).
     */
    readonly carriedBy?: readonly {
        readonly toolCallId: string;
        readonly values: number;
        readonly only: number;
    }[];
    /**
     * On `action: 'revision-asked'` only, and only when the turn held placed
     * artifact tickets a served `wants` tool can spend: the refs the
     * correction NAMED as the route to a grounded number. Absent otherwise —
     * including for every agent without a `wants`-declaring tool.
     */
    readonly stagedRefs?: readonly {
        readonly ref: string;
        readonly kind: string;
    }[];
    /** The spender tools the correction named, beside {@link stagedRefs}. */
    readonly spenderTools?: readonly string[];
    /**
     * Under the figures dial (`namesAndNumbersFromEvidence({ figures: true })`),
     * on `'grounded'`, `'flagged'` and `'refused'`: the numbers no tool result
     * carried that ARE a declared derivation of the numbers the results carry —
     * each with its `derivation` (`rounded`, `unit-scale`, `column-sum`,
     * `column-ratio`, `column-difference`, `complement`) and `from` (the
     * operands, in the results' own keys). Not read and not invented: the model
     * computed them. Present (possibly `[]`) exactly when the dial is on, so its
     * presence says the derivation check ran; the `unsupported` numbers beside
     * it matched no value AND no derivation. Sliced like `unsupported`.
     */
    readonly computed?: readonly {
        readonly value: string;
        readonly shape: string;
        readonly derivation: string;
        readonly from: string;
    }[];
    /**
     * Under `.namesAndNumbersFromEvidence({ code: 'match' })`, on a prose
     * answer: one verdict per fenced block the answer showed — a block equal,
     * byte for byte after removing only the Markdown wrapper, to a program a
     * call of THIS turn submitted to a declared code-running tool carries that
     * call's id, tool and label (`completed`, `outcome-unknown`, …) and was not
     * read for values; any other block is `unmatched` with a reason and was
     * read as prose. `earlierTurnRows` counts earlier turns' script rows (never
     * compared); `regionsDropped` the blocks past the cap. Never the code.
     */
    readonly scripts?: ScriptsVerdict;
}
/**
 * The staged-refs nudge fired (`.namesAndNumbersFromEvidence({ nudge: true })`)
 * — one per iteration whose request gained the late line naming staged
 * artifact refs and the `wants` tool that spends them.
 *
 * The line itself is request-only (never history), so this event is the ONE
 * record that it existed: which refs, which spenders, which iteration. A run
 * where the nudge never armed emits none of these.
 */
export interface AgentGroundingNudgedPayload {
    /** The ReAct iteration whose request carried the line. */
    readonly iteration: number;
    /** The staged refs the line named — `ref` verbatim, `kind` as minted
     *  (`Tool.resultKind` / `tool-result/<toolName>`). Capped; see
     *  {@link refsOmitted}. */
    readonly refs: readonly {
        readonly ref: string;
        readonly kind: string;
    }[];
    /** Matched refs beyond the cap, stated rather than silently dropped.
     *  Absent when every match was named. */
    readonly refsOmitted?: number;
    /** The spender tools named, by their registered names — the tools whose
     *  declared `wants` matched a staged ref's kind, exact-string, never by
     *  tool name. */
    readonly tools: readonly string[];
}
/**
 * The model this run STARTS with, and what may replace it mid-run.
 *
 * `model` is the agent's resolved default — the one `callLLM` sends unless
 * something overrides it. It is deliberately not the "final" model: a
 * `.configure()` resolver reads the run's input in the seed stage (AFTER this
 * event), and per-skill brains pick per iteration. Resolving `.configure()`
 * here to make this field "effective" would call a consumer's resolver twice
 * per run, and two calls can disagree — so the manifest names the starting
 * choice and {@link RunConfiguredLlmPayload.modelOverrides} names who is
 * allowed to change it. What each call REALLY used is on
 * `agentfootprint.stream.llm_start`, per call, as it always was.
 */
export interface RunConfiguredLlmPayload {
    /** `LLMProvider.name` — the effective provider, decorators included. */
    readonly provider: string;
    /** The agent's resolved default model. */
    readonly model: string;
    /**
     * Which mounted mechanisms may replace `model` during this run, by NAME —
     * `'configure'` (a `.configure()` resolver) and `'skill-brains'` (per-skill
     * providers/models, escalation included). Absent when nothing can: then
     * `model` is what every call of this run used, and an experiment can group
     * on it alone.
     */
    readonly modelOverrides?: readonly ('configure' | 'skill-brains')[];
}
/**
 * One mounted memory, by the names it DECLARED.
 *
 * The store itself is not named: `MemoryStore` declares no id and no kind, so
 * naming which backend was in play would mean inventing a label (a class name
 * that survives no bundler, say) and an invented label is exactly what an arm
 * must not be grouped on. What IS declared is here.
 */
export interface RunConfiguredMemoryPayload {
    /** The `defineMemory({ id })` — the same id `memory.*` events carry. */
    readonly id: string;
    /** `'episodic' | 'semantic' | 'narrative' | 'causal'`. */
    readonly type: MemoryType;
    /** The strategy KIND it was compiled from (`'window'`, `'topK'`, …).
     *  Absent for a hand-built definition, which declares none. */
    readonly strategy?: MemoryStrategyKind;
    /** `RetrievalStrategy.name`, when the memory was given a spelled-out
     *  retrieval rule. Absent for the `{ topK, threshold }` shorthand — which
     *  is a rule with no name, not the default one. */
    readonly retrieval?: string;
    /** The embedder in play, by its own `Embedder.id` (or the declared
     *  `embedderId` when the store embeds server-side and no embedder was
     *  passed). Absent when neither side named one. */
    readonly embedderId?: string;
    /** `'memory'` (conversation recall) or `'rag'` (corpus retrieval), when the
     *  definition declared which claim it makes. */
    readonly flavor?: MemoryFlavor;
}
/**
 * The mounted skill graph's routing posture. The OBJECT's presence means a
 * graph is mounted; each absent field means that half was not declared.
 */
export interface RunConfiguredSkillGraphPayload {
    /** The mount's `strictness` — how much authority the graph has over the
     *  model's own picks. Absent for a graph mounted without the turn-start
     *  cascade options (routing then works exactly as it did in 9.16). */
    readonly routing?: 'assist' | 'guard' | 'rails';
    /** Whether the cursor survives the turn. Absent for the same reason. */
    readonly continuity?: 'turn' | 'conversation';
    /**
     * `IntentScorer.name` — the `.classify()` scorer that routes turn starts.
     * Absent when the graph declares no classifier, INCLUDING a graph routed by
     * `entryBy()` / `entryByRelevance()`: those hand the agent a bound
     * `scoreEntries` function and never the strategy that owns it, so there is
     * no name here to report. `skill.turn_routed.scorer` names the scorer that
     * really ran, whichever door it came through.
     */
    readonly scorer?: string;
}
/**
 * The artifacts wiring. Presence means a store is configured; like
 * `MemoryStore`, `ArtifactStore` declares no id, so WHICH store is not named.
 */
export interface RunConfiguredArtifactsPayload {
    /** True when the placement dial is on — oversized tool results travel to
     *  the model as a claim ticket instead of inline text. The threshold is a
     *  number, not a name, so it is not carried. */
    readonly placement?: true;
    /** True when each run is filed as a recording artifact. */
    readonly recordings?: true;
}
/**
 * One applied recipe — the declared, versioned composition that produced part
 * of this agent (9.48.0).
 *
 * The pair is what makes the row worth reading: two runs of `support-desk` that
 * answered differently are a mystery until the record says one was `1.2.0` and
 * the other `1.3.0`. The recipe's `description` is deliberately not here — it
 * is prose about the composition, and the manifest carries what a consumer
 * BRANCHES on.
 */
export interface RunConfiguredRecipePayload {
    /** The composition's plain name, e.g. `'support-desk'`. */
    readonly id: string;
    /** Its SemVer version, e.g. `'1.2.0'`. Kept a SEPARATE field from `id` —
     *  a composed `'support-desk@1.2.0'` would be one string that two different
     *  pairs could produce as soon as either half may contain the separator. */
    readonly version: string;
}
/**
 * The run-configuration manifest — ONE event at run start naming which
 * adapters and strategies this run is about to use.
 *
 * ## Why it exists
 *
 * Swapping a strategy in this library is easy; ATTRIBUTING an outcome to it
 * was not. Every other event carries `meta.runId`, and the numbers are already
 * there (tokens, latency, iterations, routing, refusals, evidence verdicts) —
 * what was missing was the JOIN KEY that says which of them belong to the same
 * ARM. Two ports used to name what varied (`skill.turn_routed.scorer`,
 * `memory.retrieved.strategy`); everything else was anonymous, and grouping N
 * runs into labelled arms was the experimenter's own bookkeeping, differently
 * spelled in every study. Group by this payload and the arms name themselves.
 *
 * ## Names and ids ONLY — the hard rule
 *
 * No credentials, endpoints, connection strings, keys, tenants or principals,
 * and no config VALUE that could be one. A manifest that leaks an endpoint is
 * worse than no manifest: it travels into every recording, every vendor sink
 * and every shared trace, which is precisely where a secret must not be. When
 * the only handle on a component is a value (a directory, a URL, a table), the
 * component is reported as PRESENT and left unnamed. Pinned by test.
 *
 * ## Absence is a fact, never a guess
 *
 * An absent field means "not configured" or "the strategy did not say" — never
 * `'default'` or `'unknown'` filled in on a reader's behalf, the same
 * distinction `CostTickPayload.provider` already makes. `memories: []` is the
 * explicit "no memory is mounted".
 *
 * ## What it costs
 *
 * One small event per run, by design — the manifest of a run that emitted no
 * manifest cannot be recovered afterwards. It carries names, never lists that
 * grow with the workload (tool names ride `tools.offered`, injections ride
 * `context.injected`), so its size is bounded by the agent's CONFIGURATION and
 * not by the turn. Skipped entirely when nothing is listening.
 *
 * @example Group two runs into two arms
 * ```ts
 * const arms = new Map<string, string>();          // runId → arm label
 * agent.on('agentfootprint.agent.run_configured', (e) => {
 *   arms.set(e.meta.runId, `${e.payload.llm.model}/${e.payload.memories[0]?.retrieval ?? 'no-retrieval'}`);
 * });
 * agent.on('agentfootprint.agent.turn_end', (e) => {
 *   record(arms.get(e.meta.runId), e.payload.durationMs);
 * });
 * ```
 */
export interface AgentRunConfiguredPayload {
    /** The agent's stable id — the same one `meta.compositionPath` carries.
     *  A study that runs two differently-configured agents groups on this. */
    readonly agentId: string;
    readonly llm: RunConfiguredLlmPayload;
    /** The configured cache strategy cannot apply the declared policy.
     * Configuration metadata only; absent when caching is off or supported. */
    readonly cache?: {
        readonly requested: 'explicit' | 'default';
        readonly strategy: 'none';
        readonly status: 'not-applicable';
    };
    /** Which chart shape ran the loop. A strategy choice with its own
     *  behaviour, not a cosmetic one (see `AgentOptions.reactMode`). */
    readonly reactMode: 'classic' | 'dynamic' | 'dynamic-grouped';
    /** Every mounted memory, declaration order. `[]` means none is mounted —
     *  stated rather than omitted, because "no memory" is itself an arm. */
    readonly memories: readonly RunConfiguredMemoryPayload[];
    /** `WindowStrategy.name` — the one strategy `.window()` or `.compaction()`
     *  mounted (both doors set the same strategy). Absent = no window stage. */
    readonly window?: string;
    readonly skillGraph?: RunConfiguredSkillGraphPayload;
    /** The evidence gate's posture (`.namesAndNumbersFromEvidence()`).
     *  Presence means the gate is on; absence means it is not mounted. */
    readonly evidenceGate?: 'assist' | 'guard' | 'rails';
    readonly artifacts?: RunConfiguredArtifactsPayload;
    /**
     * The recipes `.recipe()` applied, in declaration order — which declared,
     * versioned composition produced this agent (9.48.0).
     *
     * ABSENT when none was applied, and that is the one asymmetry with
     * {@link memories}: "no memory" is an arm a study compares against, while
     * "no recipe" is the state of every agent written before recipes existed,
     * and an empty list on all of them would be new bytes in every recording for
     * a feature nobody used.
     */
    readonly recipes?: readonly RunConfiguredRecipePayload[];
}
export interface AgentOutputSchemaValidationFailedPayload {
    /** Validation error message (from Zod / parser), or a fixed sentence when
     *  `draftWithheld` is true — parser text can quote the draft. */
    readonly message: string;
    /** Validation stage — JSON parse vs schema validate. Lets dashboards
     *  distinguish "model emitted prose" (`json-parse`) from "model emitted
     *  JSON but wrong shape" (`schema-validate`); they trend differently
     *  under model drift. */
    readonly stage: 'json-parse' | 'schema-validate';
    /** Failing field path when the parser exposes one (e.g. `'amount.currency'`).
     *  Only set when `stage === 'schema-validate'`. */
    readonly path?: string;
    /** The raw string output that failed — useful for narrative entries showing
     *  "what the model actually said" alongside the validation error. Absent
     *  when `draftWithheld` is true. */
    readonly rawOutput?: string;
    /** An output policy governs delivery, and this draft never passed output
     *  admission: its text (`rawOutput`, and the parser's message) is withheld
     *  from the event stream. Rules still read it on `validationError`. */
    readonly draftWithheld?: true;
    /** 1-indexed attempt counter. `1` for the first failure, `2` for the
     *  retry that also failed, etc. */
    readonly attempt: number;
    /** Total output-schema failures in this gate execution. Same as
     *  `validationErrorHistory.length`. Distinct from `attempt` because a
     *  gate can also retry on non-validation errors (5xx, etc.) — this
     *  counts ONLY the schema-driven failures. */
    readonly cumulativeRetries: number;
}
export interface ErrorRetriedPayload {
    readonly attempt: number;
    readonly maxAttempts: number;
    readonly lastError: string;
    /** How long the decorator waited before the attempt. */
    readonly backoffMs: number;
    readonly reason: string;
    /**
     * The wait the failure STATED (Retry-After, a gateway's "try again in N
     * seconds"), in ms. Absent when it stated none; `backoffMs` is max(the
     * schedule, this), capped by `maxDelayMs`.
     */
    readonly statedWaitMs?: number;
}
export interface ErrorRecoveredPayload {
    readonly attempt: number;
    readonly totalDurationMs: number;
}
/**
 * v9.32 — `withCircuitBreaker` moved between states.
 *
 * The gap this fills, named: until 9.32 the breaker reported nothing at all
 * through the in-run channel. `onStateChange` was the only way to see a trip,
 * and that is a consumer-level callback where a run's correlation ids are not
 * available — so a breaker that opened mid-run was invisible in the trace
 * beside the tool calls it stopped. An independent reviewer (2026-08-13, on a
 * local harness of scripted failures) watched a breaker open after two
 * failures, serve the next request from fallback, half-open after cooldown and
 * close after two probes: every step correct, every step off the record.
 *
 * Transitions ONLY. A hundred requests rejected while the breaker is already
 * open produce zero events — a re-entry into the same state is not a change,
 * and reporting it would turn a state log into a request log.
 *
 * On the `error.*` domain because that is where the provider decorators
 * already report; `reliability.*` is the separate rules-based loop.
 */
export interface ErrorCircuitChangedPayload {
    /** The state entered. */
    readonly state: 'closed' | 'open' | 'half-open';
    /**
     * WHY, in the breaker's own words — `'3 consecutive failures'`,
     * `'cooldown elapsed'`, `'2 probe successes'`, `'half-open probe failed'`.
     *
     * Never an error's message: the failure that tripped it is reported by
     * whoever threw it, and copying it here would attribute one vendor's text to
     * a state machine that only counted.
     */
    readonly reason: string;
    /** WHICH provider this breaker wraps — `inner.name`, not a composite chain
     *  name. Two breakers under one fallback produce one stream, and this is
     *  what tells them apart. */
    readonly providerName: string;
}
/**
 * Fired when the rules loop (or the reliability gate chart) gives up via
 * a `fail-fast` decision. Superset shape: `phase`/`kind`/`attempt` are
 * always present; the remaining fields are populated by whichever site
 * emits (the loop carries `label`/`providerUsed`/`errorKind`; the gate
 * chart carries `reason`).
 */
export interface ReliabilityFailFastPayload {
    readonly phase: 'pre-check' | 'post-decide';
    /** The matched rule's `kind` (machine-readable bucket). */
    readonly kind: string;
    /** 1-indexed attempt counter at the point of failure. */
    readonly attempt: number;
    /** Human-readable label of the matched rule (loop sites). */
    readonly label?: string;
    /** Free-form reason string (gate-chart sites). */
    readonly reason?: string;
    /** Provider in use when the loop failed fast. */
    readonly providerUsed?: string;
    /** Classification of the failure being failed-fast on. */
    readonly errorKind?: string;
    /** Originating error message, when present. Absent for a schema failure
     *  under an output policy — the parser's message can quote the draft. */
    readonly errorMessage?: string;
}
/**
 * Fired each time the rules loop decides to RETRY after a failed attempt
 * — `action` distinguishes a same-provider retry from a provider failover.
 */
export interface ReliabilityRetriedPayload {
    /** 1-indexed counter of the attempt that just FAILED and is being retried. */
    readonly attempt: number;
    /** `retry` = same provider again; `retry-other` = switch provider. */
    readonly action: 'retry' | 'retry-other';
    /** Classification of the failure being retried. */
    readonly errorKind: string;
    /** Originating error message, when present. Absent for a schema failure
     *  under an output policy — the parser's message can quote the draft. */
    readonly errorMessage?: string;
    /** Provider that just failed. */
    readonly fromProvider: string;
    /** Provider the NEXT attempt will use (equals `fromProvider` for `retry`). */
    readonly toProvider: string;
}
/**
 * Fired when the rules loop produces a successful response AFTER one or
 * more failed attempts (self-healed). `recoveredVia` names the mechanism
 * of the final successful step.
 */
export interface ReliabilityRecoveredPayload {
    /** 1-indexed attempt number that finally succeeded. */
    readonly attempt: number;
    /** How recovery happened. */
    readonly recoveredVia: 'retry' | 'retry-other' | 'fallback';
    /** How many attempts failed before this success. */
    readonly priorFailures: number;
    /** Classification of the LAST failure before recovery. */
    readonly errorKind: string;
}
/**
 * Tier 2 engaged: `parseOutput()` / `runTyped()` could not validate the
 * model's answer against `outputSchema`, and the consumer-supplied
 * `fallback` function is about to run. Fires BEFORE the fallback executes,
 * so it is a record of degradation starting — not of it succeeding.
 */
export interface ResilienceOutputFallbackTriggeredPayload {
    /** Which half of validation failed: bad JSON, or good JSON of the wrong shape. */
    readonly stage: 'json-parse' | 'schema-validate';
    /** First 200 chars of the model's raw answer. Truncated by construction —
     *  never the whole output. */
    readonly rawOutputPreview: string;
    /** Message of the `OutputSchemaError` that opened the ladder. */
    readonly primaryErrorMessage: string;
    /** Corrective re-asks this run already paid for before the answer reached
     *  the ladder (the `outputAttempts` ledger minus the first attempt).
     *  ABSENT when the caller parsed a string that did not come from this
     *  agent's last run — absent means unknown, never zero. */
    readonly retriesSpent?: number;
}
/**
 * Tier 3 engaged: the fallback function itself failed (threw, or returned a
 * value that also failed the schema) and the static `canned` value is being
 * returned instead. This is the event that says a run billed real tokens and
 * ended in a constant — with `canned` configured, `runTyped()` is
 * structurally unable to throw, so this event is the ONLY signal.
 */
export interface ResilienceOutputCannedUsedPayload {
    /** First 200 chars of the model's raw answer. Truncated by construction. */
    readonly rawOutputPreview: string;
    /** Why tier 2 did not hold — the fallback's own throw, or the validation
     *  error its return value produced. */
    readonly fallbackErrorMessage: string;
    /** Corrective re-asks this run already paid for. ABSENT when unknown. */
    readonly retriesSpent?: number;
}
export interface EmbeddingGeneratedPayload {
    readonly model: string;
    readonly provider: 'openai' | 'cohere' | 'voyage' | 'local' | 'custom';
    readonly inputKind: 'query' | 'document';
    readonly dimension: number;
    readonly count: number;
    readonly durationMs: number;
    readonly tokensSpent?: number;
}
/**
 * Emitted (v2.14) per provider chunk that carries thinking-content
 * tokens. Lives in `stream.*` domain — parallel to `stream.token` for
 * visible-content tokens.
 *
 * **Provider behavior:**
 * - Anthropic: fires for every `content_block_delta` with
 *   `delta.type === 'thinking_delta'`. May fire 100s of times per turn.
 * - OpenAI o1/o3: NEVER fires (OpenAI doesn't stream reasoning content
 *   as of early 2026). Only `thinking_end` fires at response completion.
 * - Custom providers: fire when `ThinkingHandler.parseChunk()` returns
 *   a non-empty `thinkingDelta`.
 *
 * **Default consumer behavior:** thinking_delta events are emitted but
 * not surfaced to end users unless a consumer explicitly subscribes to
 * this event (e.g. for reasoning-as-it-streams UIs).
 *
 * **Sensitive data:** `content` is raw model thinking text. Use
 * `RedactionPolicy.thinkingPatterns` (Phase 3) to scrub before audit-log
 * adapters fire. Same risk profile as `stream.token`.
 */
export interface StreamThinkingDeltaPayload {
    readonly iteration: number;
    readonly tokenIndex: number;
    /** Per-chunk delta text, NOT accumulated. ~10–50 chars typical. */
    readonly content: string;
}
/**
 * Emitted (v2.14) once per LLM call where thinking blocks were
 * produced. Pairs with the leading `stream.thinking_delta` events when
 * streaming, OR fires standalone for non-streaming providers (OpenAI).
 *
 * Use this event for live per-iteration UIs (chat-bubble reasoning
 * pills, retry-rate dashboards, telemetry). The `blocks` field carries
 * the same content that lands on `LLMMessage.thinkingBlocks` — read it
 * here for live display instead of post-walking `scope.history` after
 * the run completes (the framework's "collect during traversal" rule).
 *
 * **`tokens` field population:**
 * - Anthropic: `undefined` currently — Anthropic's `response.usage`
 *   doesn't break out thinking tokens (bundled in `output_tokens`).
 *   May change in future Anthropic API revisions.
 * - OpenAI o1/o3: populated from
 *   `response.usage.completion_tokens_details.reasoning_tokens`.
 * - Custom providers: populated when handler computes it during
 *   `normalize()`.
 *
 * **Sensitive data:** the `blocks` field carries reasoning content.
 * Same risk profile as `stream.token` — wildcard (`*`) recorders
 * piping to external sinks (Datadog, CloudWatch, OTel) will see this.
 * Treat thinking content with the same redaction posture you give
 * visible response tokens. `providerMeta` is already stripped by the
 * framework before persistence (Phase 6 invariant), so the blocks
 * here match the audit-log surface bytes-exactly.
 */
export interface StreamThinkingEndPayload {
    readonly iteration: number;
    readonly blockCount: number;
    readonly totalChars: number;
    readonly tokens?: number;
    /**
     * v2.14+ — the normalized thinking blocks for this LLM call.
     *
     * Same data the framework persists to `LLMMessage.thinkingBlocks`
     * (post-`providerMeta` strip). Lets live consumers render the
     * model's chain-of-thought per iteration without scope-walking
     * after the run.
     *
     * Empty / undefined when no thinking content was produced this
     * call (handler returned `[]`). Non-empty when at least one
     * thinking or redacted_thinking block landed.
     */
    readonly blocks?: readonly ThinkingBlock[];
}
/**
 * Emitted (v2.14) when a `ThinkingHandler.normalize()` call throws.
 * The framework catches the throw, drops the thinking blocks (they
 * don't land on `LLMMessage.thinkingBlocks`), and continues the agent
 * run. Same graceful-failure pattern as v2.11.6
 * `tools.discovery_failed`.
 *
 * Lives in `agent.*` domain (NOT `stream.*`) because parse failure is
 * a turn-level error concern — recovery happens at the agent loop
 * level, not at the SDK call level.
 *
 * **Anti-pattern (provider authors):** sanitize error messages before
 * throwing. NEVER include raw unparsed thinking content in the error
 * — the message ends up in audit logs and can leak reasoning content
 * the consumer expected to be redacted. Same guidance as
 * `tools.discovery_failed.error`.
 */
export interface AgentThinkingParseFailedPayload {
    readonly providerName: string;
    readonly subflowId: string;
    readonly error: string;
    readonly errorName: string;
    readonly iteration: number;
}
/** One completed-turn thinking block omitted before a provider request. */
export interface AgentThinkingOmittedPayload {
    readonly providerName: string;
    readonly model: string;
    readonly messageIndex: number;
    readonly blockIndex: number;
    readonly reason: import('../thinking/replay.js').ThinkingReplayReason;
    /** Preparation is observable even when the subsequent transport fails. */
    readonly phase: 'prepared';
}
/** A payload was checked in and the store minted its claim ticket — by a
 *  tool (`tool` names it), or by no tool at all (see {@link tool}). */
export interface ArtifactMintedPayload {
    readonly ref: string;
    /** Consumer vocabulary: 'dataset/rows', 'chart/spec', … */
    readonly kind: string;
    readonly mediaType: string;
    readonly bytes: number;
    readonly label?: string;
    /** `sha-256:<hex>` when the put asked for one. Metadata, never the key. */
    readonly digest?: string;
    /** Unix ms when the ref stops resolving — stated at mint, never sprung. */
    readonly expiresAt?: number;
    /** The join to the trace: the run and tool call that minted it. */
    readonly origin?: ArtifactOrigin;
    /** Derivation facts — validated at mint, so they cannot dangle at birth. */
    readonly parentRefs?: readonly string[];
    /** The rows' declared time axis — validated at mint (see `ArtifactMeta.timeAxis`). */
    readonly timeAxis?: DatasetTimeAxis;
    /** The tool whose execute minted it. ABSENT when no tool did — the run's own
     *  recording (9.26.0, the `recordings` dial) or a host filing for its turn
     *  through `HostReply.turnArtifacts`; the event's `meta.sessionId` then names
     *  the session the host filed for. Typed as always-present until now, and
     *  already absent at run time since 9.26.0 — the 9.23.0 `resolved` /
     *  `refused` precedent. Naming a phantom tool would be an actor a dashboard
     *  groups by that does not exist. */
    readonly tool?: string;
}
/** A ref was redeemed — described (`head`) or read (`get`) — under scope. */
export interface ArtifactResolvedPayload {
    readonly ref: string;
    /** `head` is the render-by-ref decision; `get` pays for the payload. */
    readonly via: 'head' | 'get';
    readonly kind: string;
    readonly bytes: number;
    /** The tool that redeemed it. ABSENT when the redemption came through the
     *  hosting door instead of a tool (9.23.0) — a screen resolving
     *  `artifact-head` / `artifact-get` over the wire, under the requesting
     *  session's identity. Stamping a phantom tool name there would be a
     *  dashboard grouping by an actor that does not exist. */
    readonly tool?: string;
}
/** An artifact left the store without its owner asking — the calendar (ttl)
 *  or a budget (max-bytes / max-count). Reported by the put that swept it. */
export interface ArtifactExpiredPayload {
    readonly ref: string;
    /** One vocabulary, defined in `artifacts/` and imported here — the
     *  PermissionCapability lesson: two copies of one union can drift. */
    readonly reason: ArtifactSweepReason;
    readonly kind: string;
    readonly bytes: number;
    /** The tool whose put discovered/forced the sweep. ABSENT when the put was
     *  not a tool's — the run's own recording, or a host filing for its turn
     *  (the {@link ArtifactMintedPayload.tool} rule). */
    readonly tool?: string;
}
/** An artifact verb refused — or answered "no data" — and said why. `no-store`
 *  is the fail-closed capability teaching how to attach a store;
 *  `missing-or-expired` keeps the API's deliberate ambiguity while the record
 *  still shows a resolve that found nothing; `digest-mismatch` is integrity
 *  refusing to deliver corrupt bytes as whole. Since 9.22.0, `op: 'dispatch'`
 *  is the framework's own door: a tool's declared `wants` argument (or a
 *  `present` call) that could not be delivered — the tool did not run and the
 *  model read a teaching refusal listing what CAN resolve
 *  (`kind-mismatch` names a ref that resolved to the wrong kind). */
export interface ArtifactRefusedPayload {
    readonly op: ArtifactOp;
    readonly reason: ArtifactRefusalReason;
    readonly ref?: string;
    /** The refusal sentence, when one was thrown. */
    readonly detail?: string;
    /** The tool whose call was refused. ABSENT when the refusal answered the
     *  hosting door instead of a tool (9.23.0) — a wire resolution that found
     *  nothing (`missing-or-expired`: the one indistinguishable answer for
     *  missing, expired and another-session's alike) or reached an agent with
     *  no store (`no-store`). */
    readonly tool?: string;
}
/**
 * Why a turn's artifact hand-over (`HostReply.turnArtifacts`) did not go
 * cleanly — one vocabulary, shared by this event and the ingress record's
 * `turnArtifactsFailure`:
 *
 *  - `'hook'`      — the host's hook threw or rejected (and not merely by
 *                    re-throwing an operation's own failure, which is counted
 *                    once, as `'operation'`);
 *  - `'operation'` — a verb started through the hand-over failed, awaited or
 *                    not: a store refusal, an I/O error;
 *  - `'timeout'`   — the hook and the operations it started outran the ceiling
 *                    (`turnArtifactsTimeoutMs`); the binding was revoked and the
 *                    reply delivered, and what was in flight may still land;
 *  - `'abort'`     — the request's own signal fired (the caller hung up) before
 *                    they settled; the same consequences;
 *  - `'expired'`   — a verb was called after its turn ended and was refused.
 */
export type ArtifactHandOverFailureCause = 'hook' | 'operation' | 'timeout' | 'abort' | 'expired';
/**
 * A turn's artifact hand-over failed (the host's own filing — a story, the
 * person's clicks). The reply was delivered regardless: the hand-over never
 * decides it. Emitted on the serving agent for the session it was produced
 * for (`meta.sessionId`, and `meta.runId` = the turn's run when it had one).
 *
 * Class only, never the message: the error came from the host's own code or
 * its store, and either may carry anything — META ONLY, like every
 * `artifacts.*` payload. `errorClass` is routable without parsing prose (the
 * `tools.session_close_failed` precedent).
 */
export interface ArtifactHandOverFailedPayload {
    readonly cause: ArtifactHandOverFailureCause;
    /** The verb, for `'operation'` and `'expired'`. */
    readonly op?: ArtifactOp;
    /** Constructor name of what was thrown, when it was an `Error`. */
    readonly errorClass?: string;
    /** This package's (or the store's) own `code`, when the error carried one. */
    readonly errorCode?: string;
}
/** The model handed an artifact to the screen (9.22.0): `present({ ref, as,
 *  label? })` resolved under the run's scope. `snapshot` is the description
 *  the claim ticket carries at speak time — `{ kind, mediaType, bytes,
 *  label }` — the same snapshot stored INSIDE the tool result so a reloaded
 *  conversation can render an honest placeholder after the artifact expired.
 *  `as` is the model's consumer vocabulary ('bar-chart', 'table', …), stored
 *  as data — the component registry that would validate it is a later phase.
 *  Meta only, never the payload: the screen redeems the ref itself, under
 *  its own identity. */
export interface ArtifactPresentedPayload {
    readonly ref: string;
    readonly as: string;
    readonly snapshot: {
        readonly kind: string;
        readonly mediaType: string;
        readonly bytes: number;
        readonly label?: string;
    };
    /** The presenting call — the join to the trace. */
    readonly toolCallId: string;
    readonly iteration: number;
}
/** A mounted map's contributions are riding the calls again (or for the
 *  first time). `by` is the strength of the evidence the standing rests on
 *  — `assumed`/`lexical`/`semantic` engagements decay without corroboration;
 *  `structural`/`explicit` ones stand. `reengaged` marks a recovery from a
 *  park; `upgraded` marks a guess that real evidence confirmed. The map's own
 *  cursor is never touched by any of this: engagement is the kernel's axis,
 *  position is the map's. */
export interface MapEngagedPayload {
    readonly mapId: string;
    readonly iteration: number;
    readonly by: 'explicit' | 'structural' | 'semantic' | 'lexical' | 'assumed';
    /** The matched text that founded a guessed engagement (bounded upstream). */
    readonly witness?: string;
    readonly reengaged?: true;
    /** The standing was UPGRADED in place by corroborating evidence (9.59.0).
     *  Emitted so the record shows the moment a guess became a fact instead of
     *  silently rewriting `by` and leaving a reviewer to believe it was always
     *  that strong. */
    readonly upgraded?: true;
    /** On an `upgraded` or `tenantChanged` change: the cause the engagement was
     *  FOUNDED on, which never changes for the life of the record. */
    readonly foundedBy?: 'explicit' | 'structural' | 'semantic' | 'lexical' | 'assumed';
    /** The CURSOR moved to a different member of an already-engaged map (9.59.0)
     *  — a TENANT CHANGE. Eligibility was re-derived from the new member's own
     *  evidence rather than inherited from the old one's: a declared edge out of
     *  an explicitly-requested skill does not hand the next skill a user request
     *  it never received. `by` is the new member's strength; `foundedBy` still
     *  names how the engagement began. */
    readonly tenantChanged?: true;
    /** The member the cursor moved to, on a `tenantChanged` change. */
    readonly at?: string;
}
/** A mounted map was PARKED: its contribution was served for `idleCalls`
 *  consecutive passes in which none of its tools was called while the turn
 *  went elsewhere — the measured shape of the recorded 30-call stuck turn,
 *  and all three of those clauses are checked (9.59.0). Its prompt and tools
 *  stop riding; its cursor stays exactly where the map put it; explicit or
 *  structural evidence — including the model picking one of its skills by
 *  name — re-engages it. */
export interface MapParkedPayload {
    readonly mapId: string;
    readonly iteration: number;
    /** Strength of the evidence the parked engagement had rested on. */
    readonly by: 'explicit' | 'structural' | 'semantic' | 'lexical' | 'assumed';
    readonly idleCalls: number;
    readonly witness?: string;
}
/**
 * One detected context error, filed as a finding: two channels asserted
 * things that cannot both be true, an argument nothing served, an offered
 * action whose inputs left scope, a settled action re-executed, or a claim
 * the record does not support. The payload IS the finding — uniform across
 * seams so findings are a corpus; deduplicated by identity upstream, so
 * one defect is one event however many passes re-detect it. Synthetic
 * (canary) findings carry `synthetic` and are quarantined from real counts.
 */
export type IntegrityContextErrorPayload = import('../integrity/finding/types.js').ContextError & {
    /** The iteration the finding was filed on, when filed mid-loop. */
    readonly iteration?: number;
};
/**
 * One run's disposition accounting — one row per registered (check, seam),
 * filed ONCE at the run boundary. The rows answer the question a findings
 * stream cannot: did the checkers RUN? `checked` with zero `findings` is
 * health; a registered row with zero encounters while `workExisted` is the
 * wiring-rot signal `assertAlive` names in dev posture. Synthetic (canary)
 * counts ride each row quarantined from the real numbers.
 */
export interface IntegrityDispositionPayload {
    readonly posture: 'observe' | 'dev';
    /** Whether the run did any work a checker could have seen (≥1 LLM call). */
    readonly workExisted: boolean;
    readonly rows: readonly import('../integrity/disposition/types.js').CheckReport[];
}
/**
 * One argument value excused by an APP-ASSERTED external ground (9.72.0) —
 * the audit trail of the `externalGrounds` door. The choice-seam check found
 * the value in none of what the run served; the app's provider vouched for
 * it, so no finding was filed, and THIS event is the record of that excusal.
 * `source` is the app's own label ('viewer-selection'): the library records
 * what the app asserts — verifying the assertion was the app's duty at the
 * moment it yielded the entry. Fired per excused value per call, deliberately
 * NOT deduplicated the way findings are: a repeated call is a fresh choice.
 */
export interface IntegrityExternalGroundUsedPayload {
    readonly toolName: string;
    /** The provider's id for the call — what a reader joins to `tool_start`. */
    readonly toolCallId: string;
    /** Dot-path of the argument leaf the ground excused. */
    readonly path: string;
    readonly value: string;
    /** The app's label for where the verified value came from. */
    readonly source: string;
    readonly iteration: number;
}
/** One library-written negative statement, shared by delivery and its event. */
export interface DeliveredNegative {
    readonly kind: 'none' | 'not-found' | 'complete' | 'unknown';
    /** The single public population value derived from admitted searches, never model wording.
     * Omitted for multiple or unknown subjects; their individual scopes remain in the statement. */
    readonly about?: string | number;
    readonly restsOn: readonly string[];
    readonly statement: string;
    readonly delivery: 'record-composed';
    readonly version: string;
}
/** Served negative statements. Codes and identities are diagnostics; the statement is library-written. */
export interface AnswerNegativesPayload {
    readonly field: string;
    readonly version: string;
    readonly delivery: 'as-checked' | 'recorded' | 'fallback';
    readonly claims: readonly DeliveredNegative[];
    readonly checks: readonly {
        readonly id: string;
        readonly path: string;
        readonly index: number;
        readonly kind: 'none' | 'not-found' | 'complete' | 'unknown';
        /** Audit-only: none/not-found was normalized over an explicitly declared absence. */
        readonly notes?: readonly 'absence-kind-normalized'[];
        readonly code: string;
        readonly disposition: 'checked-fail' | 'not-applicable' | 'unreachable';
        readonly tools: readonly string[];
        readonly results: readonly string[];
        /** On a `'stale-turn'` check: the turn the cited result was read on, and when
         *  the read ran (the library's own clock), when it was recorded. */
        readonly staleSince?: {
            readonly turn: number;
            readonly readAt?: string;
        };
    }[];
    readonly coverage: readonly {
        readonly tool: string;
        readonly checked: number;
    }[];
    readonly actions: readonly {
        readonly tool: string;
        readonly outcome: 'ok' | 'error' | 'not-run';
    }[];
}
export {};
