/**
 * recordRun — save a run so a viewer can show it later.
 *
 * A RECORDING is exactly three things, and each one lights a different
 * surface:
 *
 *   `events`     the typed agentfootprint stream, in the order it fired —
 *                the story, the commentary, the summary.
 *   `snapshot`   the footprintjs run snapshot — shared state, the commit
 *                log, and every attached recorder's data (narrative,
 *                metrics, boundaries).
 *   `structure`  the agent's build-time chart — THE CHART. Nothing else
 *                can draw it, and no snapshot carries it.
 *
 * Miss one and one surface goes dark. That is why this exists: every
 * integration that assembled the bundle by hand assembled a different,
 * incomplete one, and the piece most often missing was `structure` —
 * because a completed run does not leave it behind. This is also the
 * producer for the shape lens's `observeRecording()` consumes, which
 * until now nothing produced.
 *
 *   import { recordRun } from 'agentfootprint/observe';
 *
 *   const recorder = recordRun(agent);
 *   await agent.run({ message });
 *   fs.writeFileSync('run.json', JSON.stringify(recorder.toRecording()));
 *
 * …and in the viewer:
 *
 *   const { recorder, runner } = observeRecording(JSON.parse(json));  // lens
 *   <Lens recorder={recorder} runner={runner} />
 *
 * WHAT IT WIRES THAT HAND-ROLLING GETS WRONG
 * ──────────────────────────────────────────
 * The boundary recorder — the one that puts stops on the step strip —
 * needs three connections, and each missing one fails differently:
 *
 *   attach          no boundaries at all (loud: the strip is empty)
 *   subscribe       boundaries with nothing in them (quiet: no LLM or
 *                   tool detail behind any stop)
 *   getCommitCount  every boundary stamped at commit 0 (SILENT: the
 *                   events look complete, and the strip cannot be
 *                   rebuilt offline — the commit log records what
 *                   stages wrote, never when a boundary was crossed,
 *                   so nothing downstream can repair it)
 *
 * Wire it before the run or not at all — none of the three can be
 * reconstructed from a finished run.
 *
 * WHAT IT DOES NOT DO
 * ───────────────────
 * It attaches no OTHER recorders by default. `narrative()` and `metrics()` are the
 * consumer's choice — their data rides the snapshot when attached, and
 * each viewer says so when it isn't there (a Gantt with no timings shows
 * execution ORDER and says so; a story panel falls back to "X executed.
 * Wrote: y"). Attaching them behind the consumer's back would make every
 * recording pay for panels most runs never open.
 * The explicit `trustBoundaries` option adds typed metadata capture, not an
 * engine attachment. Its bundle is overlaid only when exporting a recording.
 */
import type { Runner } from '../../core/runner.js';
import type { AgentfootprintEvent } from '../../events/registry.js';
import { type BoundaryRecorder } from './BoundaryRecorder.js';
import { type TrustBoundaryRecorder, type TrustBoundaryRecorderOptions } from '../../trust-boundaries/index.js';
/**
 * One frozen run — everything a viewer needs, and nothing it doesn't.
 *
 * The field names are the ones `observeRecording()` reads, so a recording
 * is handed over whole: no mapping, no adapter, no per-integration shape.
 */
export interface Recording {
    /** The footprintjs run snapshot. `undefined` if nothing has run yet. */
    readonly snapshot: unknown;
    /** The typed events the run fired, in order. */
    readonly events: readonly AgentfootprintEvent[];
    /** The agent's build-time chart — the only route to a drawable graph. */
    readonly structure: unknown;
}
export interface RecordRunOptions {
    /**
     * Cap on retained events. The oldest are dropped once the cap is hit,
     * so a long-running server records a bounded tail rather than growing
     * without limit. Default 10,000 — a few hundred KB for a typical turn,
     * and far more than any single turn fires.
     *
     * A recording that dropped events says so: `droppedEvents` counts them
     * rather than leaving a consumer to wonder why its timeline starts
     * mid-run.
     *
     * The cap counts DISTINCT events. Every model call re-announces each piece
     * of its context (`agentfootprint.context.injected`); an announcement equal,
     * field for field, to one the recording still holds is kept in its place but
     * holds no slot, and shares that one's payload. Counted, those repeats grow
     * with the square of the iteration count and evicted the start of a long run
     * (a 100-iteration run opened at iteration ~33). The tail stays bounded:
     * slots and repeats together are capped at ten times `maxEvents` (each
     * repeat is still a small envelope), past which the oldest go, as dropped.
     */
    readonly maxEvents?: number;
    /**
     * How much of the boundary log rides the snapshot:
     *   - `'full'` (default) — every field, content included. What an
     *     offline step strip needs to show detail behind each stop.
     *   - `'lean'` — BoundaryEvents structure only, no captured payloads. The
     *     strip still rebuilds; the detail panels are empty, and a
     *     consumer reading the bundle's `meta.mode` can say so.
     * This changes only the BoundaryEvents bundle, not the raw event tail or
     * snapshot state. It is not a whole-recording redaction policy.
     */
    readonly boundaryDetail?: 'full' | 'lean';
    /**
     * Keep raw embedding vectors in the recording (8.20.0). Default `false`:
     * every `embedding` / `embeddings` field — in boundary payloads, in the
     * snapshot's subflow results, in tool results riding the event stream —
     * is replaced with its `{ dims, norm }` summary when the recording is
     * frozen.
     *
     * Field measurement behind the default: one retrieval turn's recording
     * weighed 2.76 MB, ~1.1 MB of it embedding floats, because the
     * memory-read subflow's boundary output carries each retrieved entry's
     * full vector. Nothing that renders a recording reads those floats; the
     * retrieval evidence a debugger needs (score, passage, document,
     * rejected candidates) carries no vectors and is untouched. Set `true`
     * only when a consumer genuinely replays raw vectors offline.
     */
    readonly recordEmbeddings?: boolean;
    /**
     * Capture a bounded, content-minimized view of selected middleware,
     * permission and credential events. Default off. The facts are collected
     * at typed dispatch, never recovered from the event tail or execution tree.
     *
     * The `TrustBoundaries` row rides `snapshot.recorders`; other recording
     * fields can still contain raw content. `stop()` pins the base snapshot
     * (as it does in every mode), so runner reuse cannot substitute another log.
     */
    readonly trustBoundaries?: boolean | TrustBoundaryRecorderOptions;
}
/** A recording in progress. Keep it until the run ends, then freeze it. */
export interface RunRecorder {
    /**
     * Freeze what has happened so far into `{ snapshot, events, structure }`.
     * Call it after the run; calling it during one gives a partial
     * recording, which is legitimate for a crash report but will show a
     * timeline that stops mid-run.
     *
     * The events array is a fresh copy, so a recording taken and stored
     * does not keep growing behind the caller's back. `snapshot` and
     * `structure` are the runner's own objects, held by reference —
     * serialize to detach.
     */
    toRecording(): Recording;
    /** The boundary recorder, for richer live queries (slot rows, ranges). */
    readonly boundary: BoundaryRecorder;
    /** Opt-in typed trust capture; its detached facts remain readable after stop. */
    readonly trustBoundaries?: TrustBoundaryRecorder;
    /** How many events have been captured — re-announced context included (see
     *  `maxEvents`). */
    readonly eventCount: number;
    /** Events discarded to stay under `maxEvents`. `0` on a normal turn. */
    readonly droppedEvents: number;
    /** Original stream position of the first retained event (9.60.0) — the
     *  captured events are stream positions
     *  `[firstRetainedEventIndex, firstRetainedEventIndex + eventCount)`. */
    readonly firstRetainedEventIndex: number;
    /**
     * Stop recording. The events already captured stay readable, so
     * `toRecording()` after `stop()` is the normal end of a run; call it
     * when the runner outlives the recording (a server that records one
     * turn out of many).
     *
     * Pins the current snapshot by reference — in every mode, with or without
     * `trustBoundaries` — because the timeline and the boundary log end here
     * too: a reused runner's later run is never paired with these events. It
     * pins even if the run is unfinished; it does not assert completion or
     * drain pending events. A failing snapshot read still attempts every
     * subscription's cleanup, and no later run's snapshot is substituted.
     */
    stop(): void;
}
/**
 * Start recording a runner. Call BEFORE `run()` — a recording is
 * collected as the run happens and cannot be reconstructed after it.
 *
 * @param runner   the Agent / composition / pattern to record.
 * @param options  event cap and boundary detail level.
 *
 * @example
 * ```ts
 * const recorder = recordRun(agent);
 * await agent.run({ message: 'Weather in San Francisco?' });
 * const recording = recorder.toRecording();
 * recorder.stop();
 *
 * fs.writeFileSync('run.json', JSON.stringify(recording));
 * ```
 */
export declare function recordRun(runner: Runner, options?: RecordRunOptions): RunRecorder;
/**
 * {@link recordRun} with a timeline that keeps only the events `keep` admits —
 * the Agent's own per-run recording (`artifacts: { recordings: true }`) passes
 * its run-membership rule (`bridge/eventMeta.ts · eventBelongsToRun`), so a
 * fact produced for ANOTHER session while this run is in flight on a shared
 * instance never enters it. The boundary log and the snapshot are untouched:
 * neither ever carried such a fact.
 *
 * @internal — the Agent's; not on any door. `recordRun` is the public producer
 * and records everything, as it always has. Optional typed capture is supplied
 * by that public factory; this coordinator never constructs another observer.
 */
export declare function recordRunWhere(runner: Runner, keep: (event: AgentfootprintEvent) => boolean, options?: Omit<RecordRunOptions, 'trustBoundaries'>, trust?: TrustBoundaryRecorder): RunRecorder;
