agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
109 lines • 4.98 kB
JavaScript
;
/**
* search/resultFacts — the two facts a search tool's RESULT states out loud:
* `resolved` (the entity a population value resolved to) and `paging` (whether
* there is a next page).
*
* Pattern: one rule set, two doors (the `readItemExtras` law). The mint
* (`absent()`, `describedResult()`) THROWS a refusal at the line the
* author wrote; the reader (any envelope, minted anywhere) DROPS a
* malformed value — read, never repaired.
* Role: semantics/ layer, pure. The same spelling on the wire and in code
* (every key is one word).
* Emits: N/A.
*
* Both are the tool's own claims, said out loud — the trusted base. Each can
* only NARROW what the library composes: `resolved` attaches to the value the
* call was sent for and to no other entity; `paging.next` that is not null is
* a gap, and a first page is complete only with `next: null`.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.readPaging = exports.readResolved = exports.mintPaging = exports.mintResolved = exports.pagingProblem = exports.resolvedProblem = void 0;
const refusal_js_1 = require("../refusal.js");
const isPlain = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
const isWord = (v) => typeof v === 'string' && v.trim() !== '';
const RESOLVED_KEYS = ['argument', 'id', 'name', 'aliases'];
/** What is wrong with a `resolved` list, or `undefined` when it is fine. */
function resolvedProblem(value) {
if (!Array.isArray(value) || value.length === 0) {
return '`resolved` must be a non-empty list of { argument, id, name?, aliases? } — omit it to say nothing.';
}
for (const [i, raw] of value.entries()) {
const at = `\`resolved[${i}]\``;
if (!isPlain(raw))
return `${at} must be { argument, id, name?, aliases? }.`;
const unknown = Object.keys(raw).find((k) => !RESOLVED_KEYS.includes(k));
if (unknown !== undefined)
return `${at} has an unknown key '${unknown}'.`;
if (!isWord(raw.argument))
return `${at}.argument must name the population argument.`;
if (!isWord(raw.id))
return `${at}.id must be a non-empty string.`;
if (raw.name !== undefined && !isWord(raw.name))
return `${at}.name must be a non-empty string.`;
if (raw.aliases !== undefined && (!Array.isArray(raw.aliases) || !raw.aliases.every(isWord))) {
return `${at}.aliases must be a list of non-empty strings.`;
}
}
return undefined;
}
exports.resolvedProblem = resolvedProblem;
/** What is wrong with a `paging`, or `undefined` when it is fine. */
function pagingProblem(value) {
if (!isPlain(value) || !('next' in value)) {
return '`paging` must be { next } — `next: null` when there is no next page.';
}
const unknown = Object.keys(value).find((k) => k !== 'next');
if (unknown !== undefined)
return `\`paging\` has an unknown key '${unknown}'.`;
const next = value.next;
const ok = next === null || isWord(next) || (typeof next === 'number' && Number.isFinite(next));
return ok
? undefined
: '`paging.next` must be null (no next page), a cursor string or a page number.';
}
exports.pagingProblem = pagingProblem;
function copyResolved(list) {
return list.map((r) => ({
argument: r.argument,
id: r.id,
...(r.name !== undefined && { name: r.name }),
...(r.aliases !== undefined && { aliases: [...r.aliases] }),
}));
}
/** The minted `resolved` — a copy — or `undefined` for none; THROWS a refusal when malformed. */
function mintResolved(value) {
if (value === undefined || value === null)
return undefined;
const problem = resolvedProblem(value);
if (problem !== undefined)
throw (0, refusal_js_1.refusal)(problem);
return copyResolved(value);
}
exports.mintResolved = mintResolved;
/** The minted `paging` — a copy — or `undefined` for none; THROWS a refusal when malformed. */
function mintPaging(value) {
if (value === undefined)
return undefined;
const problem = pagingProblem(value);
if (problem !== undefined)
throw (0, refusal_js_1.refusal)(problem);
return { next: value.next };
}
exports.mintPaging = mintPaging;
/** A `resolved` read off an envelope — a copy, or `undefined` when absent or malformed. */
function readResolved(value) {
if (value === undefined || value === null || resolvedProblem(value) !== undefined) {
return undefined;
}
return copyResolved(value);
}
exports.readResolved = readResolved;
/** A `paging` read off an envelope — a copy, or `undefined` when absent or malformed. */
function readPaging(value) {
if (value === undefined || pagingProblem(value) !== undefined)
return undefined;
return { next: value.next };
}
exports.readPaging = readPaging;
//# sourceMappingURL=resultFacts.js.map