A highly semantic, agent-centric memory wrapper built on top of the high-performance runtime-memory-cache. This package provides a robust memory architecture designed specifically for LLM-based multi-agent systems.
It allows multiple agents to have isolated, high-performance in-memory caches, track chronological thought streams, and broadcast shared memories across all agents simultaneously.
- Agent Isolation: Give each agent its own sandboxed memory store.
- Chronological Thought Streams: Dedicated APIs to track sequences of thoughts, reasoning steps, or scratchpad content.
- Native LLM Tool Calling: Built-in JSON Schema generation and execution router for autonomous agents (OpenAI, MCP).
- Semantic Knowledge Graph: Built-in fuzzy search, tag-based inverted index, and memory linking. No external Vector DB required!
- Prompt Context Builder: Automatically aggregate thoughts and memories into a clean markdown string for injection.
- Global Broadcasting: Instantly drop a memory or a thought into the minds of all managed agents.
- High Performance: Built on
runtime-memory-cache, providing O(1) lookups, TTL support, and LRU/FIFO eviction policies. - Dual Output: Supports both CommonJS (
require()) and ES Modules (import). - Fully Typed: Written in TypeScript with complete type definitions.
npm install agent-runtime-memory-cacheA class representing the "brain" or memory store for a single agent. It handles explicit memories (Key-Value) and chronologically ordered "thoughts".
A centralized orchestrator that manages multiple AgentCache instances. It ensures agents don't step on each other's toes but allows you to broadcast global contexts to all of them when needed.
You can use the MultiAgentManager to initialize agents and store simple episodic memories.
import { MultiAgentManager } from 'agent-runtime-memory-cache';
// Initialize the manager
const manager = new MultiAgentManager({
ttl: 60000, // Default 1 minute TTL for memories
maxSize: 1000 // Keep up to 1000 memories
});
// Retrieve or create a memory cache for a specific agent
const agent1 = manager.getAgent('agent-1');
const agent2 = manager.getAgent('agent-2');
// Store isolated memories
agent1.remember('current_goal', 'Find the holy grail');
agent2.remember('current_goal', 'Protect the castle');
console.log(agent1.recall('current_goal')); // "Find the holy grail"
console.log(agent2.recall('current_goal')); // "Protect the castle"
// Forget a memory
agent1.forget('current_goal');Agents often need a scratchpad to store their step-by-step reasoning. You can use the addThought method to maintain a chronological sequence of thoughts. It automatically handles appending new thoughts and keeping the list within a maximum limit (default: 50).
// Add thoughts to a specific stream (e.g., 'reasoning_steps')
agent1.addThought('reasoning_steps', 'I need to cross the bridge.');
agent1.addThought('reasoning_steps', 'The bridge is guarded by a troll.');
agent1.addThought('reasoning_steps', 'I must answer his riddles.');
// Retrieve the chronologically ordered thoughts
const thoughts = agent1.getThoughts('reasoning_steps');
console.log(thoughts);
/*
[
{ id: 'x8fj2l...', timestamp: 1699999999999, content: 'I need to cross the bridge.' },
{ id: 'p9zk1m...', timestamp: 1699999999999, content: 'The bridge is guarded by a troll.' },
{ id: 'b2nc4p...', timestamp: 1699999999999, content: 'I must answer his riddles.' }
]
*/Sometimes you need to inject a piece of context into every agent's memory simultaneously (e.g., a system-wide halt command, or a change in global environment).
// Broadcast a standard memory
manager.broadcast('system_directive', 'DO NOT HARM HUMANS');
console.log(agent1.recall('system_directive')); // "DO NOT HARM HUMANS"
console.log(agent2.recall('system_directive')); // "DO NOT HARM HUMANS"
// Broadcast a thought to all agents
manager.broadcastThought('environment_updates', 'It started raining.');For advanced AI systems and Model Context Protocol (MCP) integrations, you don't want to load an agent's entire memory into the LLM context. You can use the built-in, zero-dependency Tag-Based Inverted Index to let agents learn concepts and retrieve them semantically.
// The agent learns various concepts and tags them
agent1.learn('api_auth', { endpoint: '/login', method: 'POST' }, ['api', 'auth', 'backend']);
agent1.learn('api_users', { endpoint: '/users', method: 'GET' }, ['api', 'users', 'backend']);
agent1.learn('ui_login', { component: 'LoginForm' }, ['ui', 'auth', 'frontend']);
// Later, the agent needs context about "auth".
// It retrieves only the relevant knowledge without loading everything else!
const authContext = agent1.retrieveByTags(['auth']);
/*
[
{ conceptKey: 'api_auth', content: { endpoint: '/login', method: 'POST' }, tags: ['api', 'auth', 'backend'], learnedAt: 17... },
{ conceptKey: 'ui_login', content: { component: 'LoginForm' }, tags: ['ui', 'auth', 'frontend'], learnedAt: 17... }
]
*/
// You can also require multiple tags (intersection)
const backendApiContext = agent1.retrieveByTags(['api', 'backend']);Automatically aggregate tags and thoughts into a clean markdown string that can be injected directly into an LLM's system prompt.
const contextString = agent1.buildContext(['auth'], ['reasoning_steps']);
// Returns a formatted string with ### Retrieved Knowledge and ### Recent ThoughtsTags are great, but sometimes concepts are related directly. You can build a lightweight graph of knowledge.
agent1.link('api_auth', 'api_users', 'authenticates');
const links = agent1.getLinks('api_auth');
// [{ target: 'api_users', relationship: 'authenticates' }]Give your agent autonomous control over its memory. You can retrieve standardized JSON Tool Schemas for OpenAI/MCP and automatically execute the LLM's responses.
// 1. Give the schemas directly to the LLM
const schemas = agent1.getToolSchemas();
// e.g. openai.chat.completions.create({ tools: schemas, ... })
// ... LLM outputs a tool call ...
const toolCall = response.choices[0].message.tool_calls[0];
// 2. The router automatically parses arguments and executes the correct memory function!
const resultStr = agent1.executeTool(toolCall.function.name, toolCall.function.arguments);If you don't know the exact tag, you can perform a zero-dependency fuzzy text search across all learned concepts.
const searchResults = agent1.searchKnowledge('Login');When an agent is thinking for a long time, the addThought() array eventually reaches its maximum size. You can pass an onThoughtsFull callback to summarize old thoughts before they are truncated!
const smartManager = new MultiAgentManager({
onThoughtsFull: (listKey, thoughts) => {
console.log(`Stream ${listKey} is full! Time to summarize:`, thoughts);
// Send to LLM to summarize, then save summary to long-term memory
}
});If your architecture spans multiple servers, a purely in-memory cache won't sync automatically. You can seamlessly bounce an agent's brain between servers using state hydration.
// On Server A: Export the agent's entire memory state to JSON
const stateJson = agent1.exportState();
await database.save(agent1.agentId, stateJson);
// On Server B: Wake the agent up and restore its memory
const savedState = await database.load('agent-1');
const agent1OnServerB = manager.getAgent('agent-1');
agent1OnServerB.importState(savedState);You can override the default manager options for a specific agent if it requires a different memory capacity or eviction policy.
const smartAgent = manager.getAgent('einstein', {
ttl: undefined, // Memory never expires
maxSize: 10000, // Massive capacity
evictionPolicy: 'LRU' // Evict Least Recently Used
});You can optionally configure an agent (or the manager) to trigger a callback whenever memory changes (set, del, clear, or import). This is perfect for streaming memory updates over WebSockets or writing them to a database.
const eventAgent = manager.getAgent('event-agent-1', {
// Triggers on any memory modification
onMemoryChange: (event) => {
console.log(`Agent ${event.agentId} performed a ${event.action} on key ${event.key}`);
// If you enable exportOnMemoryChange, the entire exported memory JSON is included!
if (event.stateSnapshot) {
console.log('Full memory backup:', event.stateSnapshot);
}
},
// Optionally include the full JSON state snapshot in every event
exportOnMemoryChange: false
});
eventAgent.remember('myKey', 'myValue'); // Triggers the callback!ttl?: number: Time-to-live in milliseconds.maxSize?: number: Maximum number of entries (default: 1000).enableStats?: boolean: Enable tracking hits, misses, etc. (default: false).evictionPolicy?: 'FIFO' | 'LRU': Eviction policy (default: 'LRU').onMemoryChange?: (event: MemoryChangeEvent) => void: Event listener.exportOnMemoryChange?: boolean: Attach state snapshot to events.onThoughtsFull?: (listKey: string, thoughts: Thought[]) => void: Consolidation hook.
constructor(defaultOptions?: AgentCacheOptions)getAgent(agentId: string, options?: AgentCacheOptions): AgentCachehasAgent(agentId: string): booleanremoveAgent(agentId: string): booleangetActiveAgents(): string[]broadcast<T>(key: string, value: T, ttl?: number): voidbroadcastThought<T>(listKey: string, content: T, maxThoughts?: number): voidclearAll(): void
remember<T>(key: string, value: T, ttl?: number): voidrecall<T>(key: string): T | undefinedforget(key: string): booleanhasMemory(key: string, skipTouch?: boolean): booleanaddThought<T>(listKey: string, content: T, maxThoughts?: number): Thought<T>getThoughts<T>(listKey: string): Thought<T>[]learn<T>(conceptKey: string, content: T, tags: string[], ttl?: number): voidretrieveByTags<T = any>(tags: string[]): KnowledgeDocument<T>[]forgetConcept(conceptKey: string): voidbuildContext(tags?: string[], thoughtKeys?: string[]): stringlink(conceptA: string, conceptB: string, relationship?: string): voidgetLinks(conceptKey: string): { target: string, relationship: string }[]searchKnowledge<T = any>(query: string): KnowledgeDocument<T>[]getToolSchemas(): ToolSchema[]executeTool(functionName: string, argumentsJson: string): stringexportState(): stringimportState(jsonString: string): voidclearMemory(): voidmemorySize(): numbergetStats(): AgentMemoryStatsgetUnderlyingCache(): RuntimeMemoryCache
MIT