/** * absent — an absence that names its own coverage. * * Pattern: minted by a helper, RECOGNIZED by the framework (the tool-effects * envelope precedent). A return shape the framework does not * understand is a convention, and a convention cannot set a status, * keep a value out of the evidence corpus, or stop a retry loop. * Role: core/ layer, pure. The dispatch loop calls `readAbsence` at the * execute boundary; `absent()` is what a tool author writes. * Emits: N/A (the caller emits `agentfootprint.tools.absent`). * * ## The direction-of-error argument — the whole point of this file * * A tool that finds nothing returns *something*: an empty array, a `null`, a * sentence. From any of those a model cannot tell **"I looked and there is * nothing"** from **"I could not look"**. That confusion is not symmetric, * and the asymmetry is what makes it worth a primitive: * * • a *nothing-found* misread as an *outage* sends someone to investigate a * collector that is working perfectly — expensive, and self-correcting. * • an *outage* misread as *nothing-found* declares a system healthy that * was never checked — cheap, silent, and wrong in the direction that * hurts. * * So the two must not share a shape. An error is a `role: 'tool'` result with * `error: true`, a message, and no coverage. An absence is this: a result * that RAN, carries the ground it covered, and says out loud that asking * again changes nothing. * * ## Why the note is never interpolated * * Tool results are the evidence gate's corpus, so an absence that echoed the * model's own arguments into that corpus would GROUND every identifier a model * invented, as long as it handed the invention to one tool that found nothing. * That is laundering an invention through a failed lookup — the same bug * `evidence/frames.ts` exists to stop on the other side of the conversation. * * The gate therefore withholds `looked_for`, and only `looked_for` (see * `coverage/evidence.ts`): it is the one field whose job is to quote the * request. Everything else the absence carries — the coverage lists, the * author's `tryInstead`, any extra key the tool attached to the envelope — is * the TOOL speaking about the world and does ground, because an answer that * follows the absence's own advice must not be called ungrounded for doing so. * The note is kept free of interpolation for the same reason the exclusion * exists: static library text has nothing of the caller's to leak. */ import type { AbsenceDeclaration, Coverage, ToolAbsence } from './types.js'; /** * The reserved key that makes an absence recognizable. Exported because tests, * docs and any consumer inspecting a raw tool result match on it — and * because a reserved word on the wire has to be nameable. */ export declare const ABSENCE_MARKER = "af_absent"; /** * The static sentence every absence carries. Says the three things the field * implementation proved a model needs: that the call SUCCEEDED, that nothing * was substituted, and that a retry is futile. The third clause is the one * that ends the loop. */ export declare const ABSENCE_NOTE: string; /** * Say "I looked, and there is nothing" in a way a model cannot read as a * failure — and cannot productively retry. * * Returns the value a tool's `execute` should return. The framework * recognizes it at the dispatch boundary and gives it a delivered status of * `'absent'` (routable by `onToolStatus`), a `tools.absent` event, and an * evidence-corpus rule of its own. * * @example a port-lookup tool that found no matching FLOGI * defineTool({ * name: 'flogi_for_port', * description: 'FLOGI entries for one interface', * inputSchema: { type: 'object', properties: { switch: { type: 'string' }, * port: { type: 'string' } }, required: ['switch', 'port'] }, * execute: ({ switch: sw, port }) => { * const rows = fcns.flogi(sw, port); * if (rows.length > 0) return rows; * return absent({ * what: `FLOGI entries on ${port}`, * checked: [ * `${sw}: the live fcns database`, * { what: 'window: the last 24h', why: 'FLOGI history retention on this fabric' }, * ], * notChecked: [{ what: 'the archived FLOGI history', why: 'older than the 24h window' }], * cannotCover: [{ what: 'ports on the peer fabric', * why: 'this collector is scoped to one fabric' }], * tryInstead: 'Ask for a different interface, or query the peer fabric by name.', * }); * }, * }); */ export declare function absent(decl: AbsenceDeclaration): ToolAbsence; /** * Recognize (or decline to recognize) a value as an absence — STRICT, and the * strictness is the zero-cost guarantee. Only a plain object whose * `af_absent` is exactly `true` and whose `checked` is a non-empty array * qualifies; every other value any tool has ever returned takes the path it * always took, byte for byte. * * `undefined` means "not an absence", never "a malformed one" — this library * does not guess at a shape it did not mint. */ export declare function readAbsence(value: unknown): ToolAbsence | undefined; /** The absence's coverage, in the normalized three-list shape everything * downstream (the event, the answer block) reads. */ export declare function coverageOfAbsence(absence: ToolAbsence): Coverage;