agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
103 lines • 4.97 kB
JavaScript
/** Pure prerequisite checks over caller-authenticated producer reports. No IO or value comparison. */
import { readRequirementResults, REQUIREMENT_RESULT_LIMITS, } from 'foottrace';
import { copyJson, ownData } from '../lib/boundedJson.js';
const POLICY_BYTES = 16 * 1024;
const CHECK_ID_CHARACTERS = 128;
function ref(value, maximum = REQUIREMENT_RESULT_LIMITS.name) {
if (typeof value !== 'string' ||
value.length === 0 ||
value.length > maximum ||
/\s/u.test(value))
throw new Error('Invalid reference.');
for (let index = 0; index < value.length; index++) {
const code = value.charCodeAt(index);
if (code <= 31 || (code >= 127 && code <= 159))
throw new Error('Invalid reference.');
}
return value;
}
/** Fixed diagnostics: never retain a hostile value or the exception raised while inspecting it. */
function readPolicy(value) {
try {
const source = ownData(value, ['claimRef', 'findingRef', 'windowRef', 'requirementRefs']);
if (!Array.isArray(source.requirementRefs))
throw new Error();
const length = Object.getOwnPropertyDescriptor(source.requirementRefs, 'length')
?.value;
if (typeof length !== 'number' || length < 1 || length > REQUIREMENT_RESULT_LIMITS.refs)
throw new Error();
const copied = ownData(copyJson(source, POLICY_BYTES, true));
const copiedRefs = copied.requirementRefs;
// Preflight limits the read; only the detached copy supplies admitted policy facts.
if (!Array.isArray(copiedRefs) ||
copiedRefs.length < 1 ||
copiedRefs.length > REQUIREMENT_RESULT_LIMITS.refs)
throw new Error();
const requirementRefs = copiedRefs.map((item) => ref(item));
if (new Set(requirementRefs).size !== requirementRefs.length)
throw new Error();
return Object.freeze({
claimRef: ref(copied.claimRef, CHECK_ID_CHARACTERS),
findingRef: ref(copied.findingRef),
windowRef: ref(copied.windowRef),
requirementRefs: Object.freeze(requirementRefs),
});
}
catch {
throw Object.assign(new TypeError('Invalid claim requirement policy.'), {
code: 'ERR_CLAIM_REQUIREMENT_POLICY',
});
}
}
function assessment(claimRef, resultRefs, reason) {
return Object.freeze({
check: Object.freeze({
id: claimRef,
disposition: reason === undefined ? 'checked-pass' : 'unreachable',
...(reason === undefined ? {} : { reason }),
}),
requirementResultRefs: Object.freeze([...resultRefs]),
});
}
/**
* Check every expected prerequisite for one exact claim/finding/window binding.
* Any same-scope unresolved report affecting this claim denies it, including an
* extra obligation; a satisfied report never overrides that denial. Missing
* expected obligations are unreachable, not a successful empty check.
*
* The host must authenticate the producer and current query/run/scope, authorize
* receipt access and establish domain validity BEFORE supplying results. The
* public Foottrace reader admits structure only; neither it nor this helper
* verifies provenance, freshness, a claim's value, or permission to disclose it.
* Use the returned check only when the candidate actually asserts this claim.
* Requirement result IDs are selectors, never artifact evidence references.
*
* A malformed host policy throws code ERR_CLAIM_REQUIREMENT_POLICY. Malformed
* receipt sets produce unreachable/invalid-data without exposing parser errors.
*/
export function checkClaimRequirements(policy, results) {
const expected = readPolicy(policy);
let admitted;
try {
admitted = readRequirementResults(results);
}
catch {
return assessment(expected.claimRef, [], 'invalid-data');
}
const affected = admitted.filter((result) => result.affects.includes(expected.claimRef));
const applicable = affected
.filter((result) => result.findingRef === expected.findingRef && result.windowRef === expected.windowRef)
.sort((left, right) => (left.id < right.id ? -1 : left.id > right.id ? 1 : 0));
const resultRefs = applicable.map((result) => result.id);
const unresolved = applicable.find((result) => result.state === 'unresolved');
if (unresolved?.state === 'unresolved')
return assessment(expected.claimRef, resultRefs, unresolved.reason);
for (const requirementRef of expected.requirementRefs) {
if (!applicable.some((result) => result.requirementRef === requirementRef)) {
const foreign = affected.some((result) => result.requirementRef === requirementRef);
return assessment(expected.claimRef, resultRefs, foreign ? 'incompatible' : 'not-collected');
}
}
return assessment(expected.claimRef, resultRefs);
}
//# sourceMappingURL=requirements.js.map