agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
102 lines • 4.56 kB
JavaScript
/**
* search/statementText — the rule for declaration text that can reach a
* statement the library writes (`statement-text-refused`).
*
* Pattern: one pure rule, asked by `declaration.ts` at `defineTool` (an
* ERROR: `search` is a new API, so no consumer breaks), over the ONE
* list of texts that reach a statement (`statementTextsOf`).
* Role: semantics/ layer, pure.
* Emits: N/A.
*
* A declaration is untrusted text from the tool author, and a statement of a
* negative claim is built from it — the source, the subject, each fixed
* qualifier and default as `name: value`, each argument's name, each gap's
* `what` and `why`. Three layers hold it, and only the last is the boundary:
*
* 1. SHAPE — one line of at most {@link MAX_STATEMENT_TEXT} characters, in a
* stated character class: letters, combining marks, digits, spaces and
* `. , : ; ' ( ) - _ / & % + # @`. No format character (zero-width, a
* bidi override), no control, no quote mark, backtick or bracket a
* renderer could be broken out of.
* 2. A HEURISTIC — the instruction phrases ("do not", "always", "tell the
* user", …), scanned after NFKC folding with format characters removed,
* so a fullwidth or zero-width-split phrase is caught. A denylist is
* never complete (confusables pass it); it catches the honest mistake.
* 3. THE BOUNDARY — the statement renders declaration text as QUOTED DATA
* ATTRIBUTED TO THE TOOL, never as library-authored prose:
* `source (as the tool names it): "the incident store"`. Whatever the
* words say, the person reads them as the tool's, in quotes. The
* statement writer (the negative-claim checker's `statement.ts`) owns
* that rendering; this rule only bounds what it is handed.
*/
import { plainLineProblem } from '../../../lib/plainLine.js';
/** The longest declaration text a statement carries. */
export const MAX_STATEMENT_TEXT = 120;
/** The stated character class for statement text (shape, layer 1). */
const STATEMENT_CHARACTERS = /^[\p{L}\p{M}\p{N} .,:;'()\-_/&%+#@]*$/u;
/**
* The instructing phrases — the design's `declaration-instructs` list, plus
* the plain imperatives a model obeys. Matched case-folded, on whole words,
* after NFKC folding (layer 2: a heuristic, not the boundary).
*/
export const STATEMENT_INSTRUCTION_PHRASES = Object.freeze([
'do not',
"don't",
'always',
'tell the user',
'carry these',
'report that',
'ignore',
'disregard',
'you must',
'you should',
]);
/** One spelling for the scan: NFKC, format characters removed, case-folded, one space. */
function scanSpelling(text) {
return text
.normalize('NFKC')
.replace(/\p{Cf}/gu, '')
.toLowerCase()
.replace(/[‘’ʼ]/g, "'")
.replace(/\s+/g, ' ');
}
/** The instructing phrase `text` carries, or `undefined`. */
export function instructionIn(text) {
const t = scanSpelling(text);
return STATEMENT_INSTRUCTION_PHRASES.find((phrase) => {
let at = t.indexOf(phrase);
while (at !== -1) {
const before = at === 0 ? '' : t[at - 1];
const after = t[at + phrase.length] ?? '';
if (!/[\p{L}\p{N}']/u.test(before) && !/[\p{L}\p{N}]/u.test(after))
return true;
at = t.indexOf(phrase, at + 1);
}
return false;
});
}
/**
* Why `text` (declared under `field`) cannot reach a statement, or
* `undefined` when it can.
*/
export function statementTextProblem(field, text) {
const notPlain = plainLineProblem(`\`${field}\``, text);
if (notPlain !== undefined)
return notPlain;
if (text.length > MAX_STATEMENT_TEXT) {
return (`\`${field}\` is ${text.length} characters; the library's statements carry at most ` +
`${MAX_STATEMENT_TEXT}.`);
}
if (!STATEMENT_CHARACTERS.test(text)) {
return (`\`${field}\` carries a character outside the statement set — letters, digits, spaces ` +
"and . , : ; ' ( ) - _ / & % + # @ — so no quote mark, backtick, bracket or symbol a " +
'renderer could be broken out of.');
}
const instruction = instructionIn(text);
if (instruction !== undefined) {
return (`\`${field}\` instructs ('${instruction}') — it is read into a statement the person ` +
'reads, so it describes ground and never tells a reader what to do or say.');
}
return undefined;
}
//# sourceMappingURL=statementText.js.map