agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
269 lines • 11.5 kB
JavaScript
/**
* Row "It found" — what each call of this run returned, as the MODEL read it,
* then the earlier answers' results that were in front of the model.
*
* Branches on the call's outcome first: a call that did not run "found nothing"
* because it did not run; a withheld result is never judged empty (the model
* read the rule's refusal, not the rows). Emptiness is typed only, read by the
* one emptiness reader (`core/agent/emptiness.ts` · `readEmptiness`):
* a declared absence (or an empty rowset inside a declared boundary), a
* described result's data counted per kind, a question handed back, a
* zero-length top-level array, or the app's declared `rowsAt` (then the line
* is vouched `app`, because only the app's declaration makes it "empty").
*/
import { chip, n, v } from '../render.js';
import { at, declarationAt, emptinessSource, FACT_TEXT_CHARS, historyAt, } from './common.js';
import { isRecord, str } from '../view.js';
import { endPointer } from './calls.js';
import { anchorPointer, beforePauseMore, foldMore, heldChip, MAX_LISTED_CALLS, noResultsBeforePause, } from './checked.js';
const toolOf = (call) => call.tool;
/** The declaration pointer behind an app-counted emptiness. */
function rowsAtPointers(ctx, toolName, source) {
return source === 'app' ? [declarationAt(ctx.declarations, `tools.${toolName}.rowsAt`)] : [];
}
function foundForCall(ctx, call) {
if (call.unnamed) {
return ctx.say('found.unnamed', {
vars: { id: call.tool },
status: 'not-recorded',
missing: 'unreadable',
pointers: call.fact.pointers,
});
}
const tool = toolOf(call);
const end = call.end;
const endPointers = end ? [endPointer(end, call.emptiness)] : [];
switch (call.fact.outcome) {
case 'failed':
return ctx.say('found.failed', { vars: { tool }, pointers: endPointers });
case 'refused':
return ctx.say('found.refused', { vars: { tool }, pointers: endPointers });
case 'declined':
return ctx.say('found.declined', { vars: { tool }, pointers: endPointers });
case 'not-dispatched':
return ctx.say('found.notDispatched', { vars: { tool }, pointers: endPointers });
case 'unknown':
return ctx.say('found.unknown', {
vars: { tool },
status: 'not-recorded',
missing: 'no-event',
pointers: endPointers,
});
case 'ran':
break;
}
if (call.fact.withheldBy !== undefined && call.ruleEvent !== undefined) {
return ctx.say('found.withheld', {
vars: { tool, by: v(call.fact.withheldBy, 'library', at(call.ruleEvent, 'middleware')) },
pointers: [at(call.ruleEvent, 'moment'), at(call.ruleEvent, 'outcome')],
});
}
const reading = call.emptiness;
const basis = [emptinessSource(reading)];
const extra = rowsAtPointers(ctx, call.toolName, reading.source);
switch (reading.emptiness) {
case 'declared-absent': {
const lookedFor = call.coverage?.lookedFor;
return lookedFor !== undefined
? ctx.say('found.absent', {
vars: {
tool,
lookedFor: v(lookedFor.text, `tool:${call.fact.toolName}`, lookedFor.pointer),
},
pointers: endPointers,
})
: ctx.say('found.bare', { vars: { tool }, pointers: endPointers });
}
case 'undeclared-empty':
return ctx.say('found.undeclaredEmpty', {
vars: { tool },
basis,
pointers: [...endPointers, ...extra],
chips: [chip('undeclared-empty', 'chip.undeclaredEmpty', 'warn')],
});
case 'non-empty':
if (reading.described !== undefined && call.described !== undefined) {
return describedLines(ctx, call, reading.described);
}
return ctx.say('found.rows', {
vars: { tool, n: n(reading.rows ?? 0, emptinessSource(reading)) },
pointers: [...endPointers, ...extra],
});
case 'clarify':
return ctx.say('found.clarify', {
vars: { tool },
pointers: call.described ? [at(call.described, 'toolCallId')] : endPointers,
});
case 'unknown':
return ctx.say('found.result', { vars: { tool }, pointers: endPointers });
}
}
const DESCRIBED_LINES = {
facts: 'found.described.facts',
series: 'found.described.series',
edges: 'found.described.edges',
};
/**
* A described result: one line per kind of data, counted by the library from
* the envelope the record keeps (the `tools.semantics_declared` row — the model
* read its projection), then the tool's own source and time when it declared
* both.
*/
function describedLines(ctx, call, counts) {
const event = call.described;
const lines = [];
for (const kind of ['facts', 'series', 'edges']) {
const count = counts[kind];
if (count === undefined)
continue;
lines.push(ctx.say(DESCRIBED_LINES[kind], {
vars: { tool: toolOf(call), n: n(count) },
pointers: [at(event, 'toolCallId')],
}));
}
const semantics = event.payload.semantics;
const provenance = isRecord(semantics) ? semantics.provenance : undefined;
const source = isRecord(provenance) ? str(provenance.source) : undefined;
const measuredAt = isRecord(provenance) ? str(provenance.measured_at) : undefined;
if (source !== undefined && measuredAt !== undefined) {
const by = `tool:${call.fact.toolName}`;
lines.push(ctx.say('found.described.provenance', {
vars: {
tool: toolOf(call),
source: v(source, by, at(event, 'semantics', 'provenance', 'source')),
measuredAt: v(measuredAt, by, at(event, 'semantics', 'provenance', 'measured_at')),
},
}));
}
return lines;
}
/** The vars every in-view line shares. */
export function inViewVars(read) {
return {
tool: read.tool,
distance: n(read.fact.distance),
distanceWindowed: n(read.fact.windowed ? 1 : 0),
};
}
/** Pointers behind an in-view line: the witness, the history row, the derived emptiness. */
export function inViewPointers(ctx, read) {
return [
at(read.witness, 'sourceId'),
...(read.reading.emptiness === 'unknown'
? []
: [historyAt(read.historyIndex, '#emptiness', read.fact.toolCallId)]),
...rowsAtPointers(ctx, read.toolName, read.reading.source),
];
}
function inViewLine(ctx, read) {
const vars = inViewVars(read);
const pointers = inViewPointers(ctx, read);
switch (read.reading.emptiness) {
case 'undeclared-empty':
return ctx.say('found.inView.undeclaredEmpty', {
vars,
basis: [emptinessSource(read.reading)],
pointers,
chips: [chip('undeclared-empty', 'chip.undeclaredEmpty', 'warn')],
});
case 'declared-absent':
return ctx.say('found.inView.absent', { vars, pointers });
default:
return ctx.say('found.inView', { vars, pointers });
}
}
/** Pointers behind a line about what came back from a call before the pause. */
export function beforePausePointers(call) {
return [
historyAt(call.historyIndex, '/toolName', call.toolCallId),
...(call.reading.emptiness === 'unknown'
? []
: [historyAt(call.historyIndex, '#emptiness', call.toolCallId)]),
];
}
/**
* What came back from a call answered BEFORE the pause, as the model read it —
* the tool's own bytes in the committed history, with the door its
* `coverageDeclared` rows hold (`pausedLeg.ts`). Never "it ran": a rule's
* verdict on it is in that part's events.
*/
function foundBeforePause(ctx, call) {
const tool = call.tool;
const reading = call.reading;
const pointers = [
...beforePausePointers(call),
...rowsAtPointers(ctx, call.toolName, reading.source),
];
const chips = [heldChip()];
switch (reading.emptiness) {
case 'declared-absent': {
const lookedFor = call.coverage?.lookedFor;
return lookedFor !== undefined
? ctx.say('found.beforePause.absent', {
vars: {
tool,
lookedFor: v(lookedFor.text, `tool:${call.toolName.slice(0, FACT_TEXT_CHARS)}`, lookedFor.pointer),
},
pointers,
chips,
})
: ctx.say('found.beforePause.bare', { vars: { tool }, pointers, chips });
}
case 'undeclared-empty':
return ctx.say('found.beforePause.undeclaredEmpty', {
vars: { tool },
basis: [emptinessSource(reading)],
pointers,
chips: [chip('undeclared-empty', 'chip.undeclaredEmpty', 'warn'), ...chips],
});
case 'non-empty':
if (reading.rows !== undefined) {
return ctx.say('found.beforePause.rows', {
vars: { tool, n: n(reading.rows, emptinessSource(reading)) },
pointers,
chips,
});
}
return ctx.say('found.beforePause.result', { vars: { tool }, pointers, chips });
case 'clarify':
return ctx.say('found.beforePause.clarify', { vars: { tool }, pointers, chips });
case 'unknown':
return ctx.say('found.beforePause.result', { vars: { tool }, pointers, chips });
}
}
export function readFoundRow(ctx, calls, inView, pausedLeg) {
const lines = calls.calls.slice(0, MAX_LISTED_CALLS).flatMap((c) => foundForCall(ctx, c));
// The same "…and N more tool calls" the other call rows carry, right after the listed calls.
const more = foldMore(ctx, calls);
if (more !== undefined)
lines.push(more);
if (ctx.resumedLeg && pausedLeg.stateHeld) {
const before = pausedLeg.calls;
lines.push(...before.slice(0, MAX_LISTED_CALLS).map((c) => foundBeforePause(ctx, c)));
const moreBefore = beforePauseMore(ctx, before);
if (moreBefore !== undefined)
lines.push(moreBefore);
if (before.length === 0)
lines.push(noResultsBeforePause(ctx));
}
else if (ctx.resumedLeg) {
// No paused state in this record: nothing of the part before the pause can be read.
lines.push(ctx.say('found.beforePause', {
status: 'not-recorded',
missing: 'before-pause',
chips: [chip('before-pause', 'chip.beforePause')],
}));
}
if (!ctx.resumedLeg && calls.all.length === 0) {
lines.push(ctx.say('found.noCalls', { pointers: anchorPointer(ctx) }));
}
lines.push(...inView.listed.map((read) => inViewLine(ctx, read)));
if (inView.more > 0) {
lines.push(ctx.say('found.inView.more', {
vars: { n: n(inView.more) },
pointers: inView.all.slice(inView.listed.length).map((r) => at(r.witness, 'sourceId')),
}));
}
return lines;
}
//# sourceMappingURL=found.js.map