UNPKG

agentfootprint

Version:

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

181 lines • 9.33 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.isClaimVerificationRow = exports.readClaimVerificationRow = exports.readClaimIdentity = exports.claimIdentityOf = exports.VERIFICATION_LIMITS = void 0; /** * Strict saved claim-verification protocol. Role: validate and detach a recorded * check, never reconstruct evidence or rerun its checker. Storage authenticity * and caller authorization are the surrounding checkpoint owner's responsibility. */ const boundedJson_js_1 = require("../../../lib/boundedJson.js"); const canonicalJson_js_1 = require("../../../lib/canonicalJson.js"); const encode_js_1 = require("../../../memory/identity/encode.js"); const types_js_1 = require("../../../integrity/assertion/types.js"); const scope_js_1 = require("../../../integrity/claim-binding/scope.js"); const admit_js_1 = require("../../../integrity/claim-binding/admit.js"); const capture_js_1 = require("../../../integrity/claim-binding/capture.js"); const period_js_1 = require("../../../semantics/coverage/period.js"); const types_js_2 = require("./types.js"); const verificationTypes_js_1 = require("./verificationTypes.js"); /** Reuse receipt bounds; declared values have the compact binding-request cap. */ exports.VERIFICATION_LIMITS = Object.freeze({ rowBytes: scope_js_1.BINDING_LIMITS.resultBytes, valueBytes: scope_js_1.BINDING_LIMITS.scopeBytes, identityText: scope_js_1.BINDING_LIMITS.resultIdText + 'claim-verification'.length - 'claim-result'.length, }); /** A runtime-reserved claim occurrence cannot collide with a result occurrence. */ function claimIdentityOf(raw) { const identity = (0, boundedJson_js_1.ownData)(raw, ['namespace', 'runId', 'ordinal']); 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) throw new TypeError('Invalid runtime claim identity.'); return `claim-verification/${(0, encode_js_1.encodeIdentityField)(identity.namespace)}/${(0, encode_js_1.encodeIdentityField)(identity.runId)}/${identity.ordinal}`; } exports.claimIdentityOf = claimIdentityOf; /** Canonical claim tuple reader, shared with checkpoint lineage/counter checks. */ function readClaimIdentity(value) { if (typeof value !== 'string' || value.length > exports.VERIFICATION_LIMITS.identityText) throw new TypeError('Invalid saved claim identity.'); const parts = value.split('/'); if (parts.length !== 4 || parts[0] !== 'claim-verification') throw new TypeError('Invalid saved claim identity.'); const identity = { namespace: (0, encode_js_1.decodePresentIdentityField)(parts[1]), runId: (0, encode_js_1.decodePresentIdentityField)(parts[2]), ordinal: Number(parts[3]), }; if (value !== claimIdentityOf(identity)) throw new TypeError('Noncanonical saved claim identity.'); return Object.freeze(identity); } exports.readClaimIdentity = readClaimIdentity; function integer(value, minimum) { return typeof value === 'number' && Number.isSafeInteger(value) && value >= minimum; } function eligibleAssertion(raw) { const assertion = (0, boundedJson_js_1.ownData)(raw, ['subject', 'predicate', 'value', 'stratum', 'provenance']); const subject = (0, boundedJson_js_1.ownData)(assertion.subject, ['kind', 'id']); const comparable = (0, types_js_1.comparableValueOf)(assertion.value); if (subject.kind !== 'entity' || !(0, scope_js_1.boundedText)(subject.id) || !(0, scope_js_1.boundedText)(assertion.predicate) || assertion.stratum !== 'asserted' || typeof assertion.provenance !== 'string' || !Object.hasOwn(assertion, 'value') || !(0, types_js_1.participates)(assertion.value) || comparable === null || comparable === undefined || comparable === 'unknown') throw new TypeError('Invalid eligible assertion.'); return assertion; } function validateResolved(row, claimIdentity) { const resolved = (0, boundedJson_js_1.ownData)(row.resolved, [ 'resultId', 'rule', 'scope', 'coverage', 'period', 'evidence', 'claim', ]); const rule = (0, boundedJson_js_1.ownData)(resolved.rule, ['id', 'version']); const scope = (0, boundedJson_js_1.ownData)((0, boundedJson_js_1.copyJson)(resolved.scope, scope_js_1.BINDING_LIMITS.scopeBytes, true)); const resultIdentity = (0, capture_js_1.readResultIdentity)(resolved.resultId); if (resultIdentity.namespace !== claimIdentity.namespace || resolved.resultId !== row.declaredResult || !(0, scope_js_1.boundedText)(rule.id) || rule.id !== row.declaredRule || !(0, scope_js_1.boundedText)(rule.version) || Object.keys(scope).length === 0 || Object.keys(scope).length > scope_js_1.BINDING_LIMITS.dimensions || !Object.keys(scope).every(scope_js_1.boundedText)) throw new TypeError('Invalid verification association.'); (0, admit_js_1.validateCoverage)(resolved.coverage); if (Object.hasOwn(resolved, 'period') && (0, period_js_1.periodProblem)(resolved.period, 'camel') !== undefined) throw new TypeError('Invalid verification period.'); const evidence = eligibleAssertion(resolved.evidence); const claim = eligibleAssertion(resolved.claim); if ((0, canonicalJson_js_1.canonicalJson)(evidence.subject) !== (0, canonicalJson_js_1.canonicalJson)(claim.subject) || evidence.predicate !== claim.predicate || evidence.provenance !== `result ${resolved.resultId}` || claim.provenance !== `rule ${rule.id}` || (0, canonicalJson_js_1.canonicalJson)(claim.value) !== (0, canonicalJson_js_1.canonicalJson)(row.declaredValue)) throw new TypeError('Invalid verification pair association.'); } /** Validate the recorded protocol without evaluating whether its value agrees. */ function readClaimVerificationRow(raw) { const detached = (0, boundedJson_js_1.copyJson)(raw, exports.VERIFICATION_LIMITS.rowBytes, true); const row = (0, boundedJson_js_1.ownData)(detached, [ 'kind', 'claimId', 'declaredOn', 'standing', 'iteration', 'turn', 'checker', 'declaredResult', 'declaredRule', 'declaredValue', 'outcome', 'reason', 'resolved', ]); const checker = (0, boundedJson_js_1.ownData)(row.checker, ['id', 'version']); const claimIdentity = readClaimIdentity(row.claimId); if (row.kind !== 'claim-verification' || typeof row.standing !== 'string' || !types_js_2.STANDING_VALUES.includes(row.standing) || !integer(row.iteration, 0) || (Object.hasOwn(row, 'turn') && !integer(row.turn, 1)) || checker.id !== 'claim-pair' || checker.version !== 1 || !Object.hasOwn(row, 'declaredValue') || (Object.hasOwn(row, 'declaredResult') && (typeof row.declaredResult !== 'string' || row.declaredResult.length > scope_js_1.BINDING_LIMITS.resultIdText)) || (Object.hasOwn(row, 'declaredRule') && !(0, scope_js_1.boundedText)(row.declaredRule))) throw new TypeError('Invalid claim-verification row.'); (0, boundedJson_js_1.copyJson)(row.declaredValue, exports.VERIFICATION_LIMITS.valueBytes, true); if (row.declaredOn !== 'answer') { const at = (0, boundedJson_js_1.ownData)(row.declaredOn, ['toolCallId']); if (!(0, scope_js_1.boundedText)(at.toolCallId)) throw new TypeError('Invalid claim declaration location.'); } if (row.outcome === 'unverified') { if (typeof row.reason !== 'string' || !verificationTypes_js_1.VERIFICATION_UNVERIFIED_REASONS.includes(row.reason) || Object.hasOwn(row, 'resolved') || (row.standing === 'noise' && row.reason !== 'ineligible-noise') || (row.reason === 'ineligible-noise' && row.standing !== 'noise') || (row.reason === 'missing-check' && Object.hasOwn(row, 'declaredRule')) || (!['missing-check', 'malformed-check', 'ineligible-noise'].includes(row.reason) && (!Object.hasOwn(row, 'declaredRule') || (row.reason !== 'missing-result' && !Object.hasOwn(row, 'declaredResult'))))) throw new TypeError('Invalid unverified claim row.'); } else { if (row.standing === 'noise' || !((row.outcome === 'supported' && (row.reason === 'no-conflict' || row.reason === 'numeric-leniency')) || (row.outcome === 'contradicted' && row.reason === 'conflict'))) throw new TypeError('Invalid compared claim row.'); validateResolved(row, claimIdentity); } return detached; } exports.readClaimVerificationRow = readClaimVerificationRow; /** Non-throwing checkpoint union guard; admission uses the same strict reader. */ function isClaimVerificationRow(raw) { try { readClaimVerificationRow(raw); return true; } catch { return false; } } exports.isClaimVerificationRow = isClaimVerificationRow; //# sourceMappingURL=verificationRow.js.map