UNPKG

agentfootprint

Version:

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

97 lines • 4.91 kB
/** * artifactPhrases — the plain-English vocabulary the artifact / tool-result * commentary lines are built from, plus the two size humanizers. * * Why its own module (and not more branches inside `commentaryTemplates.ts`): * these are TRANSLATIONS of library unions (`ArtifactOp`, * `ArtifactRefusalReason`, `ArtifactSweepReason`) into words a non-engineer * reads. Keeping them here means a new union member is one edit in one place — * and the exhaustive `Record<Union, string>` types below make the compiler, * not a reviewer, the thing that catches the omission. (`PermissionCapability` * taught the repo what two drifting copies of one union cost.) * * LAW, inherited from the artifact events themselves: META ONLY. These * sentences may say how BIG a payload was and what KIND it was; they never * carry the payload, a digest, or a raw refusal `detail` string. A ref is an * identifier, not prose — it belongs in DETAILS, so no phrase here prints one. * * Not exported from any barrel: this is prose plumbing for the commentary * engine, not public API. */ /** * Humanize a byte count for prose. Deliberately a LOCAL copy of the same * three-branch rule `bug-report/build.ts#formatBytes` uses: the commentary * engine is a leaf that a viewer bundles on its own, and importing a * bug-report module (zip building, manifests) to borrow four lines would drag * that whole graph in behind a sentence. The shapes are pinned by tests on * both sides. */ export function humanizeBytes(bytes) { if (!Number.isFinite(bytes) || bytes < 0) return 'an unknown size'; if (bytes < 1024) return `${Math.round(bytes)} bytes`; if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; } /** * Humanize a character count for prose — `tools.result_refused` measures the * stringified result in CHARACTERS, not bytes, and saying "bytes" there would * be a small lie in the one sentence whose whole job is the true size. */ export function humanizeChars(chars) { if (!Number.isFinite(chars) || chars < 0) return 'an unknown number of characters'; const rounded = Math.round(chars); return `${rounded.toLocaleString('en-US')} character${rounded === 1 ? '' : 's'}`; } /** What was being attempted at the door that refused. Exhaustive by type. */ export const ARTIFACT_OP_PHRASES = { put: 'checking an artifact in', head: 'describing an artifact', get: 'reading an artifact', delete: 'deleting an artifact', list: 'listing the artifacts', // Not a store verb — the framework's own door, before a tool runs. dispatch: 'delivering an artifact a tool asked for', }; /** Why the door said no, in a sentence a reader can act on. Exhaustive. */ export const ARTIFACT_REFUSAL_PHRASES = { 'no-store': 'this agent has no artifact store attached', // The API's deliberate ambiguity, kept in the prose: missing, expired and // another session's artifact are ONE answer, and pretending to tell them // apart here would invent knowledge the event does not carry. 'missing-or-expired': 'the ticket named nothing this run can still read', 'unknown-parent': 'it named an earlier artifact the store does not know', 'digest-mismatch': 'the stored data did not match the checksum the ticket promised', 'invalid-input': 'the request was not well formed', 'kind-mismatch': 'the ticket pointed at a different kind of artifact than the tool asked for', }; /** Why an artifact left the store without its owner asking. Exhaustive. */ export const ARTIFACT_SWEEP_PHRASES = { ttl: 'because the lifetime stated when it was checked in ran out', 'max-bytes': 'to keep the store inside its size budget', 'max-count': 'to keep the store inside its count budget', }; /** What went wrong with an app's own filing for a turn (the artifact * hand-over). Exhaustive by type; the reply was delivered in every case. */ export const HAND_OVER_FAILURE_PHRASES = { hook: 'failed in the app’s own code', operation: 'had a store call fail', timeout: 'ran past its time limit and was cut off — anything still in flight may land later', abort: 'was cut off because the caller hung up — anything still in flight may land later', expired: 'was used after its turn had ended, and the call was refused', }; /** * Look a phrase up without ever rendering a blank. An event that arrives with * a vocabulary this build does not know (an older or newer emitter) still gets * an honest sentence: we say we do not know, rather than printing the raw * union member into prose or leaving a hole where the reason should be. */ export function phraseFor(table, value, fallback) { if (value === undefined) return fallback; return table[value] ?? fallback; } //# sourceMappingURL=artifactPhrases.js.map