UNPKG

agentfootprint

Version:

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

119 lines • 6.6 kB
/** * semantics/described — `describedResult()`, the declaration door spelled the * way code is written. * * Pattern: one core, one authoring door (`envelope.ts` · `mintSemantics`). This file * is the camelCase door: it says how its author spells the * declaration and respells each object to the wire. It judges * nothing itself — every rule lives in the core, once. * Role: lib/ layer, pure. * Emits: N/A (the caller emits `agentfootprint.tools.semantics_declared`). * * ## One authoring spelling, one unchanged wire * * `semantic()` copied the wire's snake_case names into its declaration * (`measured_at`, `is_counter`, `filter_note`) beside camelCase ones * (`notChecked`, `cannotCover`), so an author wrote two spellings in one * object — and "semantic" already means vector retrieval everywhere else in * this library. `describedResult()` takes the same fields in camelCase and * mints the same wire, byte for byte. The wire does not change: the model and * any foreign process keep reading snake_case. * * The authoring door takes ONE spelling and refuses snake_case, quoting the key its * author wrote and naming the one meant (`provenance.measured_at` → "did you * mean `measuredAt`?"). A migration that swaps only the function name then * fails loudly at the line that needs the new spelling, instead of silently * minting without it. The declaration never accepts both spellings in one object. */ import { type SemanticIssue } from './envelope.js'; import type { DescribedResultDeclaration, SemanticProvenance, ToolSemantics } from './types.js'; /** A provenance declaration as an author writes it (camelCase) — and as the record keeps it. */ export type DeclaredProvenance = NonNullable<DescribedResultDeclaration['provenance']>; /** * Mint an author's (camelCase) `provenance` into the wire's spelling by THIS * door's rules — `absent()` asks it (honesty step 7b), so an absence says its * source and time in the shape, the spelling and the rule set a described * result does: `measuredAt` and `source` required, `ageSeconds` a finite * number ≥ 0, `sourceExportDate` a non-empty string, a snake_case key refused * naming the camelCase one. THROWS the refusal (`refused: …`) at the line the * author wrote. A fresh object, keys in the author's order. */ export declare function mintProvenance(declared: unknown): SemanticProvenance; /** What a recognizer read off an envelope's `provenance`. */ export interface ProvenanceReading { /** The record's camelCase form — present when the envelope declared a well-formed one. */ readonly provenance?: DeclaredProvenance; /** The first fault, when it declared one the record cannot carry. */ readonly problem?: SemanticIssue; } /** * Read a WIRE provenance (an absence minted anywhere — a Python helper, an * older process) into the record's camelCase form, by the same rule set — * never repaired. `{}` when none is declared (`undefined` or `null`). */ export declare function readProvenance(wire: unknown): ProvenanceReading; /** * Return rows, a series or relationships from a system of record — WITH the * caveats that make them honest — in a shape the framework recognizes, the * record keeps whole, and a build gate can refuse. * * **Where it lives.** It is the tool's RESPONSE: `execute` returns it, and * nothing is added to the system prompt or the tool's schema. The model reads * a compact projection ({@link semanticsForModel}: the data, `grain`, * `provenance`, the `not_covered` lines composed from `coverage`, a non-null * `clarify` and a static note) — never the `checked` list, the `render` hints * or the marker. The run record keeps the FULL envelope * (`agentfootprint.tools.semantics_declared`), even when the result is over * the tool's `resultCeiling` and refused. A declared `coverage` also flows * through the channel `coverage()` uses, which is the only part that can reach * the final answer (`.limitsTravelWithTheAnswer()`). * * **Values from the data.** `provenance.measuredAt` is when the WORLD was * measured, taken from the data — the export's time, the moment of a live * read, the newest sample of a series, the end of a window — never typed in. * It is never parsed: the library passes your words through. * * **The period** (honesty step 7b). `period: { queried, held, readAt? }` says * what time the READ covered — the instants it asked for, and what the store * holds (or `'unknown'`) — as ISO 8601 instants with a zone. The model reads it * as declared; the results layer compares the instants and files its verdict * (covered · partly held · not held · unknown). A malformed period is refused * here. * * Refuses (throws, at the call site — the `absent()` law) any declaration * this vocabulary cannot honor: series without `grain`, series or facts * without `provenance.measuredAt` and `provenance.source`, a counter-looking * aggregation with `isCounter` unstated, and every malformed shape. Each * refusal starts `refused: `, names the field as you spelled it, and — inside * `execute` — is the call's error result, which the model reads in place of * the data; the run continues. A snake_case key (`measured_at`, * `not_checked`) is refused, naming the camelCase one. * * Use `absent()` when nothing matched and `coverage()` for * any other value that has limits; never wrap one helper's result in another. * * @example rows from a nightly export — the time comes from the export * ```ts * const exportTime = snapshot.exportedAt; // when the WORLD was measured * return describedResult({ * facts: rows.map((r) => ({ entity: r.vm, datastore: r.datastore, size_tb: r.sizeTb })), * provenance: { measuredAt: exportTime, source: 'RVTools export' }, * coverage: { * checked: [`every VM in the RVTools export of ${exportTime}`], * cannotCover: [{ what: 'hosts that are not VMware', why: 'RVTools sees VMware only' }], * }, * }); * ``` * * @example a series — `measuredAt` is the newest sample * ```ts * const newest = rows.reduce((a, b) => (a.time > b.time ? a : b)).time; * return describedResult({ * series: rows.map((r) => ({ t: r.time, entity: r.port, metric: 'avg_iops', value: r.iops })), * grain: { interval: '30m', aggregation: 'avg', isCounter: false }, * provenance: { measuredAt: newest, source: 'InfluxDB SwitchPortStats' }, * }); * ``` */ export declare function describedResult(decl: DescribedResultDeclaration): ToolSemantics; //# sourceMappingURL=described.d.ts.map