agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
115 lines • 5.5 kB
JavaScript
/**
* Compile the explicit verifier's authored rules and retention permission.
* Role: closure-only configuration; callbacks never enter recorded state.
*/
import { bindingRuleOf } from '../../../integrity/claim-binding/admit.js';
import { resultIdentityOf } from '../../../integrity/claim-binding/capture.js';
import { BINDING_LIMITS, boundedText, ownConfigArray, } from '../../../integrity/claim-binding/scope.js';
import { copyJson, ownData } from '../../../lib/boundedJson.js';
import { canonicalJson } from '../../../lib/canonicalJson.js';
import { readSemantics } from '../../../semantics/envelope.js';
const WITHHELD = Object.freeze({
resultVisible: false,
scopeVisible: false,
});
const GRANTED = Object.freeze({ resultVisible: true, scopeVisible: true });
function sameScope(left, right) {
// Functions are author-owned identities, not JSON or a stringified source.
if (left.dimensions.length !== right.dimensions.length ||
left.dimensions.some((dimension, index) => dimension.normalize !== right.dimensions[index].normalize))
return false;
const data = (scope) => ({
...scope,
dimensions: scope.dimensions.map((dimension) => ({ ...dimension, normalize: undefined })),
});
return canonicalJson(data(left)) === canonicalJson(data(right));
}
/** Only the existing detached capture ports can enter the author's callback. */
function authorizationInputOf(raw) {
const source = ownData(raw, ['dispatch', 'returned', 'served']);
const dispatch = ownData(copyJson(source.dispatch, BINDING_LIMITS.scopeBytes * 2, true), [
'identity',
'id',
'toolName',
'toolCallId',
'iteration',
'scopeDeclaration',
'scope',
'periodDimension',
'unavailable',
]);
const identity = ownData(dispatch.identity, ['namespace', 'runId', 'ordinal']);
const declaration = ownData(dispatch.scopeDeclaration, ['id', 'version']);
const returned = ownData(copyJson(source.returned, BINDING_LIMITS.resultBytes + BINDING_LIMITS.scopeBytes, true), ['resultId', 'semantics', 'unavailable']);
if (dispatch.unavailable !== undefined ||
returned.unavailable !== undefined ||
!boundedText(identity.namespace) ||
!boundedText(identity.runId) ||
typeof identity.ordinal !== 'number' ||
dispatch.id !==
resultIdentityOf({
namespace: identity.namespace,
runId: identity.runId,
ordinal: identity.ordinal,
}) ||
returned.resultId !== dispatch.id ||
!boundedText(dispatch.toolName) ||
!boundedText(dispatch.toolCallId) ||
typeof dispatch.iteration !== 'number' ||
!Number.isSafeInteger(dispatch.iteration) ||
dispatch.iteration < 0 ||
!boundedText(declaration.id) ||
!boundedText(declaration.version) ||
(Object.hasOwn(dispatch, 'periodDimension') && !boundedText(dispatch.periodDimension)) ||
readSemantics(returned.semantics) === undefined)
throw new Error('Invalid verification authorization input.');
ownData(copyJson(dispatch.scope, BINDING_LIMITS.scopeBytes, true));
return Object.freeze({
dispatch: Object.freeze(dispatch),
returned: Object.freeze(returned),
served: copyJson(source.served, BINDING_LIMITS.resultBytes, true),
});
}
/** Strict configuration admission runs once, not at every verification. */
export function compileVerificationConfig(raw) {
const source = ownData(raw, ['rules', 'authorizeEvidence']);
const rules = ownConfigArray(source.rules, BINDING_LIMITS.results).map(bindingRuleOf);
if (rules.length === 0 || typeof source.authorizeEvidence !== 'function')
throw new Error('findings.verify requires rules and an explicit authorizeEvidence callback.');
const authorize = source.authorizeEvidence;
const ids = new Set();
const scopes = new Map();
for (const rule of rules) {
if (ids.has(rule.id))
throw new Error('Duplicate findings verification rule.');
ids.add(rule.id);
const prior = scopes.get(rule.toolName);
if (prior !== undefined && !sameScope(prior, rule.scopeDeclaration))
throw new Error('Conflicting findings verification scopes for one tool.');
scopes.set(rule.toolName, rule.scopeDeclaration);
}
return Object.freeze({
rules: Object.freeze(rules),
scopeForTool: (toolName) => scopes.get(toolName),
authorizeEvidence: (input) => {
try {
const response = authorize(authorizationInputOf(input));
// A mistaken async callback cannot grant access. Consume rejection of
// a genuine Promise without awaiting it or invoking an arbitrary then.
if (response instanceof Promise) {
void Promise.prototype.then.call(response, undefined, () => undefined);
return WITHHELD;
}
const decision = ownData(response, ['resultVisible', 'scopeVisible']);
return decision.resultVisible === true && decision.scopeVisible === true
? GRANTED
: WITHHELD;
}
catch {
// Never record policy exception text, return values or private inputs.
return WITHHELD;
}
},
});
}
//# sourceMappingURL=verificationConfig.js.map