/** * mountMemoryPipeline — mount a MemoryPipeline's read + write subflows * into an arbitrary agent flowchart. * * Given: * - a `FlowChartBuilder` the caller has been assembling, * - a `MemoryPipeline { read, write }` (typically from a preset), * - identity + turn + budget inputs sourced from agent scope, * - mount points (which stage id to insert the read subflow before), * * returns the builder with: * 1. the `read` subflow mounted BEFORE the given anchor stage, with an * inputMapper that reads identity/turn/budget from agent scope and * an outputMapper that writes `formatted` messages back, * 2. the `write` subflow (if present) appended AT THE END, with an * inputMapper that reads `newMessages` from agent scope. * * The agent's existing stages are responsible for: * - populating `identity`, `turnNumber`, `contextTokensRemaining`, * `newMessages` in scope BEFORE the relevant memory subflow runs, * - consuming the injected `formatted` messages (merge into the * agent's outgoing LLM prompt). * * This helper does NOT own any of those concerns — it owns only the * mechanical subflow mounting. Consumer-facing API * (`AgentBuilder.memoryPipeline()`) is layered on top. * * Why a standalone helper, not a direct `AgentBuilder` patch? * - Lets us test the wire mechanism end-to-end in isolation (Layer 6) * without changing the existing AgentRunner path (100+ tests). * - The wire is a small, reviewable unit; the AgentBuilder refactor is * a larger concern that can ship separately. * - Future non-Agent concepts (Swarm, Parallel) can use the same helper * — memory isn't tied to Agent conceptually. */ import type { FlowChartBuilder } from 'footprintjs'; import type { MemoryPipeline } from '../pipeline/types.js'; /** * Keys this helper reads from / writes to on the parent agent scope. * Kept as fields on the config so consumers with non-standard field names * can override without renaming scope properties. */ export interface MountMemoryPipelineConfig { /** The compiled read + write subflows from a pipeline preset. */ readonly pipeline: MemoryPipeline; /** * Scope field name the read subflow reads identity from. Default * `'identity'` — matches `MemoryState.identity`. Override when the * host flowchart uses a different name. */ readonly identityKey?: keyof ParentState & string; /** Scope field name for the turn counter. Default `'turnNumber'`. */ readonly turnNumberKey?: keyof ParentState & string; /** * Scope field name for the context-tokens-remaining signal. * Default `'contextTokensRemaining'`. */ readonly contextTokensKey?: keyof ParentState & string; /** * Scope field the read subflow writes its `formatted` output to. * Default `'memoryInjection'` — agent stages consume this to prepend * to the LLM prompt. Distinct from the pipeline's own `formatted` * field so there's no ambiguity between subflow-local and parent scope. */ readonly injectionKey?: keyof ParentState & string; /** * Scope field the write subflow reads messages to persist from. * Default `'newMessages'` — populated by the agent at turn end. */ readonly newMessagesKey?: keyof ParentState & string; /** Subflow id for the read mount. Default `'sf-memory-read'`. */ readonly readSubflowId?: string; /** Subflow id for the write mount. Default `'sf-memory-write'`. */ readonly writeSubflowId?: string; /** * Evidence source for CAUSAL pipelines (the evidence bridge, backlog #5). * Called by the write mount's inputMapper at write time; its result lands on * `MemoryState.runEvidence` so `writeSnapshot` persists real decisions / * tool calls / iterations / duration / token usage instead of zeros. The * Agent threads `causalEvidenceRecorder().collect` here automatically when a * CAUSAL memory is mounted. Omit for non-causal pipelines. */ readonly evidenceSource?: () => import('../causal/evidenceRecorder.js').RunEvidence; } /** * Mount only the READ subflow. Appends at the current builder tail, so * callers typically invoke this BEFORE their LLM-call stage: * * let b = flowChart('Seed', seedFn, 'seed'); * b = mountMemoryRead(b, { pipeline }); * b = b.addFunction('CallLLM', llmStage, 'call-llm'); // reads memoryInjection * b = mountMemoryWrite(b, { pipeline }); // persists newMessages * * Returns the same builder reference (fluent). */ export declare function mountMemoryRead(builder: FlowChartBuilder, config: MountMemoryPipelineConfig): FlowChartBuilder; /** * Mount only the WRITE subflow. No-op when the pipeline has no `write` * (e.g., ephemeral pipelines) — returns the builder unchanged. */ export declare function mountMemoryWrite(builder: FlowChartBuilder, config: MountMemoryPipelineConfig): FlowChartBuilder; /** * Convenience: mount both read and write subflows back-to-back. * Appropriate ONLY when the host flowchart has no stages between memory * read and memory write (rare — most agents have the LLM call between). * Prefer `mountMemoryRead` + stages + `mountMemoryWrite` for typical agents. */ export declare function mountMemoryPipeline(builder: FlowChartBuilder, config: MountMemoryPipelineConfig): FlowChartBuilder;