/**
 * recordingEnvelope — the versioned, serialized contract around a recording.
 *
 * `recordRun` already freezes a run into `{ events, snapshot, structure }`, and
 * that shape is exactly right for handing to a viewer in the same process. What
 * it is NOT is a thing you can put on disk and read back next quarter: it
 * carries no format marker, no producer version, no statement of which run it
 * is, and no statement of whether it is the WHOLE run. So every consumer that
 * wanted to archive one, attach one to a bug report, or feed one to an analysis
 * tool invented its own wrapper — and each wrapper made a different guess about
 * the same missing facts.
 *
 * This is the producer-owned answer: one envelope, one format string, and every
 * field either a fact the library can prove or a fact the caller stated.
 *
 *   import { persistRecording, fileRecordingSink } from 'agentfootprint/observe';
 *
 *   const recorder = recordRun(agent);
 *   await agent.run({ message });
 *   await persistRecording(recorder, {
 *     sink: fileRecordingSink({ directory: './run-archive' }),
 *     run: { complete: true },
 *   });
 *
 * ## The rule this file exists to keep: never stamp a fact you had to guess
 *
 * An envelope is read by people and tools that were not there when the run
 * happened. That makes every field a claim, and a claim that turns out to be a
 * guess is worse than a missing field — a missing field sends the reader to
 * look, a wrong field stops them looking. So each one has a stated source:
 *
 *   runId, sessionId,      derived from the EVENT META, which is the only
 *   principal, tenant      place these are recorded, or stated by the caller.
 *                          Never synthesized. See "identity" below.
 *   startedAt / endedAt    derived from event wall clocks, but only where the
 *                          stream can honestly supply them (see below).
 *   complete               CALLER INPUT, always. Nothing in a finished
 *                          recording says whether the run reached its end.
 *   droppedEvents          read off the live recorder, which counts them; a
 *                          bare recording carries no count, so it is asked for
 *                          rather than assumed to be 0.
 *   configuration          lifted from the run's own `run_configured` event —
 *                          the manifest, which is names-and-ids only by law.
 *   producer               read from the package manifests at runtime.
 *
 * Where a fact is neither derivable nor supplied, this REFUSES
 * ({@link IndeterminateRunFactError}) instead of filling in a plausible value.
 *
 * ## Identity is never invented
 *
 * `principal` and `tenant` come from `EventMeta`, and `EventMeta`'s own law
 * (see src/events/types.ts) is that they are stamped ONLY from an explicit
 * `run(input, { identity })` — never from the run's internal identity, and
 * never from a session id, because "a conversation id is not an actor". By
 * sourcing them from the meta and nowhere else, this envelope inherits that
 * guarantee whole: an anonymous run produces an envelope with no `principal`
 * key at all, not a placeholder and not a session id wearing an actor's name.
 *
 * ## `runId` has two namespaces, and this one is deliberate
 *
 * A footprintjs snapshot carries its OWN engine-level `runId`
 * (`<timestamp>-<padded counter>`, minted per `executor.run()`), while the
 * agentfootprint events carry `run-<timestamp>-<seq>` from `makeRunId()`. They
 * are different ids for different layers and they do not match. This envelope
 * uses the EVENT-meta one exclusively, because that is the id `sessionId`,
 * `principal` and `tenant` ride beside and the one every typed-event consumer
 * correlates on. When the events cannot supply it, this refuses rather than
 * falling back to the snapshot's — quietly substituting an id from another
 * namespace would make two envelopes look comparable when they are not.
 */
import type { Recording, RunRecorder } from './recordRun.js';
import { type RunManifestLike } from './recordedManifest.js';
/**
 * The format marker. Bumped only for a change an older reader could not
 * survive — a reader that does not recognise the string must refuse the file,
 * never half-read it.
 */
export declare const RECORDING_ENVELOPE_FORMAT = "agentfootprint.recording.v1";
/**
 * The privacy policy id a `'full'` envelope carries when the caller names none.
 * A stable string so a retention rule can match on it rather than on prose.
 */
export declare const FULL_PRIVACY_POLICY_ID = "agentfootprint.privacy.full.v1";
/**
 * What a caller may ASK for. Only `'full'` is implemented in v1; the other two
 * are named here so the refusal can name them back, and so a consumer writing
 * against a future version gets a compile-time hint rather than a typo.
 */
export type RecordingPrivacyMode = 'full' | 'structure-only' | 'redacted';
/** Versions of the two libraries that produced the bytes. */
export interface RecordingProducer {
    /** This library's version, or `'unknown'` when the manifest is unreadable. */
    readonly agentfootprintVersion: string;
    /** The engine underneath it, or `'unknown'`. */
    readonly footprintjsVersion: string;
}
/**
 * WHICH run this is, and how much of it this is.
 *
 * Every field is either derived from the recording's own events or stated by
 * the caller — see the module header for the per-field source.
 */
export interface RecordingRun {
    /** From `EventMeta.runId` — the agentfootprint run id, not the engine's. */
    readonly runId: string;
    /** Present only when the run was session-bound. Never invented. */
    readonly sessionId?: string;
    /** The actor the caller NAMED. Absent for an anonymous run. */
    readonly principal?: string;
    /** The tenant boundary the caller NAMED. Absent for a single-tenant run. */
    readonly tenant?: string;
    /** ISO 8601. The first recorded event's wall clock, unless the caller said. */
    readonly startedAt: string;
    /**
     * ISO 8601. Absent when the recording is not `complete` and the caller named
     * no end — a run that had not finished has no end time to report.
     */
    readonly endedAt?: string;
    /**
     * Did this recording capture the run through to its end?
     *
     * ALWAYS the caller's statement. A frozen recording looks identical whether
     * it was taken after the run or from a crash handler mid-run, and there is no
     * run-terminal event in the registry to check, so the library cannot know.
     * Defaulting it to `true` would make every crash dump claim to be whole.
     */
    readonly complete: boolean;
    /**
     * Events discarded to stay under the recorder's `maxEvents` cap. `0` means
     * "none were dropped", proven — never "we did not look".
     */
    readonly droppedEvents: number;
    /**
     * The ORIGINAL stream position of the first retained event (9.60.0) — the
     * envelope's events are stream positions
     * `[firstRetainedEventIndex, firstRetainedEventIndex + events.length)`.
     * A drop COUNT says how much is gone; this says WHERE the kept window
     * starts, which is what lets a reader align this tail against another
     * record of the same run. Present exactly when the source could prove it
     * (the live recordRun handle, or the caller's own statement beside
     * `droppedEvents`); absent means "not proven", never 0-by-assumption.
     */
    readonly firstRetainedEventIndex?: number;
}
/** What the agent was configured as — names and ids only, no values. */
export interface RecordingConfiguration {
    readonly agentId?: string;
    /**
     * The run manifest, as the run itself declared it in
     * `agentfootprint.agent.run_configured`.
     *
     * Safe to archive by construction: the manifest's own law is NAMES AND IDS
     * ONLY — no endpoints, directories, connection strings or keys — precisely
     * because it was built to ride into recordings and shared traces.
     */
    readonly manifest?: RunManifestLike;
}
/** What was done to the bytes before they were stored. */
export interface RecordingPrivacy {
    /**
     * `'full'` — the recording is exactly what `recordRun` captured, unredacted.
     *
     * A v1 envelope can say nothing else: the redacting modes are not built, and
     * this refuses to produce a label it cannot honour.
     */
    readonly mode: 'full';
    /** Names the policy the producer applied. See {@link FULL_PRIVACY_POLICY_ID}. */
    readonly policyId: string;
}
/**
 * One archived run: the recording, plus everything a reader needs to know what
 * they are holding.
 *
 * Plain JSON by construction — no Dates, no Maps, no live handles — so
 * `JSON.parse(JSON.stringify(envelope))` is the same envelope. Absent optional
 * fields are absent KEYS rather than keys with `undefined`, which is what makes
 * that round trip exact.
 */
export interface RecordingEnvelope {
    readonly format: typeof RECORDING_ENVELOPE_FORMAT;
    readonly producer: RecordingProducer;
    readonly run: RecordingRun;
    readonly configuration?: RecordingConfiguration;
    readonly privacy: RecordingPrivacy;
    /** The recording itself, unmodified — `{ snapshot, events, structure }`. */
    readonly recording: Recording;
}
/** A timestamp a caller may state, in any of the three obvious spellings. */
export type RecordingTimestamp = string | number | Date;
/** The run facts a caller states — the half the library cannot derive. */
export interface RecordingRunFacts {
    /**
     * Did the recording capture the run through to its end?
     *
     * Required, and deliberately so: this is the one field with no derivation and
     * no safe default, so the API asks rather than guesses. Say `false` for a
     * recording frozen from a crash handler, a timeout, or mid-stream.
     */
    readonly complete: boolean;
    /** Override the event-derived run id, or supply it when events cannot. */
    readonly runId?: string;
    readonly sessionId?: string;
    readonly principal?: string;
    readonly tenant?: string;
    readonly startedAt?: RecordingTimestamp;
    readonly endedAt?: RecordingTimestamp;
    /**
     * Events lost to the recorder's cap. Read off a live `recordRun` handle
     * automatically; state it when persisting a bare `Recording`, whose shape
     * carries no count.
     */
    readonly droppedEvents?: number;
    /**
     * Where the kept event window starts in the original stream. Read off a
     * live handle automatically; state it only when persisting a bare
     * `Recording` AND you can prove it. Refused if negative or fractional.
     */
    readonly firstRetainedEventIndex?: number;
}
/**
 * Where an envelope goes. One method, so a destination is a few lines: a
 * directory, an object store, a table, an HTTP endpoint.
 */
export interface RecordingSink {
    /**
     * Store one envelope.
     *
     * @returns `id`   the sink's own handle for what it just stored.
     *          `uri`  where it landed, when the sink has a meaningful address.
     */
    write(envelope: RecordingEnvelope): Promise<{
        id: string;
        uri?: string;
    }>;
}
/** Options for {@link persistRecording}. */
export interface PersistRecordingOptions {
    readonly sink: RecordingSink;
    /**
     * The run facts. Required because {@link RecordingRunFacts.complete} is:
     * an envelope that did not state whether it holds a whole run is an envelope
     * whose reader has to guess.
     */
    readonly run: RecordingRunFacts;
    /** Override the manifest lifted from the run's own `run_configured` event. */
    readonly configuration?: RecordingConfiguration;
    readonly privacy?: {
        readonly mode?: RecordingPrivacyMode;
        readonly policyId?: string;
    };
}
/** Options for {@link buildRecordingEnvelope}. */
export type BuildRecordingEnvelopeOptions = Omit<PersistRecordingOptions, 'sink'>;
/** A recording to envelope, or the live handle that is still collecting one. */
export type RecordingSource = Recording | RunRecorder;
/**
 * Raised when a privacy mode is asked for that this version cannot honour.
 *
 * Its own class because the answer is never "retry": a caller catching this has
 * to change what they store or how they store it.
 */
export declare class UnsupportedPrivacyModeError extends Error {
    readonly code: "ERR_UNSUPPORTED_PRIVACY_MODE";
    readonly mode: string;
    constructor(mode: string);
}
/**
 * Raised when a required run fact is neither derivable from the recording nor
 * stated by the caller.
 *
 * Carries the `field` so a caller can catch it and supply exactly that one.
 */
export declare class IndeterminateRunFactError extends Error {
    readonly code: "ERR_INDETERMINATE_RUN_FACT";
    readonly field: string;
    constructor(field: string, detail: string);
}
/**
 * Freeze a recording into an archivable envelope, without storing it.
 *
 * The half of {@link persistRecording} that has no destination — useful when
 * the envelope goes somewhere this library should not know about (a request
 * body, a queue), and the unit under test for the contract itself.
 */
export declare function buildRecordingEnvelope(source: RecordingSource, options: BuildRecordingEnvelopeOptions): RecordingEnvelope;
/**
 * Build the envelope and hand it to a sink.
 *
 * @param source   the handle from `recordRun(agent)`, or the recording it made.
 *                 Prefer the handle: it is the only thing that knows how many
 *                 events the cap discarded.
 * @param options  the destination, the run facts, and the privacy statement.
 *
 * @example
 * ```ts
 * const recorder = recordRun(agent);
 * await agent.run({ message: 'Weather in San Francisco?' });
 *
 * const { uri } = await persistRecording(recorder, {
 *   sink: fileRecordingSink({ directory: './run-archive' }),
 *   run: { complete: true },
 * });
 * recorder.stop();
 * ```
 */
export declare function persistRecording(source: RecordingSource, options: PersistRecordingOptions): Promise<{
    id: string;
    uri?: string;
}>;
