agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
187 lines • 8.7 kB
JavaScript
;
/**
* The manifest bridge — which arm did this run belong to, according to the RUN
* rather than according to the experimenter.
*
* Before 9.41.0 an arm label was bookkeeping: the consumer set a variable to
* `'rerank'`, built an agent it believed re-ranked, and nothing in the library
* could contradict it. `agentfootprint.agent.run_configured` names the adapters
* and strategies a run is about to use, so the belief became checkable — and
* {@link ArmFacets} is deliberately spelled in that event's own vocabulary so
* the check is a field-by-field comparison and not a translation layer.
*
* Two directions, both here:
* - **verify** — `checkArmApplied(arm, manifest)`: the arm said `retrieval:
* 'rerank'`; did the run agree? A contradiction costs the arm its verdict
* (`compare.ts`), because a difference measured between two arms that were
* secretly one configuration is not evidence about either.
* - **classify** — `matchArm(arms, manifest)`: hand it a recorded run's
* manifest and it says which declared arm that run belongs to. This is the
* door for grouping runs a study already made, offline, out of saved
* recordings.
*
* ## Absence is a contradiction, not a wildcard
*
* The manifest's own rule is that an absent field means "not configured", never
* a guessed default. So an arm declaring `window: 'tokenBudget'` against a
* manifest with no `window` is a MISMATCH, reported with `observed` absent. The
* alternative — treating absence as "could be anything" — would let a run with
* no window stage pass as the token-budget arm, which is exactly the mis-wiring
* this file exists to catch.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.matchArm = exports.checkArmApplied = exports.armLabel = exports.armFacetsFromManifest = void 0;
const validate_js_1 = require("./validate.js");
/**
* Project a manifest onto the arm-facet vocabulary — the inverse of what an arm
* declares. Memory rows collapse to the FIRST one; a study varying retrieval
* across several mounted memories should declare `memory.id` and read the rows
* itself rather than trust a projection to pick.
*/
function armFacetsFromManifest(manifest) {
const memory = manifest.memories?.[0];
const provider = manifest.llm?.provider;
const model = manifest.llm?.model;
const reactMode = manifest.reactMode;
const scorer = manifest.skillGraph?.scorer;
const routing = manifest.skillGraph?.routing;
const continuity = manifest.skillGraph?.continuity;
const evidenceGate = manifest.evidenceGate;
return {
...(provider !== undefined && { provider }),
...(model !== undefined && { model }),
...(isReactMode(reactMode) && { reactMode }),
...(manifest.window !== undefined && { window: manifest.window }),
...(scorer !== undefined && { scorer }),
...(isPosture(routing) && { routing }),
...(isContinuity(continuity) && { continuity }),
...(isPosture(evidenceGate) && { evidenceGate }),
...(memory !== undefined && { memory: { ...memory } }),
};
}
exports.armFacetsFromManifest = armFacetsFromManifest;
/**
* A stable label for a set of facets — sorted `key=value`, so two runs of one
* configuration produce the same string whatever order the fields were written
* in. Useful as a grouping key for N recorded runs.
*/
function armLabel(facets) {
const parts = [];
for (const [key, value] of Object.entries(facets)) {
if (value === undefined)
continue;
if (key === 'memory') {
for (const [mk, mv] of Object.entries(value)) {
if (mv !== undefined)
parts.push(`memory.${mk}=${String(mv)}`);
}
continue;
}
parts.push(`${key}=${String(value)}`);
}
return parts.sort().join(',');
}
exports.armLabel = armLabel;
/**
* Compare one arm's DECLARED facets against a run's manifest. Empty result =
* the run agreed with everything the arm claimed.
*
* Only declared facets are compared. An arm is a claim about the facets it
* names and says nothing about the rest — checking undeclared fields would fail
* every arm for differing from the manifest in ways it never claimed.
*/
function checkArmApplied(arm, manifest) {
const facets = arm.facets;
if (facets === undefined)
return [];
const out = [];
compare(out, 'provider', facets.provider, manifest.llm?.provider);
compare(out, 'model', facets.model, manifest.llm?.model);
compare(out, 'reactMode', facets.reactMode, manifest.reactMode);
compare(out, 'window', facets.window, manifest.window);
compare(out, 'skillGraph.scorer', facets.scorer, manifest.skillGraph?.scorer);
compare(out, 'skillGraph.routing', facets.routing, manifest.skillGraph?.routing);
compare(out, 'skillGraph.continuity', facets.continuity, manifest.skillGraph?.continuity);
compare(out, 'evidenceGate', facets.evidenceGate, manifest.evidenceGate);
const memory = facets.memory;
if (memory !== undefined) {
const rows = manifest.memories ?? [];
if (memory.id !== undefined) {
// Narrowed to ONE row: a missing row is a mismatch on the id itself, and
// the remaining fields are then compared against that row alone.
const row = rows.find((r) => r.id === memory.id);
if (row === undefined) {
out.push({
facet: 'memory.id',
declared: memory.id,
...(rows.length > 0 && { observed: rows.map((r) => r.id ?? '?').join(', ') }),
});
}
else {
for (const field of MEMORY_FIELDS) {
compare(out, `memory.${field}`, memory[field], row[field]);
}
}
}
else if (!rows.some((row) => MEMORY_FIELDS.every((f) => matches(memory[f], row[f])))) {
// Un-narrowed: ANY mounted memory satisfying every declared field counts.
// Reported as ONE mismatch — naming a per-field winner across rows would
// invent a memory that does not exist.
out.push({
facet: 'memory',
declared: armLabel({ memory }),
...(rows.length > 0 && {
observed: rows.map((row) => armLabel({ memory: row })).join(' | '),
}),
});
}
}
return out;
}
exports.checkArmApplied = checkArmApplied;
/**
* Which declared arm does this run belong to? The offline door: group N
* recorded runs into arms by what each run's own manifest says.
*
* Rules, all conservative:
* - only arms that DECLARE facets are candidates (an arm naming nothing matches
* every run and would swallow the whole study);
* - a candidate matches when the manifest contradicts none of its facets;
* - the MOST SPECIFIC match wins (most declared facets), and a tie returns
* `undefined` — two arms fitting one run equally well is an ambiguity, and
* guessing which the experimenter meant is exactly the bookkeeping this
* function replaces.
*/
function matchArm(arms, manifest) {
const fits = arms
.filter((arm) => (0, validate_js_1.declaredFacetCount)(arm.facets) > 0)
.filter((arm) => checkArmApplied(arm, manifest).length === 0)
.map((arm) => ({ arm, specificity: (0, validate_js_1.declaredFacetCount)(arm.facets) }))
.sort((a, b) => b.specificity - a.specificity);
if (fits.length === 0)
return undefined;
if (fits.length > 1 && fits[0].specificity === fits[1].specificity)
return undefined;
return fits[0].arm;
}
exports.matchArm = matchArm;
// ─── internals ───────────────────────────────────────────────────────
const MEMORY_FIELDS = ['type', 'strategy', 'retrieval', 'embedderId', 'flavor'];
function matches(declared, observed) {
return declared === undefined || declared === observed;
}
function compare(out, facet, declared, observed) {
if (matches(declared, observed))
return;
out.push({ facet, declared: declared, ...(observed !== undefined && { observed }) });
}
function isReactMode(value) {
return value === 'classic' || value === 'dynamic' || value === 'dynamic-grouped';
}
function isPosture(value) {
return value === 'assist' || value === 'guard' || value === 'rails';
}
function isContinuity(value) {
return value === 'turn' || value === 'conversation';
}
//# sourceMappingURL=manifest.js.map