UNPKG

agentfootprint

Version:

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

92 lines (91 loc) • 4.57 kB
/** * servedJson — a served tool result's JSON, read by the JSON grammar when the * whole text is NOT one JSON value. * * Pattern: two pure readers over the JSON grammar (no imports, no state, no * clock): `leadingJsonValues` (the complete values a text OPENS * with, and the text after them) and `jsonPrefixOf` (the leaves of * JSON text cut short, token by token, and the whole words of a * string the cut falls inside). * Role: core/ layer, `evidence/`. Asked by `evidenceIndex.ts` · * `readResult` — the ONE reading of a result that the evidence index * and the inputs layer's declared-sources check * (`resultCarries.ts` · `resultReader`) share. * Emits: N/A. * * ## Why a served result is not always one JSON value * * The dispatch loop serves a tool's value as JSON and joins framework notes * AFTER it — a stepped skill's step banner, an effect note, the repeated-call * note (`stages/toolCalls.ts`); an MCP text result joins its content blocks * with a newline (`mcp/toolResult.ts` · `readTextResult`); and a capped * result's `head` is the first characters of the tool's JSON, cut mid-value * (`toolResultCap.ts`). `JSON.parse` refuses all of these, and the text * fallback tokenises `{"id":4417,"up":true}` into `id`, `:4417`, `up`, * `:true` — so every number and boolean the tool returned read as ABSENT: the * evidence gate's false flag on an answer that quoted one, and the * declared-sources check's false `not-in-result` on a value the model took * from one (then a needless ask). So the tool's JSON is read by the JSON * grammar as far as it goes, and only what follows it is text. * * The extent of a value is found the way `findings/unsettled.ts` · * `leadingObjectOf` finds the served envelope — strings and their escapes * skipped, brackets counted — and each span is parsed once, guarded. */ /** * The complete JSON values `text` OPENS with — an object or an array, then * any further ones separated by whitespace (the blocks of an MCP text result) * — and the text after the last of them (`rest`, which starts at its first * non-space character; `''` when nothing follows). `values` is empty when the * text does not open with a complete, parseable value. * * @example * ```ts * leadingJsonValues('{"id":4417}\n{"id":2210}\n\n[identical call: …]'); * // { values: [{ id: 4417 }, { id: 2210 }], rest: '[identical call: …]' } * ``` */ export declare function leadingJsonValues(text: string): { readonly values: readonly unknown[]; readonly rest: string; }; /** What JSON text CUT SHORT reads as (`jsonPrefixOf`). */ export interface JsonPrefix { /** * Every complete string (a key or a value) and every complete number and * boolean, in order, each as the string the parsed walk would index * (`String(4417)`, `'true'`); `null` is not a leaf. */ readonly leaves: readonly string[]; /** * When the text ends inside a string: its WHOLE words — the string up to the * last space before the cut, unescaped (`"msg":"disk full on host-7 and mo` * → `disk full on host-7 and`). Text, never a leaf: it is not the whole * string. Absent when no whole word is left. */ readonly words?: string; } /** * JSON text CUT SHORT — an object or an array whose text ends before it * closes (a capped result's `head`, a result a tool truncated itself) — read * by the JSON grammar as far as it goes: its complete leaves, in order, and, * when the text ends inside a string, that string's whole words (`words`). * The token the text ends inside is never a leaf — it may be cut (`4417` cut * at `44` is not the value `44`) — and the word a cut string ends in is never * one of its words (`mo` may be `more`). * * `undefined` when the text is not JSON cut short: it does not open with `{` * or `[`, a token is not a JSON token (`[INFO] …`, `{level=info`) — the one * the text ends inside not even the beginning of one — a closing bracket does * not match, or the value CLOSES: a complete value that did not parse is not * JSON at all. Such text is read as text. * * @example * ```ts * jsonPrefixOf('{"hosts":[{"id":4417,"up":true},{"id":22'); * // { leaves: ['hosts', 'id', '4417', 'up', 'true', 'id'] } * jsonPrefixOf('{"id":4417,"msg":"disk full on host-7 and mo'); * // { leaves: ['id', '4417', 'msg'], words: 'disk full on host-7 and' } * ``` */ export declare function jsonPrefixOf(text: string): JsonPrefix | undefined;