UNPKG

agentfootprint

Version:

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

89 lines • 3.65 kB
/** * search/review — what a person should LOOK AT in a `Tool.search`, never a * refusal. * * Pattern: one pure lister, two readers: the `check:semantics` gate (warnings, * errors only under `--strict`) and `defineTool`'s dev-mode warning. * Role: semantics/ layer, pure. * Emits: N/A. * * • `source-said` — every word a claim may use to scope itself to the * source. A word wider than the source ("the estate") would let a claim * about everything read as a claim about this store, so a person reviews * the list, like an `authoritative` use. No word ever removes a gap. * • `said-overlap` — a `subject.said` word that repeats a qualifier: a * filter value's word (a qualifier NOT in `fixed`), or a fixed * qualifier's own word. The subject says WHAT was looked for and the * qualifiers say WHICH; a subject word carrying a qualifier lets a claim * match the subject without naming the qualifier the search applied. */ /** The words of a phrase, for a phrase-in-phrase test — NFC, case-folded. */ function wordsOf(phrase) { return phrase .normalize('NFC') .toLowerCase() .split(/[^\p{L}\p{N}]+/u) .filter((w) => w !== ''); } /** Whether `inner`'s words appear, in order and adjacent, inside `outer`'s. */ function containsPhrase(outer, inner) { if (inner.length === 0 || inner.length > outer.length) return false; for (let i = 0; i + inner.length <= outer.length; i += 1) { if (inner.every((w, j) => outer[i + j] === w)) return true; } return false; } /** Every qualifier word the search declares, with where it came from. */ function qualifierWords(search) { const out = []; for (const q of search.fixed ?? []) { for (const word of q.said) out.push({ word, from: `the fixed qualifier '${q.name}'` }); } const filters = search.arguments.filter((a) => a.role === 'filter'); for (const a of filters) { for (const [value, words] of Object.entries(a.said ?? {})) { for (const word of [value, ...words]) { out.push({ word, from: `the filter '${a.name}' (not a fixed qualifier)` }); } } } return out; } /** * The review items of one search — `[]` when there is nothing to look at. * * @example * ```ts * searchReviewItems(search).map((i) => i.code); // ['source-said'] * ``` */ export function searchReviewItems(search) { const items = [ { code: 'source-said', field: 'search.source.said', message: `a claim using one of these words is read as scoped to '${search.source.what}': ` + `${search.source.said.map((w) => `'${w}'`).join(', ')}. Check that each names this ` + 'source and nothing wider — no word ever removes a gap.', }, ]; const qualifiers = qualifierWords(search); search.subject.said.forEach((subjectWord, i) => { const subject = wordsOf(subjectWord); const hit = qualifiers.find((q) => containsPhrase(subject, wordsOf(q.word))); if (hit === undefined) return; items.push({ code: 'said-overlap', field: `search.subject.said[${i}]`, message: `'${subjectWord}' repeats '${hit.word}', a word of ${hit.from}. Keep the subject's ` + 'words to what was looked for and the qualifiers in their own lists, or a claim can ' + 'match the subject without naming a qualifier the search applied.', }); }); return items; } //# sourceMappingURL=review.js.map