UNPKG

agentfootprint

Version:

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

148 lines • 7.22 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.bindClaim = void 0; /** Exact evidence selection and eligibility, before the shared pair evaluator. */ const boundedJson_js_1 = require("../../lib/boundedJson.js"); const types_js_1 = require("../assertion/types.js"); const period_js_1 = require("../../semantics/coverage/period.js"); const capture_js_1 = require("./capture.js"); const scope_js_1 = require("./scope.js"); const admit_js_1 = require("./admit.js"); const unverified = (reason) => ({ kind: 'unverified', reason }); const isDoubt = (value) => value === null || value === 'unknown' || value === undefined; /** * The model supplies only result/value/rule. Snapshot lineage, rule registry * and intendedScope are runtime/app inputs. A bound pair is not a verdict. * JSON-restored snapshots must originate from the caller's verified recording; * this module validates shape and associations, not signatures or ownership. */ function bindClaim(rawClaim, snapshot, rules, intendedScope) { let claim; try { claim = (0, boundedJson_js_1.ownData)((0, boundedJson_js_1.copyJson)(rawClaim, scope_js_1.BINDING_LIMITS.scopeBytes, true), [ 'result', 'value', 'rule', ]); if (!(0, scope_js_1.boundedResultId)(claim.result) || !(0, scope_js_1.boundedText)(claim.rule) || !Object.hasOwn(claim, 'value')) return unverified('invalid-claim'); } catch { return unverified('invalid-claim'); } let matchingRules; try { matchingRules = (0, scope_js_1.ownConfigArray)(rules, scope_js_1.BINDING_LIMITS.results) .map(admit_js_1.bindingRuleOf) .filter((rule) => rule.id === claim.rule); } catch { return unverified('invalid-rule'); } if (matchingRules.length === 0) return unverified('unknown-rule'); if (matchingRules.length !== 1) return unverified('invalid-rule'); const rule = matchingRules[0]; if (rule === undefined) return unverified('invalid-rule'); try { const saved = (0, admit_js_1.readEvidenceSnapshot)(snapshot); const matches = saved.results.filter((result) => result.id === claim.result); if (matches.length === 0) return unverified('missing-result'); if (matches.length !== 1) return unverified('ambiguous-result'); const result = matches[0]; if (result === undefined || result.id !== (0, capture_js_1.resultIdentityOf)(result.identity)) return unverified('invalid-evidence'); if (result.identity.namespace !== saved.namespace || !saved.runIds.includes(result.identity.runId)) return unverified('foreign-result'); if (result.unavailable !== undefined) return unverified(result.unavailable); if (result.toolName !== rule.toolName) return unverified('invalid-rule'); if (result.scopeDeclaration.id !== rule.scopeDeclaration.id || result.scopeDeclaration.version !== rule.scopeDeclaration.version) return unverified('scope-version-mismatch'); if (result.scope === undefined) return unverified('missing-scope'); const dimensions = rule.scopeDeclaration.dimensions.map((dimension) => dimension.name); if (!dimensions.every(scope_js_1.boundedText) || new Set(dimensions).size !== dimensions.length || dimensions.some((name) => !Object.hasOwn(result.scope, name)) || Object.keys(result.scope).some((name) => !dimensions.includes(name))) return unverified('missing-scope'); if (result.periodDimension !== rule.scopeDeclaration.periodDimension) return unverified('scope-version-mismatch'); if (intendedScope !== undefined && !(0, scope_js_1.scopesMatch)(result.scope, intendedScope)) return unverified('scope-mismatch'); const entity = typeof rule.fact.entity === 'string' ? rule.fact.entity : result.scope[rule.fact.entity.scopeDimension]; if (!(0, scope_js_1.boundedText)(entity) || !(0, scope_js_1.boundedText)(rule.fact.field)) return unverified('invalid-rule'); if (!Array.isArray(result.facts)) return unverified('invalid-evidence'); const facts = result.facts.filter((fact) => fact.entity === entity && fact.field === rule.fact.field); if (facts.length === 0) return unverified('missing-fact'); if (facts.length !== 1) return unverified('ambiguous-fact'); const fact = facts[0]; if (fact === undefined || !(0, types_js_1.participates)(fact.value) || !(0, types_js_1.participates)(claim.value)) return unverified('unasserted-value'); // Absence/doubt needs a separately authored absence rule in a later slice. if (isDoubt((0, types_js_1.comparableValueOf)(fact.value)) || isDoubt((0, types_js_1.comparableValueOf)(claim.value))) return unverified('unasserted-value'); if (result.period !== undefined) { const verdict = (0, period_js_1.periodVerdict)(result.period); if (verdict === 'unknown') return unverified('coverage-unknown'); if (verdict !== 'covered') return unverified('coverage-partial'); } if (result.periodDimension !== undefined) { if (result.period === undefined) return unverified('coverage-undeclared'); if (!(0, scope_js_1.queriedPeriodMatches)(result.scope[result.periodDimension], result.period)) return unverified('scope-mismatch'); } if (rule.coverage === undefined || result.coverage === undefined) return unverified('coverage-undeclared'); const completeness = rule.coverage({ scope: result.scope, coverage: result.coverage, ...(result.period !== undefined && { period: result.period }), }); if (completeness === 'partial') return unverified('coverage-partial'); if (completeness === 'stale') return unverified('coverage-stale'); if (completeness !== 'complete') return unverified('coverage-unknown'); const common = { subject: { kind: 'entity', id: entity }, predicate: rule.fact.field, stratum: 'asserted', }; return (0, boundedJson_js_1.copyJson)({ kind: 'bound', resultId: result.id, rule: { id: rule.id, version: rule.version }, scope: result.scope, coverage: result.coverage, ...(result.period !== undefined && { period: result.period }), evidence: { ...common, value: fact.value, provenance: `result ${result.id}` }, claim: { ...common, value: claim.value, provenance: `rule ${rule.id}` }, }, scope_js_1.BINDING_LIMITS.resultBytes, true); } catch { return unverified('invalid-evidence'); } } exports.bindClaim = bindClaim; //# sourceMappingURL=bindEvidence.js.map