UNPKG

agentfootprint

Version:

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

120 lines • 6.58 kB
"use strict"; /** * System-Prompt slot subflow builder * * Pattern: Builder (returns a FlowChart mountable via addSubFlowChartNext). * Role: Layer-3 context engineering; inside Layer-5 primitives * (LLMCall, Agent). Ported from v1's buildSystemPromptSubflow * to InjectionRecord + SlotComposition shape. * Emits: None directly. Writes to conventional scope keys; ContextRecorder * observes and emits context.* events. * * Minimal scope for Phase 3e: static prompt string OR a dynamic function * of the input. Full SystemPromptProvider / Skill / RAG integration * arrives in Phase 5. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.buildSystemPromptSlot = void 0; const footprintjs_1 = require("footprintjs"); const conventions_js_1 = require("../../lib/conventions.js"); const types_js_1 = require("../../recorders/core/types.js"); // Writes the context recorder derives its events from — relayed under a policy. const runRedaction_js_1 = require("../../redaction/runRedaction.js"); const helpers_js_1 = require("./helpers.js"); /** * Build the System-Prompt slot subflow. * * Mount with: * builder.addSubFlowChartNext(SUBFLOW_IDS.SYSTEM_PROMPT, buildSystemPromptSlot(cfg), 'System Prompt', { * inputMapper: (parent) => ({ userMessage: parent.userMessage, iteration: parent.iteration }), * outputMapper: (sf) => ({ systemPromptInjections: sf.systemPromptInjections }), * }) */ function buildSystemPromptSlot(config) { const budgetCap = config.budgetCap ?? 4000; const reasonSource = config.reason ?? 'static system prompt'; const promptSource = config.prompt; // Dedup latch for the human-facing overflow warning (see buildToolsSlot). let warnedOverflow = false; return (0, footprintjs_1.flowChart)('Compose', async (scope) => { const args = scope.$getArgs(); const resolved = typeof promptSource === 'function' ? await promptSource(args) : promptSource; const reason = typeof reasonSource === 'function' ? reasonSource(args) : reasonSource; const injections = []; // Base prompt — `source: 'base'`. Configured at build time via // Agent.create({...}).system('...') OR LLMCall config. Baseline // LLM API flow, not context engineering. The InjectionEngine // subflow (mounted before this one) writes activeInjections[] // to scope; this slot reads them and appends Injection-derived // InjectionRecords below. if (resolved && resolved.length > 0) { injections.push({ contentSummary: (0, helpers_js_1.truncate)(resolved, 80), contentHash: (0, helpers_js_1.fnv1a)(`sp:${resolved}`), slot: 'system-prompt', source: 'base', reason, rawContent: resolved, }); } // Active Injections targeting the system-prompt slot — the // InjectionEngine subflow (mounted before this slot) wrote // `activeInjections` to scope. We filter by `inject.systemPrompt` // and append one InjectionRecord per active injection, tagged // with the Injection's `flavor` (skill / steering / instructions / // fact / etc.). ContextRecorder picks up zero-change. const activeInjections = scope.$getValue('activeInjections') ?? []; for (const inj of activeInjections) { const promptContent = inj.inject.systemPrompt; if (!promptContent || promptContent.length === 0) continue; // Block C — per-mode dispatch. Skills with surfaceMode='tool-only' // do NOT land in the system slot; their body is delivered via // the read_skill tool result instead. Other modes (system-prompt, // both, auto, or absent) keep the current v2.4 path: body lands // here. 'both' lands here AND in the tool result; the duplication // is intentional belt-and-suspenders for high-stakes skills. if (inj.flavor === 'skill' && inj.surfaceMode === 'tool-only') continue; injections.push({ contentSummary: (0, helpers_js_1.truncate)(promptContent, 80), contentHash: (0, helpers_js_1.fnv1a)(`sp:${inj.flavor}:${inj.id}:${promptContent}`), slot: 'system-prompt', source: inj.flavor, sourceId: inj.id, reason: inj.description ?? `${inj.flavor} '${inj.id}' active`, rawContent: promptContent, // Retrieval provenance (8.8.0) — present only when a retrieval // produced this injection. These are the payload's declared // `retrievalScore` / `rankPosition` / `threshold`, written for // the first time; before the recall was split per chunk there // was no single score a record could honestly carry. ...(inj.retrieval !== undefined && { retrievalScore: inj.retrieval.score, rankPosition: inj.retrieval.rank, ...(inj.retrieval.threshold !== undefined && { threshold: inj.retrieval.threshold }), }), }); } (0, runRedaction_js_1.setEventSource)(scope, conventions_js_1.INJECTION_KEYS.SYSTEM_PROMPT, injections); const composition = (0, helpers_js_1.composeSlot)('system-prompt', args.iteration ?? 1, injections, budgetCap); (0, runRedaction_js_1.setEventSource)(scope, types_js_1.COMPOSITION_KEYS.SLOT_COMPOSED, composition); // Overflow is LOUD — nothing here truncates (see buildToolsSlot). const pressure = (0, helpers_js_1.slotOverflow)(composition); if (pressure) { (0, runRedaction_js_1.setEventSource)(scope, types_js_1.COMPOSITION_KEYS.BUDGET_PRESSURE, [pressure]); if (!warnedOverflow) { warnedOverflow = true; console.warn((0, helpers_js_1.formatOverflowWarning)({ pressure, itemCount: injections.length, itemNoun: 'prompt fragment', contentNoun: 'fragments', remedy: 'Raise contextBudget.systemPrompt on the agent, or shorten the system prompt.', })); } } }, 'compose', { description: 'Compose system-prompt slot' }).build(); } exports.buildSystemPromptSlot = buildSystemPromptSlot; //# sourceMappingURL=buildSystemPromptSlot.js.map