UNPKG

agentfootprint

Version:

The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.

122 lines • 6.2 kB
"use strict"; /** * Earlier answers' tool results that were IN FRONT OF THE MODEL when it * answered — proven by the record's witness, never by end-of-run state. * * The witness rule, in typed terms: * N = the `iteration` of the LAST `stream.llm_start` before `turn_end` (the * answering iteration). Context assembly for an iteration PRECEDES its * `iteration_start`, and `context.injected` carries no iteration of its * own, so the rule keys on the compose STAGE: * the witnesses are the `context.injected` rows with `slot: 'messages'`, * `source: 'tool-result'`, `sourceId: <toolCallId>` whose * `meta.runtimeStageId` equals that of the `context.slot_composed` with * `slot: 'messages'` and `iteration: N`. * The content is parsed from the matching `history` message (by `toolCallId`). * * Only EARLIER answers' results: `distance` (messages a PERSON wrote after the * result, up to and including the current one — `common.ts` · `isPersonEntry`, * never a frame the library wrote in the user role) must be at least 1, and a call of this run * is never "in view" — on the flagship recording the answering compose also * witnesses this run's own `get_array_inventory` result (`events[68]`), which * must never become an in-view line. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.readInView = exports.witnessesOf = exports.answeringIteration = exports.MAX_IN_VIEW = void 0; const toolBytes_js_1 = require("../../lib/toolBytes.js"); const render_js_1 = require("../render.js"); const view_js_1 = require("../view.js"); const common_js_1 = require("./common.js"); /** At most this many in-view results are listed, newest first. */ exports.MAX_IN_VIEW = 3; /** N — the answering iteration. `undefined` when no `llm_start` is recorded. */ function answeringIteration(view) { const end = view.last('agent.turn_end'); const starts = view .ofType('stream.llm_start') .filter((e) => end === undefined || e.index < end.index); return (0, view_js_1.num)(starts[starts.length - 1]?.payload.iteration); } exports.answeringIteration = answeringIteration; /** The witness rows of the answering iteration's messages compose. */ function witnessesOf(view, iteration) { if (iteration === undefined) return []; const composed = view .ofType('context.slot_composed') .filter((e) => e.payload.slot === 'messages' && e.payload.iteration === iteration); const stage = (0, view_js_1.str)(composed[composed.length - 1]?.meta.runtimeStageId); if (stage === undefined) return []; return view.ofType('context.injected').filter((e) => e.meta.runtimeStageId === stage && e.payload.slot === 'messages' && e.payload.source === 'tool-result' && // A witness whose call id the record keeps out is still a witness: a // result was in view, whose call the record will not name. ((0, view_js_1.keptOut)(e, 'sourceId') || typeof e.payload.sourceId === 'string')); } exports.witnessesOf = witnessesOf; function readInView(ctx, callIds) { const history = (0, common_js_1.historyOf)(ctx.view); const historyKeptOut = ctx.view.isStateKeptOut('history'); const windowed = typeof ctx.view.first('agent.run_configured')?.payload.window === 'string'; const reads = []; let keptOut = 0; for (const witness of witnessesOf(ctx.view, ctx.answeringIteration)) { // A call id the record keeps out is not one of this run's calls (those are // ids the run recorded, and kept): an earlier result whose call is unnamed. if ((0, view_js_1.keptOut)(witness, 'sourceId')) { keptOut += 1; continue; } const id = witness.payload.sourceId; if (callIds.has(id)) continue; if (historyKeptOut) { keptOut += 1; continue; } const historyIndex = history.findIndex((m) => (0, view_js_1.isRecord)(m) && m.role === 'tool' && m.toolCallId === id); if (historyIndex < 0) continue; const message = history[historyIndex]; const toolName = (0, view_js_1.str)(message.toolName); if (toolName === undefined) continue; const distance = history.slice(historyIndex + 1).filter((m) => (0, common_js_1.isPersonEntry)(m)).length; if (distance < 1) continue; // An EARLIER answer's result: its run's door is not in this record, so the one reader's strict // recognizers read the history text itself — the only evidence of the door that is left. The // TOOL's own bytes (`lib/toolBytes.ts` · `toolBytesOf`): a note the inputs layer appended after // a filled call's result is the library's, and read whole it breaks the parse. const rowsAt = (0, common_js_1.rowsAtOf)(ctx.declarations, toolName); const reading = (0, common_js_1.readEmptiness)((0, toolBytes_js_1.toolBytesOf)({ content: message.content, toolChars: message.toolChars }), rowsAt !== undefined ? { rowsAt } : {}); reads.push({ fact: { toolName: toolName.slice(0, common_js_1.FACT_TEXT_CHARS), toolCallId: id.slice(0, common_js_1.FACT_TEXT_CHARS), distance, windowed, emptiness: reading.emptiness, ...(0, common_js_1.countedFields)(reading), pointers: [(0, common_js_1.at)(witness, 'sourceId'), (0, common_js_1.historyAt)(historyIndex, '/toolName', id)], }, witness, historyIndex, reading, toolName, tool: (0, render_js_1.v)(toolName, 'library', (0, common_js_1.historyAt)(historyIndex, '/toolName', id), common_js_1.FACT_TEXT_CHARS), }); } // Newest first: the result nearest the current question. reads.sort((a, b) => b.historyIndex - a.historyIndex); return { all: reads, listed: reads.slice(0, exports.MAX_IN_VIEW), more: Math.max(0, reads.length - exports.MAX_IN_VIEW), keptOut, }; } exports.readInView = readInView; //# sourceMappingURL=inView.js.map