UNPKG

agentfootprint

Version:

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

162 lines • 6 kB
"use strict"; /** * contextEngineering(agent) — first-class handle on the engineered * subset of `context.injected` events. * * The Block A8 piece. agentfootprint already emits `context.injected` * for EVERY piece of content that lands in a slot — including the * baseline flow (the user message, every tool result, the static * system prompt). For a developer who wants to inspect what their * RAG / Skills / Memory / Instructions / Steering / Facts ARE * INJECTING (the actual context-engineering work), the baseline flow * is noise. * * `contextEngineering(agent)` filters the stream to ONLY the * engineered injections and gives consumers two cleaner subscriptions: * * - `onEngineered(cb)` — fires for `source` ∈ {rag, skill, memory, * instructions, steering, fact, custom}. The actual * context-engineering work. * - `onBaseline(cb)` — fires for `source` ∈ {user, tool-result, * assistant, base, registry}. The baseline message-history flow. * * Use cases: * - **Lens UI**: render only engineered injections in the "context * bin"; show the baseline flow as edges between iterations. * - **Eval pipelines**: count how many RAG chunks vs Memory entries * vs Skill bodies entered the prompt for an eval-set query. * - **Cost attribution**: sum tokens by `source` to know what * fraction of spend is RAG vs Skills vs baseline. * - **Debug logging**: tail just the engineered signals to spot * surprising activations during dev. * * Pattern: Strategy + Filter (GoF) — pure classifier function over * the existing `context.injected` event payload, paired with * a thin subscription helper. * * Role: Layer-2 (event taxonomy) consumer-side helper. Doesn't * emit new events; doesn't change the agent's flowchart. * Pure observation. * * @example * import { contextEngineering } from 'agentfootprint'; * * const ce = contextEngineering(agent); * ce.onEngineered((e) => { * console.log(`[${e.payload.source}] ${e.payload.contentSummary}`); * }); * ce.onBaseline((e) => { * console.log(`[baseline:${e.payload.source}]`); * }); * * await agent.run({ message: 'help me' }); * // ... runs; engineered + baseline streams fire separately * * ce.detach(); // stops both subscriptions; the agent itself is fine */ Object.defineProperty(exports, "__esModule", { value: true }); exports.contextEngineering = exports.isBaselineSource = exports.isEngineeredSource = exports.BASELINE_SOURCES = exports.ENGINEERED_SOURCES = void 0; /** * Public set of "engineered" sources — the context-engineering * primitives that consumers configure (RAG, Skills, Memory, * Instructions, Steering, Facts) plus user-defined `custom`. * * Frozen so consumers can `.has(value)` directly without copy. */ exports.ENGINEERED_SOURCES = new Set([ 'rag', 'skill', 'memory', 'instructions', 'evidence-recovery', 'steering', 'fact', 'custom', ]); /** * Public set of "baseline" sources — the message-history flow that * exists regardless of context engineering: user input, tool results, * assistant outputs, the always-on system prompt anchor (`base`), and * the agent's static tool registry advertisement (`registry`). */ exports.BASELINE_SOURCES = new Set([ 'user', 'tool-result', 'assistant', 'base', 'registry', ]); /** * Pure classifier: given a `ContextSource`, is it engineered? * * Useful for ad-hoc filtering on a raw `agent.on('agentfootprint.context.injected', ...)` * subscription when you don't need the wrapper helper. */ function isEngineeredSource(source) { return exports.ENGINEERED_SOURCES.has(source); } exports.isEngineeredSource = isEngineeredSource; /** * Pure classifier: given a `ContextSource`, is it baseline? */ function isBaselineSource(source) { return exports.BASELINE_SOURCES.has(source); } exports.isBaselineSource = isBaselineSource; /** * Wrap a runner's `agentfootprint.context.injected` stream into two * filtered subscriptions: engineered + baseline. Multiple listeners * per stream are allowed; `detach()` removes all of them. * * The classifier inspects `event.payload.source`. Unknown sources * (forward-compat: `ContextSource` is open-extensible) are routed * to NEITHER stream — preferring under-firing over miscategorizing. * Use `agent.on('agentfootprint.context.injected', ...)` directly * if you need to observe sources that aren't (yet) classified. */ function contextEngineering(agent) { const unsubscribers = []; function onEngineered(listener) { const wrapped = (e) => { if (isEngineeredSource(e.payload.source)) listener(e); }; const unsub = agent.on('agentfootprint.context.injected', wrapped); unsubscribers.push(unsub); return () => { unsub(); const idx = unsubscribers.indexOf(unsub); if (idx >= 0) unsubscribers.splice(idx, 1); }; } function onBaseline(listener) { const wrapped = (e) => { if (isBaselineSource(e.payload.source)) listener(e); }; const unsub = agent.on('agentfootprint.context.injected', wrapped); unsubscribers.push(unsub); return () => { unsub(); const idx = unsubscribers.indexOf(unsub); if (idx >= 0) unsubscribers.splice(idx, 1); }; } function detach() { for (const unsub of unsubscribers) { try { unsub(); } catch { // Defensive: a misbehaving subscription's throw should not // prevent detaching the rest. } } unsubscribers.length = 0; } return { onEngineered, onBaseline, detach }; } exports.contextEngineering = contextEngineering; //# sourceMappingURL=contextEngineering.js.map