UNPKG

agentfootprint

Version:

The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.

106 lines • 5.59 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.mountMemoryPipeline = exports.mountMemoryWrite = exports.mountMemoryRead = void 0; // NOTE on stage ordering: // This helper uses `addSubFlowChartNext`, which appends the subflow at // the current builder tail. Consumers who need the read subflow to run // BEFORE a specific agent stage should arrange the call order // accordingly — e.g., `mountMemoryPipeline(builder).addFunction('CallLLM', ...)`. // If the underlying builder gains "insert before stage id" API later, // this helper can be extended non-breakingly. const DEFAULTS = { identityKey: 'identity', turnNumberKey: 'turnNumber', contextTokensKey: 'contextTokensRemaining', injectionKey: 'memoryInjection', newMessagesKey: 'newMessages', readSubflowId: 'sf-memory-read', writeSubflowId: 'sf-memory-write', }; /** * 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). */ function mountMemoryRead(builder, config) { const identityKey = config.identityKey ?? DEFAULTS.identityKey; const turnNumberKey = config.turnNumberKey ?? DEFAULTS.turnNumberKey; const contextTokensKey = config.contextTokensKey ?? DEFAULTS.contextTokensKey; const injectionKey = config.injectionKey ?? DEFAULTS.injectionKey; const readSubflowId = config.readSubflowId ?? DEFAULTS.readSubflowId; const evidenceKey = config.evidenceKey; const identityOverride = config.identityOverride; return builder.addSubFlowChartNext(readSubflowId, config.pipeline.read, 'Load Memory', { inputMapper: (parentState) => ({ identity: identityOverride ?? parentState[identityKey], turnNumber: parentState[turnNumberKey], contextTokensRemaining: parentState[contextTokensKey], // Pass the current turn's messages through — semantic read stages // like `loadRelevant` derive the query from the last user // message here. The write-side `newMessages` field is empty // during read; these are two different concerns. // Agents carry the conversation as `history`; bare hosts may use // `messages`. Without this fallback, read stages that derive the query // from the last user message (loadSnapshot/loadRelevant) saw [] inside // an Agent and silently injected nothing — the causal READ never fired. messages: parentState.messages ?? parentState.history ?? [], newMessages: [], // write side unused in read subflow }), outputMapper: (subflowState) => ({ [injectionKey]: subflowState.formatted, // The retrieval record, lifted to the parent so it lands in the ROOT // commit log. Only present for pipelines that actually retrieve. ...(evidenceKey !== undefined && subflowState.retrieved !== undefined && { [evidenceKey]: subflowState.retrieved, }), }), }); } exports.mountMemoryRead = mountMemoryRead; /** * Mount only the WRITE subflow. No-op when the pipeline has no `write` * (e.g., ephemeral pipelines) — returns the builder unchanged. */ function mountMemoryWrite(builder, config) { if (!config.pipeline.write) return builder; const identityKey = config.identityKey ?? DEFAULTS.identityKey; const turnNumberKey = config.turnNumberKey ?? DEFAULTS.turnNumberKey; const contextTokensKey = config.contextTokensKey ?? DEFAULTS.contextTokensKey; const newMessagesKey = config.newMessagesKey ?? DEFAULTS.newMessagesKey; const writeSubflowId = config.writeSubflowId ?? DEFAULTS.writeSubflowId; const identityOverride = config.identityOverride; return builder.addSubFlowChartNext(writeSubflowId, config.pipeline.write, 'Save Memory', { inputMapper: (parentState) => ({ identity: identityOverride ?? parentState[identityKey], turnNumber: parentState[turnNumberKey], contextTokensRemaining: parentState[contextTokensKey] ?? 0, newMessages: parentState[newMessagesKey] ?? [], // Evidence bridge (#5): harvested run evidence for causal writeSnapshot. // Closure-delivered (no PARENT-scope write) — note it DOES land in the // write subflow's own tracked scope/commit log, like newMessages; the // values are already observable as events, so no new exposure class. ...(config.evidenceSource && { runEvidence: config.evidenceSource() }), }), // No outputMapper — write has no parent-visible output. }); } exports.mountMemoryWrite = mountMemoryWrite; /** * 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. */ function mountMemoryPipeline(builder, config) { return mountMemoryWrite(mountMemoryRead(builder, config), config); } exports.mountMemoryPipeline = mountMemoryPipeline; //# sourceMappingURL=mountMemoryPipeline.js.map