agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
153 lines • 6.66 kB
JavaScript
/**
* Memory subsystem — public type surface.
*
* THE 2D mental model the library teaches:
*
* MEMORY = TYPE × STRATEGY × STORE
*
* TYPE — what shape of memory you're keeping
* (Episodic messages / Semantic facts / Narrative beats /
* Causal footprintjs snapshots)
* STRATEGY — how to fit content into the next LLM call
* (Window / Budget / Summarize / TopK / Extract / Decay / Hybrid)
* STORE — where the bytes live
* (InMemoryStore / Redis / Postgres / DynamoDB / Vector ...)
*
* Strategy is universal — same Window works for Episodic and for Causal.
* That's why examples are organized by strategy (the discipline) not by
* type (the shape).
*
* Pattern: Single-Source-of-Truth const objects + discriminated union.
* Mirrors `src/lib/conventions.ts` (SUBFLOW_IDS, INJECTION_KEYS).
* NEVER enums (TS enums emit runtime objects + opacity).
* Const-as-const erases at compile time, accepts string literals,
* and gives consumers IDE autocomplete + refactor safety.
*
* Role: Layer-1 contract for the memory subsystem. Step 2's
* `defineMemory()` factory consumes these to build pipelines;
* Step 4's `Agent.memory()` builder mounts the resulting
* definitions; Step 5's Causal machinery extends them.
*
* Emits: Indirectly — every memory pipeline emits the unified
* `agentfootprint.context.injected` event with `source: 'memory'`
* when its read subflow places content into the system-prompt
* slot (every shipped formatter writes `role: 'system'`, and
* system-role recall composes into `inject.systemPrompt`).
*
* @see ./define.ts for the `defineMemory()` factory itself
* @see ../../docs-next for guides + the 7 strategy examples
* @see MEMORY.md for the load-bearing design memory
*/
// ─── Const-objects (SSOT) ───────────────────────────────────────────
/**
* What shape of memory you're keeping.
*
* - `EPISODIC` — raw conversation messages, replayed on next turn
* - `SEMANTIC` — extracted structured facts, deduped on key
* - `NARRATIVE` — beats / summaries of prior runs (append-only)
* - `CAUSAL` — footprintjs execution snapshots, the differentiator
* (replays stored decisions + tool evidence for "why?"
* follow-ups — harvested automatically per run)
*/
export const MEMORY_TYPES = {
EPISODIC: 'episodic',
SEMANTIC: 'semantic',
NARRATIVE: 'narrative',
CAUSAL: 'causal',
};
/**
* How content is selected / compressed for the next LLM call.
*
* A `WINDOW` strategy on an Episodic store keeps the last N messages; on
* Semantic / Narrative it keeps the last N facts / beats. NOT universal: the
* `CAUSAL` type accepts ONLY `TOP_K` — its snapshots are matched semantically
* against the new query, never by recency, so `buildCausalPipeline` throws on
* any other strategy kind. Mix and match the non-Causal types.
*
* These are BARE STRINGS, which is enough to write one and not enough to
* OFFER one. `listMemoryStrategies()` (./strategies.ts) is the same seven
* described — what each does, which TYPES accept it, and what the host must
* supply (`requirements`) — so a picker can grey out what this deployment
* cannot run instead of finding out from an exception.
*/
export const MEMORY_STRATEGIES = {
WINDOW: 'window',
BUDGET: 'budget',
SUMMARIZE: 'summarize',
TOP_K: 'topK',
EXTRACT: 'extract',
DECAY: 'decay',
HYBRID: 'hybrid',
};
/**
* When the memory's READ subflow runs.
*
* Default `TURN_START` reads memory once per `agent.run()`. Use
* `EVERY_ITERATION` only when the strategy is sensitive to in-loop tool
* results — every-iteration multiplies store-latency by iteration-count.
*/
export const MEMORY_TIMING = {
EVERY_ITERATION: 'every-iteration',
TURN_START: 'turn-start',
};
/**
* For Causal memory only — which slice of a footprintjs snapshot to
* inject. Snapshots can run 100KB+; projecting prevents context blowup.
*
* - `DECISIONS` — `decide()`/`select()` evidence only (the "why" chain)
* - `COMMITS` — commitLog only (every state write, ordered)
* - `NARRATIVE` — narrative entries only (human-readable trace)
* - `FULL` — entire snapshot (use sparingly)
*/
export const SNAPSHOT_PROJECTIONS = {
DECISIONS: 'decisions',
COMMITS: 'commits',
NARRATIVE: 'narrative',
FULL: 'full',
};
// ─── Type guards (consumers + recorders) ────────────────────────────
export function isMemoryType(value) {
return Object.values(MEMORY_TYPES).includes(value);
}
export function isMemoryStrategyKind(value) {
return Object.values(MEMORY_STRATEGIES).includes(value);
}
export function isMemoryTiming(value) {
return Object.values(MEMORY_TIMING).includes(value);
}
export function isSnapshotProjection(value) {
return Object.values(SNAPSHOT_PROJECTIONS).includes(value);
}
// ─── Per-id scope-key convention (multi-memory layering) ────────────
/**
* Scope-key prefix used when mounting multiple `.memory()` definitions
* on the same Agent. Each memory writes to `memoryInjection_${id}` so
* registrations never collide. Formatter merges all keys with this
* prefix in registration order.
*/
export const MEMORY_INJECTION_KEY_PREFIX = 'memoryInjection_';
export function memoryInjectionKey(id) {
return `${MEMORY_INJECTION_KEY_PREFIX}${id}`;
}
export function isMemoryInjectionKey(key) {
return key.startsWith(MEMORY_INJECTION_KEY_PREFIX);
}
/**
* Scope-key prefix for the retrieval record a memory lifts to the parent
* scope (8.8.0), one key per memory id — the same layering rule as
* `memoryInjection_`.
*
* This key is the reason a backward slice can now reach a passage. It is
* ORDINARY root state, so `sliceForKey('finalContent')` walks the
* system-prompt write, and the record naming every candidate id and score
* is one hop away. Before 8.8.0 the scores existed only inside the memory
* subflow, which the root commit log never sees.
*/
export const RETRIEVAL_EVIDENCE_KEY_PREFIX = 'retrievalEvidence_';
export function retrievalEvidenceKey(id) {
return `${RETRIEVAL_EVIDENCE_KEY_PREFIX}${id}`;
}
export function isRetrievalEvidenceKey(key) {
return key.startsWith(RETRIEVAL_EVIDENCE_KEY_PREFIX);
}
//# sourceMappingURL=define.types.js.map