agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
91 lines • 4.05 kB
JavaScript
;
/**
* The app's declared data — validated at call time. A bad declaration is a
* CALLER error and throws (on the server the declarations are the host's, handed
* in at boot, so it throws when the host boots, never per request).
*
* Rules: labels non-empty, one plain visible line (`lib/plainLine.ts` ·
* `plainLineProblem`, the rule `short` and `title` use), at most 60 characters; `rowsAt` a non-empty
* top-level key with no `/` or `.`; unknown keys refused by name.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.validateDeclarations = exports.MAX_LABEL_CHARS = void 0;
const emptiness_js_1 = require("../core/agent/emptiness.js");
const plainLine_js_1 = require("../lib/plainLine.js");
exports.MAX_LABEL_CHARS = 60;
const TOP_KEYS = new Set(['id', 'version', 'skills', 'tools', 'routing']);
function fail(message) {
throw new TypeError(`accountForAnswer declarations: ${message}`);
}
const isPlain = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
function onlyKeys(where, value, allowed) {
for (const key of Object.keys(value)) {
if (!allowed.includes(key))
fail(`unknown key "${key}" in ${where}`);
}
}
function checkLabel(id, label) {
if (typeof label !== 'string' || label.trim().length === 0)
fail(`skills.${id}.label must be a non-empty string`);
// The same rule as a coverage item's `short` and a skill's `title`: all three print in
// one report, so they must refuse the same characters (controls, separators, format).
const problem = (0, plainLine_js_1.plainLineProblem)(`skills.${id}.label`, label);
if (problem !== undefined)
fail(problem);
if (label.length > exports.MAX_LABEL_CHARS)
fail(`skills.${id}.label is over ${exports.MAX_LABEL_CHARS} characters`);
}
function checkRowsAt(tool, rowsAt) {
const problem = (0, emptiness_js_1.rowsAtProblem)(rowsAt);
if (problem === 'empty')
fail(`tools.${tool}.rowsAt must be a non-empty key`);
if (problem === 'nested')
fail(`tools.${tool}.rowsAt must be a top-level key (no "/" or ".")`);
}
/** Returns the declarations (or `{}`), or throws a `TypeError` naming the first problem. */
function validateDeclarations(value) {
if (value === undefined)
return {};
if (!isPlain(value))
fail('must be an object');
for (const key of Object.keys(value))
if (!TOP_KEYS.has(key))
fail(`unknown key "${key}"`);
for (const key of ['id', 'version']) {
if (value[key] !== undefined && typeof value[key] !== 'string')
fail(`${key} must be a string`);
}
if (value.skills !== undefined) {
if (!isPlain(value.skills))
fail('skills must be an object keyed by skill id');
for (const [id, entry] of Object.entries(value.skills)) {
if (!isPlain(entry))
fail(`skills.${id} must be an object`);
onlyKeys(`skills.${id}`, entry, ['label']);
if (entry.label !== undefined)
checkLabel(id, entry.label);
}
}
if (value.tools !== undefined) {
if (!isPlain(value.tools))
fail('tools must be an object keyed by tool name');
for (const [tool, entry] of Object.entries(value.tools)) {
if (!isPlain(entry))
fail(`tools.${tool} must be an object`);
onlyKeys(`tools.${tool}`, entry, ['rowsAt']);
if (entry.rowsAt !== undefined)
checkRowsAt(tool, entry.rowsAt);
}
}
if (value.routing !== undefined) {
if (!isPlain(value.routing))
fail('routing must be an object');
onlyKeys('routing', value.routing, ['appDecides']);
if (value.routing.appDecides !== undefined && typeof value.routing.appDecides !== 'boolean') {
fail('routing.appDecides must be a boolean');
}
}
return value;
}
exports.validateDeclarations = validateDeclarations;
//# sourceMappingURL=declarations.js.map