UNPKG

agentfootprint

Version:

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

277 lines • 12.4 kB
"use strict"; /** * arguments/rows — the inputs layer's verdicts, as rows on the one ledger. * * Pattern: Map. One row type, its builders and its well-formedness check — * the one owner of the `argument` row kind. `findings/types.ts` · * `FindingsRow` imports the type; this folder never imports * `findings/` (the one-way law). * Role: core/ layer leaf of the inputs layer (honesty layer 2). * Emits: N/A (the Record stage emits one `agentfootprint.findings.argument` * per row through the ledger's emit half). * * ## What a row may hold * * Names, enums and counts — and, in three fields only (`value`, `proposed`, * `quote`), a value AS THE TOOL'S OWN ARGUMENT VIEW SHOWS IT * (`core/toolShownArgs.ts` · `shownArgsOf`): a key the view hides reads * `'REDACTED'`. The layer's checks run on the raw value in memory; the raw * value is never stored on a row. * * ## One row per ruled argument per call, stamped with the turn * * `turn` is `AgentState.turnNumber` when the row was filed — the conversation * turn, not the window position — because the ledger crosses turns on a * continued conversation and `iteration` restarts at 1 every run. The * standing fold reads this turn's rows only. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.argumentRowIsWellFormed = exports.sourcedRowOf = exports.shownQuote = exports.QUOTE_CHARS = exports.TIME_WINDOW_SOURCE = exports.windowRowOf = exports.argumentRowOf = exports.answeredRowOf = exports.askedRowOf = exports.shownValue = exports.HIDDEN_VALUE = exports.ARGUMENT_CHECKS_FAILED = exports.ARGUMENT_CLAIMS = exports.ARGUMENT_ASKED = exports.ARGUMENT_SOURCES = exports.ARGUMENT_MATCHED = void 0; const argumentLeaves_js_1 = require("../../../integrity/argumentLeaves.js"); /** The closed vocabularies, for the checkpoint door and the event. */ exports.ARGUMENT_MATCHED = Object.freeze([ 'quote', 'phrase', 'spelling', 'mention', ]); exports.ARGUMENT_SOURCES = Object.freeze([ 'said', 'answered', 'result', 'app', 'default', 'model', ]); exports.ARGUMENT_ASKED = Object.freeze([ 'missing', 'unverified', 'invalid-answer', ]); exports.ARGUMENT_CLAIMS = Object.freeze([ 'user', 'result', 'turn', 'app', 'assumed', 'none', ]); exports.ARGUMENT_CHECKS_FAILED = Object.freeze([ 'quote-not-found', 'composed-message', 'unknown-result', 'placed-result', 'not-in-result', 'no-earlier-turn', 'not-in-earlier-turns', 'only-in-model-answer', 'not-in-app-text', 'uncheckable', ]); // ─── Builders ─────────────────────────────────────────────────────────── /** The placeholder a hidden argument reads — the redacted mirror's own. */ exports.HIDDEN_VALUE = 'REDACTED'; /** * One value as a row may hold it: the shown view's value, printed as a string * (`String()` for a number or a boolean, JSON for anything else) and clipped. * A value the view replaced with the placeholder stays the placeholder. */ function shownValue(value) { if (typeof value === 'string') return (0, argumentLeaves_js_1.clipValue)(value); if (typeof value === 'number' || typeof value === 'boolean') return String(value); let printed; try { printed = JSON.stringify(value) ?? String(value); } catch { printed = String(value); } return (0, argumentLeaves_js_1.clipValue)(printed); } exports.shownValue = shownValue; /** The identity fields, in the one order every `argument` row is written in. */ function identityOf(who, stamp) { return { kind: 'argument', turn: stamp.turn, toolCallId: who.toolCallId, toolName: who.toolName, iteration: stamp.iteration, argument: who.argument, rule: who.rule, ...(who.period === true && { period: true }), }; } /** * The row for an argument the person is ASKED for: no `source` (nobody has * given the value yet) and no `value` (there is none). `invalid-answer` when * an answer did not fit the property's own schema and the field is asked * again — the answer itself is not kept. */ function askedRowOf(who, stamp, asked = 'missing') { return { ...identityOf(who, stamp), asked }; } exports.askedRowOf = askedRowOf; /** * The row for a value the person's ANSWER filled (`source: 'answered'`): the * value the call runs with, in the tool's own argument view; `proposed` only * when the call had carried a value the answer replaced (absent = the call * left it out); `free` when the answer came through a free-text field — a * name the person typed, which is provenance and never support. */ function answeredRowOf(who, stamp) { return { ...identityOf(who, stamp), source: 'answered', value: shownValue(who.shownValue), ...(who.shownProposed !== undefined && { proposed: shownValue(who.shownProposed) }), ...(who.free === true && { free: true }), }; } exports.answeredRowOf = answeredRowOf; /** * The row for one verdict. `proposed` is written only on a `default` row the * model sent itself — on a `model` row the value IS the proposal, and a second * copy would say nothing. */ function argumentRowOf(verdict, stamp) { return { kind: 'argument', turn: stamp.turn, toolCallId: verdict.toolCallId, toolName: verdict.toolName, iteration: stamp.iteration, argument: verdict.argument, rule: verdict.rule, ...(verdict.period === true && { period: true }), source: verdict.source, value: shownValue(verdict.shownValue), ...(verdict.source === 'default' && verdict.shownProposed !== undefined && { proposed: shownValue(verdict.shownProposed) }), }; } exports.argumentRowOf = argumentRowOf; /** * The row for a value the library filled from the turn's ONE window of the * person's (the time layer, `time/bind.ts`): from the window the person * settled in the time ask (`answered`, `matched: 'mention'`), from a `model` * reader's unconfirmed reading of their words (`said`, `matched: 'mention'` * and `reading` — it can support "not sure", never "known"), or from a window * set in a UI (`app`, `appSource: 'time.window'`). A `said` window with no * `reading` is read back only from a record an earlier version filed. The * call's `call-window` row names the window and the form. */ function windowRowOf(who, stamp) { const value = shownValue(who.shownValue); if (who.window === 'control') { return { ...identityOf(who, stamp), source: 'app', value, appSource: exports.TIME_WINDOW_SOURCE }; } if (who.window === 'answered') { return { ...identityOf(who, stamp), source: 'answered', value, matched: 'mention' }; } return { ...identityOf(who, stamp), source: 'said', value, matched: 'mention', ...(who.window === 'derived-from-reading' && { reading: true }), }; } exports.windowRowOf = windowRowOf; /** The `appSource` of a value filled from the run's `time.window` (a UI control). */ exports.TIME_WINDOW_SOURCE = 'time.window'; // ─── The declared sources' rows ───────────────────────────────────────── /** * A quote's bound on a row — the bound a basis row's one-line texts carry * (`findings/types.ts` · `PROPOSITION_CHARS`, which this leaf cannot import), * the cut STATED in the text (`…[clipped N chars]`). */ exports.QUOTE_CHARS = 240; /** A quote as a row holds it: at most `QUOTE_CHARS`, the cut stated. */ function shownQuote(quote) { if (quote.length <= exports.QUOTE_CHARS) return quote; return `${quote.slice(0, exports.QUOTE_CHARS)} …[clipped ${quote.length - exports.QUOTE_CHARS} chars]`; } exports.shownQuote = shownQuote; /** * The row for one argument the declared-sources check judged (honesty layer * 2, declared sources): the identity, the verdict's * source (or `asked: 'unverified'` — the person is asked, and nothing runs on * the model's value), the value in the tool's own view, and every field of the * claim and the check as the verdict carries it. Written in the one field order * `ArgumentRow` declares. */ function sourcedRowOf(verdict, stamp) { return { kind: 'argument', turn: stamp.turn, toolCallId: verdict.toolCallId, toolName: verdict.toolName, iteration: stamp.iteration, argument: verdict.argument, ...(verdict.rule !== undefined && { rule: verdict.rule }), ...(verdict.period === true && { period: true }), ...(verdict.source !== undefined && { source: verdict.source }), ...(verdict.asked !== undefined && { asked: verdict.asked }), ...(verdict.shownValue !== undefined && { value: shownValue(verdict.shownValue) }), ...(verdict.shownProposed !== undefined && { proposed: shownValue(verdict.shownProposed) }), claimed: verdict.claimed, ...(verdict.matched !== undefined && { matched: verdict.matched }), ...(verdict.shownQuoteText !== undefined && { quote: shownQuote(verdict.shownQuoteText) }), ...(verdict.reading === true && { reading: true }), ...(verdict.earlier === true && { earlier: true }), ...(verdict.result !== undefined && { result: verdict.result }), ...(verdict.setAside !== undefined && { setAside: verdict.setAside }), ...(verdict.argumentsFrom !== undefined && { argumentsFrom: verdict.argumentsFrom }), ...(verdict.appSource !== undefined && { appSource: (0, argumentLeaves_js_1.clipValue)(verdict.appSource) }), ...(verdict.coincides !== undefined && { coincides: verdict.coincides }), ...(verdict.malformed !== undefined && verdict.malformed > 0 && { malformed: verdict.malformed }), ...(verdict.failed !== undefined && { failed: verdict.failed }), }; } exports.sourcedRowOf = sourcedRowOf; // ─── Well-formed ──────────────────────────────────────────────────────── const isIn = (value, vocabulary) => typeof value === 'string' && vocabulary.includes(value); const optionalIn = (value, vocabulary) => value === undefined || isIn(value, vocabulary); const optionalString = (value) => value === undefined || typeof value === 'string'; const optionalTrue = (value) => value === undefined || value === true; /** * Whether a value read from a stored checkpoint is an `argument` row this * library could have written: the identity strings, a numeric `iteration` and * `turn`, a `source` or an `asked` in its vocabulary, and every enum field in * its own. The checkpoint door's `argument` arm (`core/runCheckpoint.ts`). */ function argumentRowIsWellFormed(r) { return (r.kind === 'argument' && typeof r.toolCallId === 'string' && typeof r.toolName === 'string' && typeof r.argument === 'string' && typeof r.iteration === 'number' && typeof r.turn === 'number' && (isIn(r.source, exports.ARGUMENT_SOURCES) || isIn(r.asked, exports.ARGUMENT_ASKED)) && optionalIn(r.source, exports.ARGUMENT_SOURCES) && optionalIn(r.asked, exports.ARGUMENT_ASKED) && optionalIn(r.rule, ['ask', 'assume']) && optionalIn(r.claimed, exports.ARGUMENT_CLAIMS) && optionalIn(r.matched, exports.ARGUMENT_MATCHED) && optionalIn(r.setAside, ['open', 'noise', 'ruled-out']) && optionalIn(r.argumentsFrom, ['listed', 'unlisted']) && optionalIn(r.coincides, ['person', 'result', 'app']) && optionalIn(r.failed, exports.ARGUMENT_CHECKS_FAILED) && optionalString(r.value) && optionalString(r.proposed) && optionalString(r.quote) && optionalString(r.result) && optionalString(r.appSource) && optionalTrue(r.period) && optionalTrue(r.reading) && optionalTrue(r.earlier) && optionalTrue(r.free) && (r.malformed === undefined || typeof r.malformed === 'number')); } exports.argumentRowIsWellFormed = argumentRowIsWellFormed; //# sourceMappingURL=rows.js.map