/** * 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;