agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
392 lines • 16.7 kB
JavaScript
"use strict";
/**
* Rows "It checked" and "It did not check" — one block per tool call of THIS
* run, in call order, from what each tool DECLARED (`tools.absent` /
* `tools.coverage_declared`). An item prints its declared `short` form, else its
* full `what`, verbatim. More than five calls fold into `row.more@1`.
*
* A resumed leg never says "did not run any tools": the calls answered before
* the pause are read from the committed state this record holds
* (`pausedLeg.ts` · `readPausedLeg`) — each with what its tool declared, from
* its `coverageDeclared` rows — and, when the record holds no committed
* history, said to be out of this record.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.readCheckedRows = exports.foldMore = exports.noResultsBeforePause = exports.beforePauseMore = exports.anchorPointer = exports.heldChip = exports.declaredBeforePause = exports.MAX_LISTED_CALLS = void 0;
const render_js_1 = require("../render.js");
const calls_js_1 = require("./calls.js");
const common_js_1 = require("./common.js");
/** Calls listed per row before folding. */
exports.MAX_LISTED_CALLS = 5;
const toolOf = (call) => call.tool;
const declaredOf = (call) => ({
...(call.coverage !== undefined && { coverage: call.coverage }),
tool: call.tool,
toolName: call.fact.toolName,
pointers: call.fact.pointers,
});
/** A call answered before the pause, as its declaration's lines read it. */
const declaredBeforePause = (call) => ({
...(call.coverage !== undefined && { coverage: call.coverage }),
tool: call.tool,
toolName: call.toolName.slice(0, common_js_1.FACT_TEXT_CHARS),
pointers: [(0, common_js_1.historyAt)(call.historyIndex, '/toolName', call.toolCallId)],
});
exports.declaredBeforePause = declaredBeforePause;
/** The chip every line about the part before the pause carries, read from the state. */
const heldChip = () => (0, render_js_1.chip)('before-pause', 'chip.beforePause.held');
exports.heldChip = heldChip;
/** The pointer that shows the rule that refused a call. */
function rulePointer(call) {
const e = call.ruleEvent;
if (e === undefined)
return undefined;
if (e.type.endsWith('middleware.decision'))
return (0, common_js_1.at)(e, 'middleware');
if (e.type.endsWith('permission.check'))
return (0, common_js_1.at)(e, 'policyRuleId');
if (e.type.endsWith('checkin.decision'))
return (0, common_js_1.at)(e, 'approved');
return (0, common_js_1.at)(e, 'notExecuted');
}
function itemLines(ctx, call, section, id) {
const source = `tool:${call.toolName}`;
const declared = call.coverage?.items.filter((i) => i.section === section) ?? [];
// Items whose words the record keeps out: counted on one line, never printed.
const kept = declared.filter((i) => i.keptOut === true);
const all = declared.filter((i) => i.keptOut !== true);
const items = [];
for (const item of all) {
if (!(0, common_js_1.takeItem)(ctx, Math.min((item.short ?? item.what).length, render_js_1.MAX_VAR_CHARS)))
break;
items.push(item);
}
const lines = items.map((item) => {
const chips = item.kind === 'existence'
? [(0, render_js_1.chip)('kind', 'chip.kind.existence', 'warn')]
: item.kind === 'scope'
? [(0, render_js_1.chip)('kind', 'chip.kind.scope')]
: [];
const kindPointer = item.kind !== undefined ? [(0, calls_js_1.itemAt)(item, 'kind')] : [];
return item.short !== undefined
? ctx.say(id.short, {
vars: { short: (0, render_js_1.v)(item.short, source, (0, calls_js_1.itemAt)(item, 'short')), tool: call.tool },
pointers: [(0, calls_js_1.itemAt)(item, 'what'), ...kindPointer],
chips,
item: true,
})
: ctx.say(id.full, {
vars: { what: (0, render_js_1.v)(item.what, source, (0, calls_js_1.itemAt)(item, 'what')), tool: call.tool },
pointers: kindPointer,
chips,
item: true,
});
});
if (kept.length > 0)
lines.push(ctx.say('items.keptOut', {
vars: { n: (0, render_js_1.n)(kept.length) },
status: 'not-recorded',
missing: 'redacted',
pointers: kept.slice(0, exports.MAX_LISTED_CALLS).map((item) => (0, calls_js_1.itemAt)(item, 'what')),
item: true,
}));
const omitted = all.length - items.length;
if (omitted > 0)
lines.push(ctx.say('items.more', {
vars: { n: (0, render_js_1.n)(omitted) },
status: 'recorded',
pointers: call.coverage ? [(0, calls_js_1.coverageHead)(call.coverage)] : [],
item: true,
}));
return lines;
}
/** The one line an unnamed call gets: it is in the record, and no event names its tool. */
function unnamedLine(ctx, call) {
return ctx.say('checked.unnamed', {
vars: { id: call.tool },
status: 'not-recorded',
missing: 'unreadable',
pointers: call.fact.pointers,
});
}
function checkedBlock(ctx, call) {
if (call.unnamed)
return [unnamedLine(ctx, call)];
const tool = toolOf(call);
const by = call.fact.refusedBy;
const rule = rulePointer(call);
const endPointers = call.fact.pointers;
switch (call.fact.outcome) {
case 'failed':
return [
ctx.say('checked.failed', {
vars: { tool },
pointers: call.end ? [(0, common_js_1.at)(call.end, 'error')] : [],
}),
];
case 'refused':
return [
by !== undefined && rule !== undefined
? ctx.say('checked.refused', { vars: { tool, by: (0, render_js_1.v)(by, 'library', rule) } })
: ctx.say('checked.refused.unnamed', {
vars: { tool },
pointers: rule ? [rule] : endPointers,
}),
];
case 'declined':
return [
ctx.say('checked.declined', { vars: { tool }, pointers: rule ? [rule] : endPointers }),
];
case 'not-dispatched':
return [ctx.say('checked.notDispatched', { vars: { tool }, pointers: endPointers })];
case 'unknown':
return [
ctx.say('checked.unknown', {
vars: { tool },
status: 'not-recorded',
missing: 'no-event',
pointers: endPointers,
}),
];
case 'ran': {
const checked = call.coverage?.items.filter((i) => i.section === 'checked') ?? [];
if (call.coverage === undefined) {
return [
ctx.say('checked.undeclared', {
vars: { tool },
pointers: endPointers,
chips: [(0, render_js_1.chip)('not-declared', 'chip.notDeclared')],
}),
];
}
if (checked.length === 0) {
return [
ctx.say('checked.silent', { vars: { tool }, pointers: [(0, calls_js_1.coverageHead)(call.coverage)] }),
];
}
return [
ctx.say('checked.declared', {
vars: { tool },
pointers: [(0, calls_js_1.coverageHead)(call.coverage)],
chips: [(0, render_js_1.chip)('declared', 'chip.declared')],
}),
...itemLines(ctx, declaredOf(call), 'checked', {
short: 'checked.item',
full: 'checked.item.full',
}),
];
}
}
}
/**
* The "It checked" block of a call answered BEFORE the pause — what its tool
* declared, read from its `coverageDeclared` rows (`pausedLeg.ts`). Whether it
* failed or was refused is in that part's events, so the line says what the
* model read, never that the tool "ran".
*/
function checkedBeforePauseBlock(ctx, call) {
const d = (0, exports.declaredBeforePause)(call);
const checked = call.coverage?.items.filter((i) => i.section === 'checked') ?? [];
if (call.coverage === undefined) {
return [
ctx.say('checked.beforePause.undeclared', {
vars: { tool: d.tool },
pointers: d.pointers,
chips: [(0, render_js_1.chip)('not-declared', 'chip.notDeclared'), (0, exports.heldChip)()],
}),
];
}
if (checked.length === 0) {
return [
ctx.say('checked.beforePause.silent', {
vars: { tool: d.tool },
pointers: [(0, calls_js_1.coverageHead)(call.coverage)],
chips: [(0, exports.heldChip)()],
}),
];
}
return [
ctx.say('checked.beforePause.declared', {
vars: { tool: d.tool },
pointers: [(0, calls_js_1.coverageHead)(call.coverage)],
chips: [(0, render_js_1.chip)('declared', 'chip.declared'), (0, exports.heldChip)()],
}),
...itemLines(ctx, d, 'checked', { short: 'checked.item', full: 'checked.item.full' }),
];
}
function notCheckedBlock(ctx, call, extra = []) {
const tool = call.tool;
if (call.coverage === undefined) {
return [
ctx.say('notChecked.undeclared', {
vars: { tool },
pointers: call.pointers,
chips: [(0, render_js_1.chip)('not-declared', 'chip.notDeclared'), ...extra],
}),
];
}
const head = (0, calls_js_1.coverageHead)(call.coverage);
const lines = [];
const has = (section) => (call.coverage?.items ?? []).some((i) => i.section === section);
if (has('notChecked')) {
lines.push(ctx.say('notChecked.declared', {
vars: { tool },
pointers: [head],
chips: [(0, render_js_1.chip)('declared', 'chip.declared'), ...extra],
}), ...itemLines(ctx, call, 'notChecked', {
short: 'notChecked.item',
full: 'notChecked.item.full',
}));
}
else {
lines.push(ctx.say('notChecked.silent', { vars: { tool }, pointers: [head], chips: [...extra] }));
}
if (has('cannotCover')) {
lines.push(ctx.say('cannotCover.declared', {
vars: { tool },
pointers: [head],
chips: [(0, render_js_1.chip)('declared', 'chip.declared'), ...extra],
}), ...itemLines(ctx, call, 'cannotCover', {
short: 'cannotCover.item',
full: 'cannotCover.item.full',
}));
}
const other = call.coverage.tryInsteadTool;
if (other !== undefined) {
lines.push(ctx.say('tryInstead.tool', {
vars: {
tool,
other: (0, render_js_1.v)(other.tool, `tool:${call.toolName}`, other.pointer, common_js_1.FACT_TEXT_CHARS),
},
}));
}
return lines;
}
/** The anchor a "nothing ran" line points at: the end of the turn, else its start. */
function anchorPointer(ctx) {
const anchor = ctx.view.last('agent.turn_end') ?? ctx.view.first('agent.turn_start') ?? ctx.view.events[0];
return anchor === undefined
? []
: [{ kind: 'event', index: anchor.index, type: anchor.type, path: '#meta/runId' }];
}
exports.anchorPointer = anchorPointer;
/** "…and N more tool calls from before the pause." — past `MAX_LISTED_CALLS` of them. */
function beforePauseMore(ctx, before) {
const hidden = before.length - exports.MAX_LISTED_CALLS;
if (hidden <= 0)
return undefined;
return ctx.say('beforePause.more', {
vars: { n: (0, render_js_1.n)(hidden) },
pointers: before
.slice(exports.MAX_LISTED_CALLS)
.map((c) => (0, common_js_1.historyAt)(c.historyIndex, '/toolName', c.toolCallId)),
chips: [(0, exports.heldChip)()],
});
}
exports.beforePauseMore = beforePauseMore;
/**
* The committed history holds no tool result from before the pause. Said as
* exactly that — never "nothing ran": a window strategy may have dropped an
* earlier result, and the record cannot tell the two apart. A history the
* record keeps out (a redaction policy's placeholder) is said to be kept out.
*/
function noResultsBeforePause(ctx) {
if (ctx.view.isStateKeptOut('history')) {
return ctx.say('beforePause.keptOut', {
status: 'not-recorded',
missing: 'redacted',
chips: [(0, exports.heldChip)()],
});
}
return ctx.say('beforePause.noResults', {
status: 'not-recorded',
missing: 'before-pause',
chips: [(0, exports.heldChip)()],
});
}
exports.noResultsBeforePause = noResultsBeforePause;
function foldMore(ctx, calls) {
const hidden = Math.max(0, calls.calls.length - exports.MAX_LISTED_CALLS) + calls.omitted;
if (hidden === 0)
return undefined;
const pointers = calls.calls.slice(exports.MAX_LISTED_CALLS).flatMap((c) => c.fact.pointers.slice(0, 1));
return ctx.say('row.more', {
vars: { n: (0, render_js_1.n)(hidden) },
pointers,
status: pointers.length > 0 ? 'recorded' : 'not-recorded',
});
}
exports.foldMore = foldMore;
function readCheckedRows(ctx, calls, pausedLeg) {
const listed = calls.calls.slice(0, exports.MAX_LISTED_CALLS);
const checked = listed.flatMap((c) => checkedBlock(ctx, c));
// Listed: what the row PRINTS. Whether any call ran is a judgement — over every call,
// on the outcome alone, named or not (R2-B1).
const ranListed = listed.filter((c) => c.fact.outcome === 'ran');
const ranAll = calls.all.filter((c) => c.fact.outcome === 'ran');
const notChecked = ranListed.flatMap((c) => c.unnamed
? [
ctx.say('notChecked.unnamed', {
vars: { id: c.tool },
status: 'not-recorded',
missing: 'unreadable',
pointers: c.fact.pointers,
}),
]
: notCheckedBlock(ctx, declaredOf(c)));
if (ctx.resumedLeg && pausedLeg.stateHeld) {
// The part before the pause, read from the committed state this record holds.
const before = pausedLeg.calls;
const shown = before.slice(0, exports.MAX_LISTED_CALLS);
checked.push(...shown.flatMap((c) => checkedBeforePauseBlock(ctx, c)));
notChecked.push(...shown.flatMap((c) => notCheckedBlock(ctx, (0, exports.declaredBeforePause)(c), [(0, exports.heldChip)()])));
const more = beforePauseMore(ctx, before);
if (more !== undefined) {
checked.push(more);
notChecked.push(more);
}
if (before.length === 0) {
checked.push(noResultsBeforePause(ctx));
if (notChecked.length === 0)
notChecked.push(noResultsBeforePause(ctx));
}
}
else if (ctx.resumedLeg) {
// No committed history in this record: nothing of the part before the pause can be read.
checked.push(ctx.say('checked.beforePause.none', {
status: 'not-recorded',
missing: 'before-pause',
chips: [(0, render_js_1.chip)('before-pause', 'chip.beforePause')],
}));
notChecked.push(ctx.say('notChecked.beforePause', {
status: 'not-recorded',
missing: 'before-pause',
chips: [(0, render_js_1.chip)('before-pause', 'chip.beforePause')],
}));
}
else if (calls.all.length === 0) {
checked.push(ctx.say('checked.noCalls', { pointers: anchorPointer(ctx) }));
notChecked.push(ctx.say('notChecked.noCalls', { status: 'not-applicable', pointers: anchorPointer(ctx) }));
}
else if (ranAll.length === 0) {
notChecked.push(ctx.say('notChecked.noneRan', {
status: 'not-applicable',
pointers: calls.all.flatMap((c) => c.fact.pointers.slice(-1)),
}));
}
else if (notChecked.length === 0) {
// Calls ran, but none of the LISTED ones did: say where they are, never "none ran".
notChecked.push(ctx.say('notChecked.notListed', {
vars: { n: (0, render_js_1.n)(ranAll.length) },
pointers: ranAll.flatMap((c) => c.fact.pointers.slice(0, 1)),
}));
}
const more = foldMore(ctx, calls);
return {
checked,
notChecked,
...(more !== undefined && { checkedMore: more, notCheckedMore: more }),
};
}
exports.readCheckedRows = readCheckedRows;
//# sourceMappingURL=checked.js.map