agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
71 lines (70 loc) • 3.4 kB
TypeScript
/**
* Messages slot subflow builder
*
* Pattern: Builder (returns a FlowChart mountable via addSubFlowChartNext).
* Role: Layer-3 context engineering. Produces InjectionRecord[] from
* the current conversation history. For LLMCall, that's one
* user message. For Agent, it's user + assistant + tool-result
* messages accumulated over iterations.
* Emits: None directly; ContextRecorder sees the writes.
*
* ── What this slot is, and is not ───────────────────────────────────
* It is the OBSERVABILITY PROJECTION of the conversation: one
* InjectionRecord per message, with the source, reason, role and
* recency a consumer needs to see what the turn was made of. It is not
* the wire. The request's message list comes from `scope.history`
* directly (`stages/callLLM.ts`), because the projection flattens away
* the LLM-protocol shape a provider needs — an assistant turn's
* `toolCalls[]`, a tool turn's `toolCallId`.
*
* Nothing enters the conversation HERE, and that is the point. Two stages
* upstream edit `scope.history` before this one reads it: the window
* strategy takes messages out (`stages/window.ts`, 7.16–7.17) and the
* delivery stage lets declared ones in (`stages/deliver.ts`, 7.21). Both run
* at the loop head, before the slots, so the projection and the wire always
* describe the same past.
*
* That is why this file no longer walks `activeInjections` looking for
* `inject.messages`. Until 7.19.1 it did, and it produced a record — an
* emitted `context.injected` — for content the request never carried, which
* is the exact bug the wire-truth test now guards. A delivered injection is
* IN the window by the time this runs, so it is projected like any other
* message and attributed to its injection by the `injectedBy` marker the
* delivery stage stamped. One wire message, one record, one name.
*/
import type { FlowChart } from 'footprintjs';
import type { ContextRole, ContextSource } from '../../events/types.js';
/**
* A single message supplied by the caller. Structurally matches the
* LLMMessage adapter type but local-aliased to keep this file free of
* adapter-layer coupling.
*/
export interface InputMessage {
readonly role: ContextRole;
readonly content: string;
readonly toolCallId?: string;
readonly toolName?: string;
/** Set by the delivery stage on a message an Injection put here — the
* marker this slot attributes by. Mirrors `LLMMessage.injectedBy`. */
readonly injectedBy?: {
readonly injectionId: string;
readonly flavor: ContextSource;
readonly reason?: string;
readonly iteration: number;
};
}
export interface MessagesSlotConfig {
/** Budget cap (chars). Default: 10000. Set from the public door as
* `contextBudget.messages` on `AgentOptions` / `LLMCallOptions`. */
readonly budgetCap?: number;
}
/**
* Build the Messages slot subflow.
*
* Mount with:
* builder.addSubFlowChartNext(SUBFLOW_IDS.MESSAGES, buildMessagesSlot(cfg), 'Messages', {
* inputMapper: (parent) => ({ messages: parent.messages, iteration: parent.iteration }),
* outputMapper: (sf) => ({ messagesInjections: sf.messagesInjections }),
* })
*/
export declare function buildMessagesSlot(config?: MessagesSlotConfig): FlowChart;