Chat History & Memory Engine
Large Language Models (LLMs) are stateless by default. To maintain coherent multi-turn conversations, agents require state management that balances context window constraints, latency, storage cost, and long-term user facts.
AgentCore provides a modular memory architecture covering context windowing strategies, pluggable database persistence adapters, UI message converters, self-managed tool memory, and third-party semantic memory integrations (Mem0, Vector DBs).
Memory Pipeline Flow
1. Dual Message Format & UI Conversion
Frontend UI libraries (like Next.js React client components) track message states with UI-specific attributes such as tool invocation progress (state: 'call' | 'result'), attachments, and temporary rendering flags. LLM providers expect strict, standardized AgentMessage role arrays.
The convertToModelMessages() Function
Transforms client UIMessage[] into clean, role-compliant AgentMessage[], automatically formatting tool invocation results into tool-role messages:
| 1 | import { convertToModelMessages, type UIMessage, type AgentMessage } from "@bablusingh-dev/agentcore"; |
| 2 | |
| 3 | // 1. Raw UI messages received from client request |
| 4 | const uiMessages: UIMessage[] = [ |
| 5 | { |
| 6 | id: "msg-1", |
| 7 | role: "user", |
| 8 | content: "What's the weather in Tokyo?", |
| 9 | createdAt: new Date(), |
| 10 | }, |
| 11 | { |
| 12 | id: "msg-2", |
| 13 | role: "assistant", |
| 14 | content: "", |
| 15 | toolInvocations: [ |
| 16 | { |
| 17 | toolCallId: "call_abc123", |
| 18 | toolName: "get_weather", |
| 19 | args: { city: "Tokyo" }, |
| 20 | state: "result", |
| 21 | result: { temperature: "22°C", condition: "Sunny" }, |
| 22 | }, |
| 23 | ], |
| 24 | }, |
| 25 | ]; |
| 26 | |
| 27 | // 2. Convert UI state to Model-ready AgentMessage array |
| 28 | const modelMessages: AgentMessage[] = convertToModelMessages(uiMessages); |
| 29 | |
| 30 | console.log(modelMessages); |
| 31 | /* Output: |
| 32 | [ |
| 33 | { id: 'msg-1', role: 'user', content: "What's the weather in Tokyo?", timestamp: 1770417000000 }, |
| 34 | { id: 'msg-2', role: 'assistant', content: '', toolCalls: [{ id: 'call_abc123', name: 'get_weather', arguments: { city: 'Tokyo' } }] }, |
| 35 | { role: 'tool', toolCallId: 'call_abc123', name: 'get_weather', content: '{"temperature":"22°C","condition":"Sunny"}' } |
| 36 | ] |
| 37 | */ |
2. Context Window Strategies & Truncation
As multi-turn chats grow, passing full history consumes excessive tokens and risks exceeding the LLM context limit. AgentCore provides four built-in strategy processors:
Sliding Window
Keeps system prompts at the top and retains the last maxMessages turns. Drops oldest turns automatically.
Token Budget Window
Calculates total estimated tokens and fits recent non-system messages into a strict maxTokens budget from back to front.
Auto-Summarization
When turns exceed summarizeThreshold, older turns are condensed into a summary system message, preserving key context.
Full History
Unbounded message retention. Passes all historical turns directly to the provider without trimming.
Configuring Strategies in Agent
| 1 | import { Agent, SlidingWindowMemory, TokenWindowMemory, SummaryMemory } from "@bablusingh-dev/agentcore"; |
| 2 | |
| 3 | // Option A: Quick string declaration with maxMessages |
| 4 | const agentSliding = new Agent({ |
| 5 | model: "openai/gpt-4o", |
| 6 | memory: "sliding_window", |
| 7 | maxMessages: 12, // Keeps system prompt + last 12 turns |
| 8 | }); |
| 9 | |
| 10 | // Option B: Token budget window (e.g. 3,000 token max context budget) |
| 11 | const agentToken = new Agent({ |
| 12 | model: "anthropic/claude-3-5-sonnet", |
| 13 | memory: "token_window", |
| 14 | maxTokens: 3000, |
| 15 | }); |
| 16 | |
| 17 | // Option C: Explicit strategy class instantiation |
| 18 | const agentSummary = new Agent({ |
| 19 | model: "openai/gpt-4o-mini", |
| 20 | memory: new SummaryMemory(10, 4), // Summarize after 10 turns, retain 4 recent turns |
| 21 | }); |
3. Persistence Stores & Custom Database Drivers
By default, memory is cached in-RAM via InMemoryStore. For serverless applications, background tasks, or persistent multi-session chats, AgentCore provides file storage and a pluggable MemoryStore interface.
Built-in Stores
| 1 | import { Agent, InMemoryStore, FileMemoryStore } from "@bablusingh-dev/agentcore"; |
| 2 | |
| 3 | // 1. High-performance In-RAM store (default) |
| 4 | const inMemoryStore = new InMemoryStore(); |
| 5 | |
| 6 | // 2. Local File-backed JSON store (Node.js environment) |
| 7 | const fileStore = new FileMemoryStore("./storage/agent-sessions.json"); |
| 8 | |
| 9 | const agent = new Agent({ |
| 10 | model: "openai/gpt-4o", |
| 11 | memory: "sliding_window", |
| 12 | memoryStore: fileStore, |
| 13 | }); |
Building a Custom Database Adapter (Redis / Supabase / PostgreSQL)
Implement the MemoryStore interface to connect Redis, Supabase, MongoDB, or PostgreSQL seamlessly:
| 1 | import { type MemoryStore, type AgentMessage } from "@bablusingh-dev/agentcore"; |
| 2 | import { Redis } from "@upstash/redis"; // Example Upstash/Redis client |
| 3 | |
| 4 | export class RedisMemoryStore implements MemoryStore { |
| 5 | private redis: Redis; |
| 6 | |
| 7 | constructor() { |
| 8 | this.redis = Redis.fromEnv(); |
| 9 | } |
| 10 | |
| 11 | async getMessages(sessionId: string): Promise<AgentMessage[]> { |
| 12 | const raw = await this.redis.get<AgentMessage[]>(`chat:messages:${sessionId}`); |
| 13 | return raw ?? []; |
| 14 | } |
| 15 | |
| 16 | async setMessages(sessionId: string, messages: AgentMessage[]): Promise<void> { |
| 17 | await this.redis.set(`chat:messages:${sessionId}`, messages); |
| 18 | } |
| 19 | |
| 20 | async clearMessages(sessionId: string): Promise<void> { |
| 21 | await this.redis.del(`chat:messages:${sessionId}`); |
| 22 | await this.redis.del(`chat:facts:${sessionId}`); |
| 23 | } |
| 24 | |
| 25 | async getFacts(sessionId: string): Promise<Record<string, string>> { |
| 26 | const facts = await this.redis.hgetall(`chat:facts:${sessionId}`); |
| 27 | return (facts as Record<string, string>) ?? {}; |
| 28 | } |
| 29 | |
| 30 | async setFact(sessionId: string, key: string, value: string): Promise<void> { |
| 31 | await this.redis.hset(`chat:facts:${sessionId}`, { [key]: value }); |
| 32 | } |
| 33 | |
| 34 | async deleteFact(sessionId: string, key: string): Promise<void> { |
| 35 | await this.redis.hdel(`chat:facts:${sessionId}`, key); |
| 36 | } |
| 37 | } |
| 38 | |
| 39 | // Pass custom store to Agent |
| 40 | const agentWithRedis = new Agent({ |
| 41 | model: "openai/gpt-4o", |
| 42 | memoryStore: new RedisMemoryStore(), |
| 43 | }); |
4. Tool-Driven Agentic Memory (Self-Managing Memory)
When enableMemoryTools: true is configured, AgentCore automatically registers type-safe memory tools with the LLM agent:
- remember_fact(key, value): Allows the model to save user facts or explicit preferences into the facts scratchpad during dialogue.
- recall_fact(key?): Allows the model to query stored user preferences.
- forget_fact(key): Deletes an outdated fact.
Stored facts are automatically injected into future prompt turns under a dedicated system prompt block:
| 1 | import { Agent } from "@bablusingh-dev/agentcore"; |
| 2 | |
| 3 | const agent = new Agent({ |
| 4 | model: "openai/gpt-4o", |
| 5 | systemPrompt: "You are a helpful coding assistant.", |
| 6 | memory: "sliding_window", |
| 7 | enableMemoryTools: true, // Enables remember_fact, recall_fact, and forget_fact |
| 8 | maxSteps: 3, // Allows agent to execute memory tool and respond in 1 call |
| 9 | }); |
| 10 | |
| 11 | // Turn 1: User discloses a preference |
| 12 | const res1 = await agent.run("Hi, remember that I prefer TypeScript and Dark Mode in Next.js."); |
| 13 | // Agent executes 'remember_fact({ key: "tech_stack", value: "TypeScript, Dark Mode, Next.js" })' |
| 14 | |
| 15 | // Turn 2: In a later turn, the agent automatically recalls the saved facts! |
| 16 | const res2 = await agent.run("Generate a basic button component for me."); |
| 17 | console.log(res2.text); |
| 18 | // Output will automatically use TypeScript + Dark mode styling based on saved memory facts! |
5. Third-Party Memory Providers (Mem0 & Vector DBs)
Connect external memory engines to retrieve semantic memories and extract facts automatically across multi-session conversations.
Using Mem0Provider
| 1 | import { Agent, Mem0Provider } from "@bablusingh-dev/agentcore"; |
| 2 | |
| 3 | const mem0Provider = new Mem0Provider({ |
| 4 | apiKey: process.env.MEM0_API_KEY, |
| 5 | }); |
| 6 | |
| 7 | const agent = new Agent({ |
| 8 | model: "openai/gpt-4o", |
| 9 | memoryProvider: mem0Provider, |
| 10 | }); |
| 11 | |
| 12 | // Before generation, relevant memories from Mem0 are automatically injected into prompt context. |
| 13 | // After generation finishes, conversation turns are automatically saved back to Mem0. |
| 14 | const res = await agent.run("What projects was I working on last week?"); |
Using VectorMemoryProvider (Semantic Embeddings)
| 1 | import { Agent, VectorMemoryProvider } from "@bablusingh-dev/agentcore"; |
| 2 | |
| 3 | // Connect any vector database (Pinecone, Qdrant, ChromaDB, Weaviate) |
| 4 | const vectorProvider = new VectorMemoryProvider({ |
| 5 | search: async (query: string, limit = 5) => { |
| 6 | // Perform vector similarity search in your database |
| 7 | return ["User built an e-commerce dashboard using AgentCore and Tailwind."]; |
| 8 | }, |
| 9 | insert: async (text: string, metadata) => { |
| 10 | // Generate vector embedding and store in database |
| 11 | }, |
| 12 | }); |
| 13 | |
| 14 | const agent = new Agent({ |
| 15 | model: "openai/gpt-4o", |
| 16 | memoryProvider: vectorProvider, |
| 17 | }); |
6. End-to-End Production Next.js Example
Here is a complete Next.js App Router API Route (app/api/chat/route.ts) combining UI conversion, sliding window memory, tool execution, and server-side persistence callbacks:
| 1 | // app/api/chat/route.ts |
| 2 | import { convertToModelMessages, streamText, FileMemoryStore } from "@bablusingh-dev/agentcore"; |
| 3 | |
| 4 | const memoryStore = new FileMemoryStore("./storage/sessions.json"); |
| 5 | |
| 6 | export async function POST(req: Request) { |
| 7 | const { messages, sessionId = "user_123" } = await req.json(); |
| 8 | |
| 9 | // 1. Convert client UI messages to AgentMessage format |
| 10 | const modelMessages = convertToModelMessages(messages); |
| 11 | |
| 12 | // 2. Initiate streaming text generation with memory strategy & persistence |
| 13 | const { textStream, response } = streamText({ |
| 14 | model: "openai/gpt-4o-mini", |
| 15 | messages: modelMessages, |
| 16 | memory: "sliding_window", |
| 17 | memoryStore: memoryStore, |
| 18 | enableMemoryTools: true, |
| 19 | maxSteps: 3, |
| 20 | onFinish: async ({ response, messages }) => { |
| 21 | console.log(`[Session ${sessionId}] Usage: ${response.usage?.totalTokens} tokens`); |
| 22 | // Optional: Save final context to external analytics / Postgres database |
| 23 | }, |
| 24 | }); |
| 25 | |
| 26 | // 3. Return readable stream to client |
| 27 | const encoder = new TextEncoder(); |
| 28 | const readable = new ReadableStream({ |
| 29 | async start(controller) { |
| 30 | for await (const chunk of textStream) { |
| 31 | controller.enqueue(encoder.encode(chunk)); |
| 32 | } |
| 33 | controller.close(); |
| 34 | }, |
| 35 | }); |
| 36 | |
| 37 | return new Response(readable, { |
| 38 | headers: { "Content-Type": "text/plain; charset=utf-8" }, |
| 39 | }); |
| 40 | } |
7. Memory API Reference
Agent Memory Configuration (AgentConfig)
| Option | Type | Default | Description |
|---|---|---|---|
| memory | 'sliding_window' | 'token_window' | 'summary' | 'full' | IMemoryStrategy | 'full' | Memory strategy processor mode. |
| maxMessages | number | 20 | Maximum turn limit for sliding_window mode. |
| maxTokens | number | 4000 | Max token budget for token_window mode. |
| memoryStore | MemoryStore | InMemoryStore | Storage driver for message history & facts persistence. |
| enableMemoryTools | boolean | false | Registers remember_fact and recall_fact tools. |
| memoryProvider | MemoryProvider | undefined | Third-party memory engine (Mem0, Vector DB) for auto RAG context. |