UNPKG

agentfootprint

Version:

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

130 lines • 6.22 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.toolOutcomeOf = exports.toolCallsView = exports.ledgerAtCheck = exports.appendToolCall = exports.startToolCall = exports.incompleteToolCalls = exports.MAX_TOOL_CALL_TEXT = void 0; /** Longest tool name or call id listed. A model can send any name and a * provider any id; a longer one is left out and the view marked incomplete. */ exports.MAX_TOOL_CALL_TEXT = 256; const fits = (text) => typeof text === 'string' && text.length > 0 && text.length <= exports.MAX_TOOL_CALL_TEXT; /** A ledger that may have missed calls: what ran before this run observed it. */ function incompleteToolCalls() { return { calls: [], complete: false }; } exports.incompleteToolCalls = incompleteToolCalls; // Seed starts a ledger on every run that keeps one, so a missing one at a // landing means a run resumed from a checkpoint written before ledgers // existed: what it did before the pause was not observed. const orIncomplete = (prior) => prior ?? incompleteToolCalls(); const withInFlight = (ledger, inFlight, complete) => ({ calls: [...ledger.calls], complete, ...(inFlight.length > 0 && { inFlight: [...inFlight] }), }); /** * The ledger after a call's `tool_start` bracket: the call is open until its * `tool_end` lands. Past `max` open calls, or for an oversized id, the ledger * cannot track it and says it is incomplete instead. */ function startToolCall(prior, callId, max) { const ledger = orIncomplete(prior); const inFlight = ledger.inFlight ?? []; if (inFlight.length >= max || !fits(callId)) { return ledger.complete ? withInFlight(ledger, inFlight, false) : ledger; } return withInFlight(ledger, [...inFlight, callId], ledger.complete); } exports.startToolCall = startToolCall; /** * The ledger after one call's `tool_end` bracket: the call is closed and * listed. Past `max` calls (or for an oversized name or id) nothing more is * listed and the ledger says it is incomplete, so a rule that counts can tell * "none ran" from "not listed". A call whose own work has not settled (a tool * that declared `pending`) makes it incomplete too. Returns `prior` itself * when nothing changes, so the caller can skip the write. */ function appendToolCall(prior, call, max, settled = true) { const ledger = orIncomplete(prior); const open = ledger.inFlight ?? []; const at = open.indexOf(call.callId); const inFlight = at === -1 ? open : [...open.slice(0, at), ...open.slice(at + 1)]; const complete = ledger.complete && settled; if (ledger.calls.length >= max || !fits(call.name) || !fits(call.callId)) { if (at === -1 && !ledger.complete) return ledger; return withInFlight(ledger, inFlight, false); } return { ...withInFlight(ledger, inFlight, complete), calls: [...ledger.calls, { name: call.name, callId: call.callId, outcome: call.outcome }], }; } exports.appendToolCall = appendToolCall; /** * The ledger as one check sees it, read at the moment of the check: incomplete * when there is none, when a call is still open, or when the model's last * reply asked for calls that never went out (`undispatched` — the check runs * on the terminal branch, so a call on that reply was never dispatched: a run * cut off by its iteration or cost budget with calls still pending). */ function ledgerAtCheck(ledger, undispatched) { if (ledger === undefined) return incompleteToolCalls(); // Anything off the declared shape (a placeholder where a list belongs) is // unknown, never "empty": fail closed. const wellFormed = Array.isArray(ledger.calls) && (ledger.inFlight === undefined || Array.isArray(ledger.inFlight)); if (!wellFormed) return incompleteToolCalls(); const open = ledger.inFlight?.length ?? 0; return { calls: ledger.calls, complete: ledger.complete === true && open === 0 && undispatched === 0, }; } exports.ledgerAtCheck = ledgerAtCheck; /** * The parcel one validation receives: detached from the run's state and frozen * at every level, so a check can neither edit the ledger nor reach live state * through it. Anything that is not the declared shape is dropped, never passed on. */ function toolCallsView(ledger, max) { // No ledger is not "nothing ran": a checkpoint written before the run kept // one, or a caller that passed none. Fail closed — say the list is partial. if (ledger === undefined) return Object.freeze({ calls: Object.freeze([]), complete: false }); const listed = Array.isArray(ledger.calls); const source = listed ? ledger.calls : []; const calls = []; let complete = listed && ledger.complete === true && source.length <= max; for (const raw of source.slice(0, max)) { const name = raw?.name; const callId = raw?.callId; const outcome = raw?.outcome; if (!fits(name) || !fits(callId) || (outcome !== 'ok' && outcome !== 'error' && outcome !== 'not-run')) { complete = false; continue; } calls.push(Object.freeze({ name, callId, outcome })); } return Object.freeze({ calls: Object.freeze(calls), complete }); } exports.toolCallsView = toolCallsView; /** A tool's own declared status that says it did not succeed. */ const FAILED_STATUS = new Set(['failure', 'denied', 'invalid']); /** * The outcome of one `stream.tool_end` bracket, in the view's words. `ran` is * whether the tool's own code ran: a call that did not run and carries no * `error` (a person declined it, a rule refused it, a paused batch settled it) * is `not-run`, never `ok`. A tool that ran but declared `failure`, `denied` or * `invalid` (a refused oversize result among them) is `error`. */ function toolOutcomeOf(bracket, ran) { if (bracket.error === true) return 'error'; if (bracket.notDispatched !== undefined || bracket.notExecuted === true || !ran) return 'not-run'; return bracket.status !== undefined && FAILED_STATUS.has(bracket.status) ? 'error' : 'ok'; } exports.toolOutcomeOf = toolOutcomeOf; //# sourceMappingURL=toolLedger.js.map