agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
91 lines • 4.6 kB
TypeScript
/**
* The messages-slot refusals — said in the same words wherever they are said.
*
* ── The arc this module records ──────────────────────────────────────
* 7.19.1 refused `slot: 'messages'` BY NAME. An Injection targeting the
* messages slot was RECORDED as injected — `context.injected` fired with
* its content, the slot composition counted it, the engine routed it —
* while the request assembled its message list from the conversation
* itself, so the model never saw it. The recording said one thing and the
* wire said another, and absence beat that lie.
*
* 7.21.0 delivers it, so the refusal moves: from "this placement never
* reaches the model" to "this placement reaches the model, but not with
* THAT role on THIS provider, and not at THAT position this iteration."
* The two facts the old sentence bundled together are now separate, and
* each is decided against something real:
*
* • ROLE — the wire has no system role inside the message list on the
* Anthropic family (system is a separate top-level field) while the
* OpenAI family carries it. A provider declares what it carries
* (`LLMProvider.carriesInMessages`); a role it does not carry is
* REFUSED at run start, naming the provider and the roles it does
* carry. Never silently re-roled: changing who appears to speak is a
* meaning change the app must make, not the library.
*
* • POSITION — a delivered injection goes at the END of the history the
* iteration assembled. If its role would collide with the turn already
* there, or the tail is waiting on a tool result, delivery is DEFERRED
* to the next boundary and the deferral is recorded on
* `messagesDelivery.deferred`. Never a silent drop, never a reorder,
* never a split `tool_use` / `tool_result` pair.
*
* Both refusals are stated in full sentences that name the fix, because a
* refusal that does not teach just moves the puzzle.
*/
import type { WireRole } from '../provider-contract/types.js';
/**
* The role refusal (D2), addressed from `site` (e.g.
* `defineFact('turn-time')` or the run-start check).
*
* Thrown when a `slot: 'messages'` injection declares a role the attached
* provider drops on the wire. The alternative — quietly rewriting the role
* to one that fits — would make the recording true and the meaning false.
*/
export declare function messagesRoleRefusal(args: {
readonly site: string;
readonly role: string;
readonly providerName: string;
readonly carries: readonly WireRole[];
}): string;
/**
* The role refusal for a role no wire can carry (`'tool'`).
*
* A tool message answers one specific `tool_use` id. An injection has no
* call to answer, so the message it would produce is malformed on every
* provider — this one is refused by shape, not by capability.
*/
export declare function messagesToolRoleRefusal(site: string): string;
/**
* The content refusal (8.18.0) — a declared message whose `content` is not text.
*
* The named factories (`defineFact`, `defineInstruction`, …) have always
* refused empty content; a hand-built `Injection` passed straight to
* `.injection()` went through unchecked, and the delivery stage put it on the
* wire as-is. A message with no content is not a quieter message: it becomes
* `content: undefined` in the window, crashes the next composition, and — when
* two of them are declared — collides on the delivery ledger's key so one is
* silently swallowed. Refused where the declaration is, naming the injection.
*/
export declare function messagesContentRefusal(args: {
readonly site: string;
readonly role: string;
readonly received: string;
}): string;
/** Why a delivery was held back this iteration. Recorded, never thrown. */
export type DeferralReason = 'role-collision' | 'unanswered-tool-call';
/**
* The deferral note (D3) — a sentence on `messagesDelivery.deferred`, which
* is the committed answer to "why is my declaration not on the wire yet?".
*
* Not an error: the next boundary retries it unchanged. The note names what
* blocked it so the reader does not have to reconstruct the window to find out.
*/
export declare function messagesDeferralNote(args: {
readonly injectionId: string;
readonly role: string;
readonly reason: DeferralReason;
/** The effective wire role of the turn currently at the end of the window. */
readonly tailRole?: string;
}): string;
//# sourceMappingURL=messagesSlotRefusal.d.ts.map