UNPKG

agentfootprint

Version:

The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.

142 lines • 6.94 kB
"use strict"; /** * artifacts/recordingArtifact — a finished run, checked into the store like any * other payload. * * `recordRun` has produced `{ snapshot, events, structure }` since 8.x and the * lens family has consumed exactly that shape since. What nobody shipped was * the boring half in between: WHERE a completed run's recording goes so a * screen can ask for it later. Every deployment that wanted the Lens over the * wire wrote the same twenty lines — record the run, serialize it, put it * somewhere with a key, hand the key to the frontend — and each one wrote a * slightly different, slightly wrong version of retention and scoping. * * All four of those problems already have an answer in this package: the * artifact store IS a keyed payload store with scope isolation, retention and * a read-only wire. So a recording is simply an artifact of kind * `'recording/run'`, and the wire ops that already redeem claim tickets serve * it with **zero new operations** — `{ op: 'artifact-get', ref }` returns the * recording, and the frontend does what it always did with the bytes. * * ── Why the payload is TEXT and not an object ─────────────────────────────── * The recording is minted as its JSON text — the same bytes * `JSON.stringify(recorder.toRecording())` produces, which is precisely what * `observeRecording(JSON.parse(text))` consumes on the viewer side. Two * reasons, and both are about not lying: * * • `recordRun` states that `snapshot` and `structure` are the runner's OWN * objects, held by reference. An in-process store handed those would keep * a live view into a finished run's state — the artifact would silently * change if anything touched it. Serializing detaches it, once, here. * • A recording that cannot be serialized cannot be delivered over any wire * either, so failing at the mint is failing at the honest moment rather * than at whatever reads it next. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.chartWalkPutInput = exports.recordingPutInput = exports.UnserializableRecordingError = exports.RECORDING_MEDIA_TYPE = exports.CHART_WALK_ARTIFACT_KIND = exports.RECORDING_ARTIFACT_KIND = void 0; const wireJson_js_1 = require("../lib/wireJson.js"); /** The consumer vocabulary a run recording is stored under. One kind, exact * match — the same rule every `wants` declaration is judged by. */ exports.RECORDING_ARTIFACT_KIND = 'recording/run'; /** * The consumer vocabulary a chart WALK is stored under (9.76.0) — one row per * execution step of a runbook's inner chart, with the decider evidence * sentences in the `condition` rows. Namespaced by what it IS (`recording/…`, * beside `recording/run`), never by what produced it: a walk is a recording * projection, not a dataset that happens to mention stages. */ exports.CHART_WALK_ARTIFACT_KIND = 'recording/chart-walk'; /** The media type a recording is minted with. */ exports.RECORDING_MEDIA_TYPE = 'application/json'; /** * Raised when a recording could not be turned into bytes. * * Its own class because the CALLER's answer differs from every other mint * failure: a full store is retryable and a cyclic snapshot is not. */ class UnserializableRecordingError extends Error { code = 'ERR_UNSERIALIZABLE_RECORDING'; constructor(detail) { super(`[artifacts] the run recording could not be serialized (${detail}), so it was not ` + `stored. A recording that JSON cannot carry could not be delivered over any wire ` + `either — this fails at the mint rather than at whatever tried to read it. The run ` + `itself is unaffected: its answer was already composed before this was attempted.`); this.name = 'UnserializableRecordingError'; } } exports.UnserializableRecordingError = UnserializableRecordingError; /** * Turn one finished recording into the `put` input that stores it. * * Pure: no store, no events, no agent. The caller owns WHEN this happens (after * the answer is composed) and what to do when it fails. * * @throws UnserializableRecordingError when the recording cannot be * JSON-serialized — a cyclic object in a snapshot, most likely. */ function recordingPutInput(recording, facts = {}) { let text; try { // The wire rule (`lib/wireJson.ts`): the recording holds the RAW events, and // a stored recording is redeemable — an Error in one is written by name, // message, code and cause only. text = (0, wireJson_js_1.toWireJson)(recording) ?? ''; } catch (err) { throw new UnserializableRecordingError(err instanceof Error ? err.message : String(err)); } if (text === '') { throw new UnserializableRecordingError('it serializes to nothing'); } const origin = { ...(facts.runId !== undefined && { runId: facts.runId }), ...(facts.toolCallId !== undefined && { toolCallId: facts.toolCallId }), }; return { kind: exports.RECORDING_ARTIFACT_KIND, mediaType: exports.RECORDING_MEDIA_TYPE, data: text, label: facts.label ?? (facts.runId !== undefined ? `run ${facts.runId}` : 'run recording'), ...(Object.keys(origin).length > 0 && { origin }), }; } exports.recordingPutInput = recordingPutInput; /** * Turn one chart walk into the `put` input that stores it under * {@link CHART_WALK_ARTIFACT_KIND}. * * Pure, and serialized to JSON TEXT at the mint for exactly the reasons the * run recording is (see the file header): a walk row must never be a live * view into engine memory, and a walk JSON cannot carry could not cross any * wire either. * * @throws UnserializableRecordingError when the rows cannot be * JSON-serialized. Walk rows are projected to plain data upstream, so this * firing means the projection let a live value through — fail at the mint, * loudly. */ function chartWalkPutInput(rows, facts = {}) { let text; try { text = (0, wireJson_js_1.toWireJson)(rows) ?? ''; } catch (err) { throw new UnserializableRecordingError(err instanceof Error ? err.message : String(err)); } if (text === '') { throw new UnserializableRecordingError('it serializes to nothing'); } const origin = { ...(facts.runId !== undefined && { runId: facts.runId }), ...(facts.toolCallId !== undefined && { toolCallId: facts.toolCallId }), }; return { kind: exports.CHART_WALK_ARTIFACT_KIND, mediaType: exports.RECORDING_MEDIA_TYPE, data: text, label: facts.label ?? (facts.toolName !== undefined ? `${facts.toolName} walk` : 'chart walk'), ...(Object.keys(origin).length > 0 && { origin }), }; } exports.chartWalkPutInput = chartWalkPutInput; //# sourceMappingURL=recordingArtifact.js.map