UNPKG

agentfootprint

Version:

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

115 lines • 5.91 kB
/** * coverage/timeLimits.ts — the time layer's limits lines for ONE turn (step T8), * composed from the record and nothing else. * * Two readers ask it, so the person and the model are told the same thing: * * - the limits block (`stages/prepareFinal.ts`), which prints the lines under * `Period` and `Clocks` for the PERSON after the answer; * - the served time line (`agent/arguments/serve.ts` · `timeLimitsSentence`), * which hands the MODEL the same lines as the library's conclusion, late, at * the decision point, before it writes the answer. * * What it reads: the facts `timeLimitFacts.ts` · `timeLimitFactsOf` reads — * the turn's `period` rows whose result checks hold, the turn's * `source-clock` rows (`time/checkRecord.ts` · `clocksDiffer`) and the * clock row's zone — rendered here (`period.ts` · `periodCheckLine`, * `clockLines`). `undefined` when there is no clock (nothing to render in) or * nothing to say. * * Loaded through `import()` only, under `.time()` (`stages/prepareFinal.ts`; * `arguments/serve.ts`, itself loaded under the arm): it renders through * `time/present.ts`, which a plain agent never loads. */ import { clockLines, periodCheckLine } from './period.js'; import { bindPresentation } from '../../time/present.js'; import { timeLimitFactsOf, } from './timeLimitFacts.js'; // LENS · late-line · request-ephemeral (and the person's limits block) // reads: nothing — a fixed clause, added when the turn's clock zone is unknown (G15) // law: names WHY the ranges are in UTC, so no reader takes UTC for the person's zone. /** The clocks line under an unknown zone (G15): the ranges are in UTC because the person's zone is not known. */ export const UNKNOWN_ZONE_LINE = "times are shown in UTC — the person's time zone is not known"; // LENS · late-line · request-ephemeral // reads: one `period` row's `held` (the declared `queried` and `held` spans its `not-held` / // `partly-held` verdict came from), rendered in the person's zone by the bound renderer // law: the library's CONCLUSION for the MODEL: the time the source holds, already in the person's // zone, beside the time the call asked about — so no model converts a source's UTC instants // (take 3: "9:22 AM–5:02 PM Pacific" for a 16:22–17:02 UTC span). /** * The model's line for a call whose source holds none or only part of the * time it asked about (`PeriodRow.held`): what the call asked about and what * its source holds, each AS DECLARED (inclusive ends, no end moved) and * rendered in the presentation zone by the time layer's one renderer — the * same spans the person's `Period:` line prints (`period.ts` · `periodLine`) — then * how much of the asked time that is. `undefined` when the row carries none. * * @example * ```ts * heldLine(row, bindPresentation({ zone: 'America/Los_Angeles' })); * // 'pscale_client_health asked about 2026-09-30 06:00:00–11:59:50 America/Los_Angeles (UTC-07:00); * // its source holds 2026-10-01 09:22:44.300–10:02:44.300 America/Los_Angeles (UTC-07:00), which * // covers none of that time' * ``` */ export function heldLine(row, presentation) { const h = row.held; if (h === undefined) return undefined; const share = row.verdict === 'partly-held' ? 'which covers only part of that time' : 'which covers none of that time'; return (`${row.toolName} asked about ${presentation.span(h.queried.from, h.queried.to)}; ` + `its source holds ${presentation.span(h.held.from, h.held.to)}, ${share}`); } /** * The turn's time limits lines, from the ledger — {@link renderTimeLimits} * over `timeLimitFacts.ts` · `timeLimitFactsOf`. `turn` undefined reads every * row (a ledger that carries one turn). `audience` `'model'` (the served time * line) names the person's window as theirs and keeps, of the clocks, only the * label that two differ. * * @example * ```ts * timeLimitLinesOf(ledger, 1); * // { period: ['client_activity read less than was asked — asked: …; read: …'], clocks: [] } * ``` */ export function timeLimitLinesOf(ledger, turn, audience = 'person') { return renderTimeLimits(timeLimitFactsOf(ledger, turn, audience), audience); } /** * The limits lines for `audience`, rendered in the facts' zone by the time * layer's one renderer (`time/present.ts` · `bindPresentation`) — * `undefined` when there are no facts or nothing to say. */ export function renderTimeLimits(facts, audience = 'person') { if (facts === undefined) return undefined; const presentation = bindPresentation({ zone: facts.zone }); const period = []; const held = []; for (const row of facts.period) { const line = periodCheckLine(row, presentation, audience); if (line !== undefined) period.push(line); // The source's held span, for the model (the person's `Period:` line already prints it). const holds = audience === 'model' ? heldLine(row, presentation) : undefined; if (holds !== undefined) held.push(holds); } // The person reads every wall-clock source; the model only the label that two differ — one // declared clock is compared as instants and changes nothing the answer states. const clocks = [ ...(audience === 'person' ? clockLines(facts.sources, facts.differ) : facts.differ === undefined ? [] : clockLines([], facts.differ)), // The ranges above are spelled in UTC only because the person's zone is unknown (G15). ...(facts.zoneUnknown === true && period.length + held.length > 0 ? [UNKNOWN_ZONE_LINE] : []), ]; if (period.length + clocks.length + held.length === 0) return undefined; return { period, clocks, ...(held.length > 0 && { held }) }; } //# sourceMappingURL=timeLimits.js.map