agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
61 lines • 3.33 kB
JavaScript
import { findingsReferenceRefusal } from './admission.js';
export class FindingsAnswerRefusal extends Error {
teaching;
constructor(teaching) {
super('Findings reference admission refused the answer.');
this.teaching = teaching;
}
}
/** Throws only after the caller has preserved the attempted standings. */
export function assertAnswerReferences(scope, declaration, malformed = 0) {
const refusal = findingsReferenceRefusal(declaration, scope.findingsRequest?.[0], scope.iteration, malformed);
if (refusal !== undefined)
throw new FindingsAnswerRefusal(refusal);
}
export function withFindingsAdmission(decider, enforcement, recordStandings, announce, willJudgeAnswer) {
return async (scope) => {
const emission = scope.llmLatestContent;
try {
// Do not give an output callback an invalid raw proposal. A callback's
// own rewrite is checked again by the shared peel before any judge.
if (willJudgeAnswer(scope)) {
const { peelAnswerFindings } = await import('./peel.js');
const peeled = peelAnswerFindings(emission);
if (findingsReferenceRefusal(peeled.findings, scope.findingsRequest?.[0], scope.iteration, peeled.malformed) !== undefined)
await recordStandings(scope); // records, then throws the same refusal
}
return await decider(scope);
}
catch (error) {
if (!(error instanceof FindingsAnswerRefusal))
throw error;
// A host output denial is terminal. The peel already preserved the
// invalid attempted findings; neither a retry nor a replacement denial
// may work around the owner's decision to withhold this answer.
if (scope.messageDeniedPhase === 'output' && scope.messageDeniedReason !== undefined) {
announce(scope, 'final', 'host output denial remains terminal; findings retained as diagnostics');
return 'final';
}
const attempt = (scope.outputAttempts?.length ?? 0) + 1;
if (enforcement !== undefined &&
attempt <= enforcement.retries &&
scope.iteration < scope.maxIterations &&
scope.wrapUpAsked !== true &&
!(scope.costBudgetHit && scope.costBudgetOnExceed === 'halt')) {
scope.outputSchemaFailure = { attempt, stage: 'schema-validate', error: error.teaching };
scope.llmLatestContent = emission;
announce(scope, 'output-retry', 'findings reference refused; spending a configured output retry');
return 'output-retry';
}
// This is a terminal verdict, not a replacement successful answer.
// Already streamed drafts remain drafts; Phase A does not buffer them.
scope.messageDeniedReason = error.teaching;
scope.messageDeniedPhase = 'output';
scope.messageDeniedBy = 'findings-reference-admission';
scope.llmLatestContent = error.teaching;
announce(scope, 'final', 'findings reference refused; no configured correction allowance remains');
return 'final';
}
};
}
//# sourceMappingURL=answerAdmission.js.map