agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
80 lines (79 loc) • 3.99 kB
TypeScript
/**
* stampConversation — put the bundle's evidence in the archive contract, or
* say in one sentence why it could not be.
*
* The repo has ONE archive contract (`RecordingEnvelope`) and several
* presentations over it. This bundle used to be an exception: it packed a bare
* `recording.json` plus an `environment.json` that repeated the producer facts
* the envelope already stamps, so the same two versions were written by two
* different pieces of code with no relationship between them. This is the fold
* — the envelope is BUILT here, never re-implemented, so every rule it enforces
* (identity is never invented, `droppedEvents` is proven or refused, an
* incomplete recording gets no `endedAt`) arrives whole.
*
* ## Why a refusal is a return value here, not a throw
*
* `persistRecording` throws when a run fact is indeterminate, and it is right
* to: its caller asked for an ARCHIVE, and an archive stamped with a guess is
* worse than none. A bug report's caller asked for something else — the
* evidence, in a zip, so a maintainer can open it. Throwing would leave that
* person with no bundle at all because the library could not name a start time.
*
* So the refusal travels instead of stopping the export: this returns the
* REASON, the bundle carries the bare recording under its own honest name, and
* the manifest states which fact was missing and the one line that supplies it.
* Nothing is stamped that was not known — which is the rule the throw exists to
* keep — and the report still gets filed.
*
* @internal Not a public export; `exportBugReport` is the door.
*/
import { type RecordingEnvelope, type RecordingSource } from '../recorders/observability/recordingEnvelope.js';
import type { BugReportRunFacts } from './types.js';
/**
* One recording, plus the one thing only the CALL SITE knows about it: whether
* the thing we were handed can count its own dropped events.
*/
export interface EnvelopeSource {
/** What `buildRecordingEnvelope` reads: a live handle, or a bare recording. */
readonly source: RecordingSource;
/**
* `true` for a live `recordRun` handle — it counts what the `maxEvents` cap
* discarded, so nothing the caller states may override it.
*/
readonly countsDrops: boolean;
}
/**
* The result for ONE conversation: every recording enveloped, or none of them
* and the reason.
*
* All-or-nothing on purpose. A conversation file holding two envelopes and one
* bare recording would need a reader to check each entry's shape before it
* could read any of them, and a file whose shape depends on its contents is the
* kind of archive this program exists to stop producing.
*/
export interface StampedConversation {
/** In the recordings' own order. Absent when {@link refusal} is present. */
readonly envelopes?: readonly RecordingEnvelope[];
/** One sentence naming the missing fact and how to supply it. */
readonly refusal?: string;
/**
* The envelope field that could not be determined — `'complete'` when the
* reporter stated no run facts at all.
*
* The caller needs it to know whether the fix is even available at this door:
* `complete` and `droppedEvents` are stated HERE, while `startedAt` and the
* identity fields are read from the recording's own events, so a refusal
* naming one of those has to be fixed at recording time.
*/
readonly field?: string;
}
/**
* Envelope every recording of one conversation.
*
* @param sources the conversation's recordings, in order, each with whether it
* can prove its own drop count.
* @param facts what the reporter stated. `undefined` — the default, since
* `run` is optional on both entry points — is itself a refusal
* reason, and a named one.
*/
export declare function stampConversation(sources: readonly EnvelopeSource[], facts: BugReportRunFacts | undefined): StampedConversation;