UNPKG

agentfootprint

Version:

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

211 lines • 10.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.appendEvidenceSnapshot = exports.readEvidenceSnapshot = exports.validateCoverage = exports.bindingRuleOf = void 0; /** Closed restored-data schemas; shared JSON/coverage/period owners do validation. */ const boundedJson_js_1 = require("../../lib/boundedJson.js"); const items_js_1 = require("../../semantics/coverage/items.js"); const period_js_1 = require("../../semantics/coverage/period.js"); const scope_js_1 = require("./scope.js"); const capture_js_1 = require("./capture.js"); const types_js_1 = require("./types.js"); // Only this owner's detached, deeply frozen outputs carry this ephemeral proof. // A frozen input, JSON restore or proxy of an output is NOT an admitted identity. const admittedSnapshots = new WeakMap(); const encoder = new TextEncoder(); const jsonBytes = (value) => encoder.encode(JSON.stringify(value)).byteLength; function rememberSnapshot(snapshot, bytes = jsonBytes(snapshot)) { admittedSnapshots.set(snapshot, Object.freeze({ bytes, emptyBytes: jsonBytes({ ...snapshot, results: [] }) })); return snapshot; } function bindingRuleOf(raw) { const rule = (0, boundedJson_js_1.ownData)(raw, ['id', 'version', 'toolName', 'scopeDeclaration', 'fact', 'coverage']); const fact = (0, boundedJson_js_1.ownData)(rule.fact, ['entity', 'field']); if (!(0, scope_js_1.boundedText)(rule.id) || !(0, scope_js_1.boundedText)(rule.version) || !(0, scope_js_1.boundedText)(rule.toolName) || !(0, scope_js_1.boundedText)(fact.field) || (Object.hasOwn(rule, 'coverage') && typeof rule.coverage !== 'function')) throw new Error('Invalid claim binding rule.'); const scopeDeclaration = (0, scope_js_1.scopeDeclarationOf)(rule.scopeDeclaration); let entity; if (typeof fact.entity === 'string') { if (!(0, scope_js_1.boundedText)(fact.entity)) throw new Error('Invalid fact entity.'); entity = fact.entity; } else { const selector = (0, boundedJson_js_1.ownData)(fact.entity, ['scopeDimension']); if (!(0, scope_js_1.boundedText)(selector.scopeDimension) || !scopeDeclaration.dimensions.some((dimension) => dimension.name === selector.scopeDimension)) throw new Error('Invalid entity scope selector.'); entity = Object.freeze({ scopeDimension: selector.scopeDimension }); } return Object.freeze({ id: rule.id, version: rule.version, toolName: rule.toolName, scopeDeclaration, fact: Object.freeze({ entity, field: fact.field }), ...(typeof rule.coverage === 'function' && { coverage: rule.coverage, }), }); } exports.bindingRuleOf = bindingRuleOf; /** Closed coverage admission shared by receipts and saved verification rows. */ function validateCoverage(raw) { const coverage = (0, boundedJson_js_1.ownData)(raw, ['checked', 'notChecked', 'cannotCover']); for (const section of ['checked', 'notChecked', 'cannotCover']) { const list = coverage[section]; if (!Array.isArray(list) || list.length > scope_js_1.BINDING_LIMITS.results) throw new Error('Invalid coverage list.'); for (const rawItem of list) { const item = (0, boundedJson_js_1.ownData)(rawItem, ['what', 'why', 'short', 'kind']); if (!(0, scope_js_1.boundedText)(item.what) || (Object.hasOwn(item, 'why') && !(0, scope_js_1.boundedText)(item.why))) throw new Error('Invalid coverage item.'); } // Ask the one existing rule owner, but never repair the saved shape. (0, items_js_1.normalizeCoverageList)(section, list, section === 'cannotCover'); } } exports.validateCoverage = validateCoverage; function resultOf(raw) { raw = (0, boundedJson_js_1.copyJson)(raw, scope_js_1.BINDING_LIMITS.resultBytes, true); const result = (0, boundedJson_js_1.ownData)(raw, [ 'identity', 'id', 'toolName', 'toolCallId', 'iteration', 'scopeDeclaration', 'scope', 'periodDimension', 'unavailable', 'facts', 'coverage', 'period', ]); const identity = (0, boundedJson_js_1.ownData)(result.identity, ['namespace', 'runId', 'ordinal']); const declaration = (0, boundedJson_js_1.ownData)(result.scopeDeclaration, ['id', 'version']); if (!(0, scope_js_1.boundedText)(identity.namespace) || !(0, scope_js_1.boundedText)(identity.runId) || typeof identity.ordinal !== 'number' || !Number.isSafeInteger(identity.ordinal) || identity.ordinal < 1 || !(0, scope_js_1.boundedResultId)(result.id) || result.id !== (0, capture_js_1.resultIdentityOf)({ namespace: identity.namespace, runId: identity.runId, ordinal: identity.ordinal, }) || !(0, scope_js_1.boundedText)(result.toolName) || !(0, scope_js_1.boundedText)(result.toolCallId) || typeof result.iteration !== 'number' || !Number.isSafeInteger(result.iteration) || result.iteration < 0 || !(0, scope_js_1.boundedText)(declaration.id) || !(0, scope_js_1.boundedText)(declaration.version)) throw new Error('Invalid result receipt identity.'); if (Object.hasOwn(result, 'unavailable')) { if (typeof result.unavailable !== 'string' || !types_js_1.BINDING_REASONS.includes(result.unavailable) || ['scope', 'periodDimension', 'facts', 'coverage', 'period'].some((key) => Object.hasOwn(result, key))) throw new Error('Invalid unavailable receipt.'); } else { const scope = (0, boundedJson_js_1.ownData)((0, boundedJson_js_1.copyJson)(result.scope, scope_js_1.BINDING_LIMITS.scopeBytes, true)); if (Object.keys(scope).length === 0 || Object.keys(scope).length > scope_js_1.BINDING_LIMITS.dimensions || !Object.keys(scope).every(scope_js_1.boundedText) || !Array.isArray(result.facts) || result.facts.length > scope_js_1.BINDING_LIMITS.results) throw new Error('Invalid receipt scope or facts.'); if (Object.hasOwn(result, 'periodDimension') && !(0, scope_js_1.boundedText)(result.periodDimension)) throw new Error('Invalid period dimension.'); for (const rawFact of result.facts) { const fact = (0, boundedJson_js_1.ownData)(rawFact, [ 'entity', 'field', 'value', 'toolName', 'toolCallId', 'iteration', 'measuredAt', ]); if (!(0, scope_js_1.boundedText)(fact.entity) || !(0, scope_js_1.boundedText)(fact.field) || !Object.hasOwn(fact, 'value') || fact.toolName !== result.toolName || fact.toolCallId !== result.toolCallId || fact.iteration !== result.iteration || (Object.hasOwn(fact, 'measuredAt') && !(0, scope_js_1.boundedText)(fact.measuredAt) && typeof fact.measuredAt !== 'number')) throw new Error('Invalid fact receipt.'); } if (Object.hasOwn(result, 'coverage')) validateCoverage(result.coverage); if (Object.hasOwn(result, 'period') && (0, period_js_1.periodProblem)(result.period, 'camel') !== undefined) throw new Error('Invalid period receipt.'); } return raw; } /** Validate a detached saved snapshot without evaluating or reconstructing evidence. */ function readEvidenceSnapshot(raw) { if (raw !== null && typeof raw === 'object' && admittedSnapshots.has(raw)) return raw; const source = (0, boundedJson_js_1.ownData)((0, boundedJson_js_1.copyJson)(raw, scope_js_1.BINDING_LIMITS.snapshotBytes, true), [ 'version', 'namespace', 'runIds', 'results', ]); if (source.version !== 1 || !(0, scope_js_1.boundedText)(source.namespace) || !Array.isArray(source.runIds) || source.runIds.length > scope_js_1.BINDING_LIMITS.results || !source.runIds.every(scope_js_1.boundedText) || new Set(source.runIds).size !== source.runIds.length || !Array.isArray(source.results) || source.results.length > scope_js_1.BINDING_LIMITS.results) throw new Error('Invalid evidence snapshot.'); for (const result of source.results) resultOf(result); return rememberSnapshot(Object.freeze(source)); } exports.readEvidenceSnapshot = readEvidenceSnapshot; /** * Append a singleton admitted at the SAME snapshot root depth. Reuse only * owner-proven immutable history; unknown inputs still pass the strict reader. * The carrier owner separately checks reservation/lineage/ordinal coherence. */ function appendEvidenceSnapshot(snapshot, singleton) { const previous = readEvidenceSnapshot(snapshot); const addition = readEvidenceSnapshot(singleton); if (addition.results.length !== 1 || previous.namespace !== addition.namespace || previous.runIds.length !== addition.runIds.length || previous.runIds.some((runId, index) => runId !== addition.runIds[index])) throw new Error('Evidence append requires the same snapshot header and one receipt.'); if (previous.results.length >= scope_js_1.BINDING_LIMITS.results) throw new Error('Invalid evidence snapshot.'); const priorSize = admittedSnapshots.get(previous); const additionSize = admittedSnapshots.get(addition); if (priorSize === undefined || additionSize === undefined) throw new Error('Missing internal evidence admission proof.'); // Exact UTF-8 JSON bytes, including escaping and the new separating comma. // No old receipt is re-serialized, re-validated or deep-copied here. const bytes = priorSize.bytes + additionSize.bytes - additionSize.emptyBytes + (previous.results.length ? 1 : 0); if (bytes > scope_js_1.BINDING_LIMITS.snapshotBytes) throw new Error('JSON byte limit.'); return rememberSnapshot(Object.freeze({ ...previous, results: Object.freeze([...previous.results, addition.results[0]]), }), bytes); } exports.appendEvidenceSnapshot = appendEvidenceSnapshot; //# sourceMappingURL=admit.js.map