UNPKG

agentfootprint

Version:

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

156 lines • 7.18 kB
"use strict"; /** * artifacts/payload — one owner for how a payload is measured, carried and * digested. * * Three adapters store the same three payload shapes (string, `Uint8Array`, * JSON value). If each measured or serialized on its own, `bytes` on the meta * could disagree with what a digest was computed over, and a digest written by * one adapter would not verify in another. So the laws live once, here: * * • `measure` — `bytes` is the UTF-8 length of text/JSON, the byteLength of * binary. The number a consumer decides from, computed one way. * • `encode/decode` — the durable adapters persist ONE envelope * (`text` / `binary` base64 / `json`), so a file written by * `fileArtifacts` is legible to a debugging human and identical in law to * a row written by `sqliteArtifacts`. * • `digest` — sha-256 over the SAME canonical bytes `measure` counts, via * `globalThis.crypto.subtle` (browser + Node, zero dependencies). * * A payload JSON cannot carry (a function, a bigint, `undefined`, a cyclic * object) is refused BY NAME at put — storing an approximation and returning * it later as the real thing is the exact accepted-and-silently-wrong failure * a claim check exists to prevent. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.decodeArtifactData = exports.encodeArtifactData = exports.computeArtifactDigest = exports.decodeCanonicalPayload = exports.payloadShapeOf = exports.measureArtifactBytes = exports.canonicalPayloadBytes = void 0; const types_js_1 = require("./types.js"); const footprintjs_1 = require("footprintjs"); const textEncoder = new TextEncoder(); const textDecoder = new TextDecoder(); /** * The canonical bytes of a payload — what `bytes` counts and what a digest * is computed over. Throws {@link InvalidArtifactError} for a value that * cannot be carried faithfully. */ function canonicalPayloadBytes(data) { if (data instanceof Uint8Array) return data; if (typeof data === 'string') return textEncoder.encode(data); let serialized; try { // footprintjs's own encoder (9.26.0): the same bytes as JSON.stringify, // with its own stack — a recording of a long linear run nests one level // per stage and the engine's recursive encoder refused it. serialized = (0, footprintjs_1.stringifySnapshot)(data); } catch (err) { throw new types_js_1.InvalidArtifactError(`the payload cannot be JSON-serialized (${err instanceof Error ? err.message : String(err)}). ` + `Store a string, a Uint8Array, or a JSON-serializable value — an approximation of ` + `anything else would be returned later as if it were the real thing.`); } if (serialized === undefined) { throw new types_js_1.InvalidArtifactError(`the payload serializes to nothing (undefined, a function, or a symbol). ` + `JSON cannot carry it, so the store will not pretend to. Store a string, a ` + `Uint8Array, or a JSON-serializable value.`); } return textEncoder.encode(serialized); } exports.canonicalPayloadBytes = canonicalPayloadBytes; /** Payload size in bytes — UTF-8 for text/JSON, byteLength for binary. */ function measureArtifactBytes(data) { return canonicalPayloadBytes(data).byteLength; } exports.measureArtifactBytes = measureArtifactBytes; /** The shape a payload will be stored under. Total: everything that is not a * string or a `Uint8Array` rides as JSON (and is refused at measure/encode if * JSON cannot carry it). */ function payloadShapeOf(data) { if (data instanceof Uint8Array) return 'binary'; if (typeof data === 'string') return 'text'; return 'json'; } exports.payloadShapeOf = payloadShapeOf; /** * Rebuild a payload from its CANONICAL BYTES and its shape — the read half of * `canonicalPayloadBytes`, for stores that hold the bytes themselves (an * object body, a stream) rather than the text envelope. * * Round-trip law: `decodeCanonicalPayload(payloadShapeOf(x), * canonicalPayloadBytes(x))` equals `x` for every payload this library * accepts — which is what lets `bytes`, the digest, and the stored object all * be the same bytes in every adapter. */ function decodeCanonicalPayload(shape, bytes) { switch (shape) { case 'text': return textDecoder.decode(bytes); case 'binary': return bytes; case 'json': return JSON.parse(textDecoder.decode(bytes)); } } exports.decodeCanonicalPayload = decodeCanonicalPayload; /** `sha-256:<hex>` over the canonical bytes. */ async function computeArtifactDigest(data) { const bytes = canonicalPayloadBytes(data); // `subtle.digest` wants an ArrayBuffer-backed view; copy defensively so a // Uint8Array over a SharedArrayBuffer cannot reach it. const buffer = new Uint8Array(bytes).buffer; const hash = new Uint8Array(await globalThis.crypto.subtle.digest('SHA-256', buffer)); let hex = ''; for (const b of hash) hex += b.toString(16).padStart(2, '0'); return `sha-256:${hex}`; } exports.computeArtifactDigest = computeArtifactDigest; /** Encode for durable storage. Validates exactly as `measure` does. */ function encodeArtifactData(data) { if (data instanceof Uint8Array) return { shape: 'binary', value: toBase64(data) }; if (typeof data === 'string') return { shape: 'text', value: data }; // canonicalPayloadBytes carries the refusal for unserializable values. return { shape: 'json', value: textDecoder.decode(canonicalPayloadBytes(data)) }; } exports.encodeArtifactData = encodeArtifactData; /** Decode what {@link encodeArtifactData} wrote. Total over its own output; * an `'external'` envelope is the one thing it refuses, by name — the bytes * live beside the envelope and only the adapter that wrote them can reach * them. */ function decodeArtifactData(encoded) { switch (encoded.shape) { case 'text': return encoded.value; case 'binary': return fromBase64(encoded.value); case 'json': return JSON.parse(encoded.value); case 'external': throw new types_js_1.InvalidArtifactError(`the payload was streamed and lives beside the envelope ('${encoded.value}'), not in ` + `it. Only the store that wrote it can read it back — read this artifact through ` + `that store's get()/getStream(), never by decoding the envelope alone.`); } } exports.decodeArtifactData = decodeArtifactData; // Buffer-free base64, so the in-file law works in a browser bundle too. function toBase64(bytes) { let binary = ''; const CHUNK = 0x8000; for (let i = 0; i < bytes.length; i += CHUNK) { binary += String.fromCharCode(...bytes.subarray(i, i + CHUNK)); } return btoa(binary); } function fromBase64(value) { const binary = atob(value); const bytes = new Uint8Array(binary.length); for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i); return bytes; } //# sourceMappingURL=payload.js.map