Agent SDK

Memory

Persistent state across conversations and sessions

Memory

Memory gives agents the ability to persist information across conversations, learn from past interactions, and maintain context over time. Without memory, every conversation starts from zero.

Memory Types

The SDK provides three types of memory, mirroring how human cognition works:

TypePurposeExample
SemanticFacts and knowledge"User prefers dark mode"
EpisodicPast experiences"Last time user asked about X, they wanted Y"
ProceduralHow to do things"To deploy, run npm run deploy"

Quick Start

import { Agent, Memory } from 'assistme-agent-sdk'
import { claude } from 'assistme-agent-sdk-provider-claude'

const agent = new Agent({
  name: 'assistant',
  model: claude('claude-sonnet-4-6'),
  instructions: 'You are a helpful assistant. Use your memory to personalize interactions.',
  memory: Memory.persistent({
    store: Memory.stores.sqlite('./memory.db'),
    namespace: 'user-123',
  }),
})

Memory Stores

Built-in Stores

// SQLite (local, single-process)
Memory.stores.sqlite('./memory.db')

// Redis (distributed, multi-process)
Memory.stores.redis({ url: 'redis://localhost:6379' })

// PostgreSQL (persistent, scalable)
Memory.stores.postgres({ connectionString: process.env.DATABASE_URL })

// In-memory (testing, ephemeral)
Memory.stores.inMemory()

Custom Stores

Implement the MemoryStore interface to use any storage backend:

import { MemoryStore, MemoryEntry } from 'assistme-agent-sdk'

class CustomStore implements MemoryStore {
  async get(namespace: string, key: string): Promise<MemoryEntry | null> {
    // Your implementation
  }

  async set(namespace: string, key: string, entry: MemoryEntry): Promise<void> {
    // Your implementation
  }

  async search(namespace: string, query: string, limit?: number): Promise<MemoryEntry[]> {
    // Your implementation (semantic search)
  }

  async list(namespace: string): Promise<MemoryEntry[]> {
    // Your implementation
  }

  async delete(namespace: string, key: string): Promise<void> {
    // Your implementation
  }
}

Automatic Memory

When memory is configured, the SDK automatically:

  1. Loads relevant memories into context at the start of each run
  2. Extracts and stores new memories after each run
const agent = new Agent({
  name: 'assistant',
  model: claude('claude-sonnet-4-6'),
  instructions: 'You are a helpful assistant.',
  memory: Memory.persistent({
    store: Memory.stores.sqlite('./memory.db'),
    namespace: 'user-123',
    auto: {
      /** Automatically extract and store memories from conversations */
      extract: true,
      /** Load relevant memories into context before each run */
      recall: true,
      /** Maximum memories to inject into context */
      maxRecall: 10,
    },
  }),
})

// First conversation
await Runner.run(agent, {
  messages: [{ role: 'user', content: 'My name is Alice and I prefer Python over JavaScript.' }],
})

// Second conversation — agent automatically recalls "User's name is Alice, prefers Python"
await Runner.run(agent, {
  messages: [{ role: 'user', content: 'What programming language should I use for this project?' }],
})

Manual Memory

For full control, manage memory explicitly:

const memory = Memory.persistent({
  store: Memory.stores.sqlite('./memory.db'),
  namespace: 'user-123',
})

// Write memories
await memory.set('preferences', {
  type: 'semantic',
  content: 'User prefers dark mode and compact layouts',
  metadata: { source: 'settings', updatedAt: new Date() },
})

await memory.set('last-project', {
  type: 'episodic',
  content: 'User was working on a React dashboard for inventory management',
  metadata: { date: '2026-03-12', project: 'inventory-dashboard' },
})

// Search memories
const relevant = await memory.search('What does the user prefer?', { limit: 5 })

// List all memories
const all = await memory.list()

// Delete a memory
await memory.delete('last-project')

Scoped Memory

Different agents in a multi-agent system can share or isolate memory:

// Shared memory — both agents read and write to the same namespace
const sharedMemory = Memory.persistent({
  store: Memory.stores.redis({ url: 'redis://localhost:6379' }),
  namespace: 'team-shared',
})

const agent1 = new Agent({ name: 'agent-1', memory: sharedMemory, ... })
const agent2 = new Agent({ name: 'agent-2', memory: sharedMemory, ... })

// Isolated memory — each agent has its own namespace
const agent3 = new Agent({
  name: 'agent-3',
  memory: Memory.persistent({
    store: sharedMemory.store,
    namespace: 'agent-3-private',
  }),
  ...
})

Memory Lifecycle

User Message


┌─────────────┐
│   Recall     │ ← Load relevant memories into context
│  (before)    │
└──────┬──────┘


┌─────────────┐
│  Agent Run   │ ← Agent sees memories as part of context
└──────┬──────┘


┌─────────────┐
│  Extract     │ ← Extract new memories from conversation
│  (after)     │
└──────┬──────┘


┌─────────────┐
│   Store      │ ← Persist new/updated memories
└─────────────┘

Memory with Conversation History

Memory is distinct from conversation history. Conversation history is the full message thread; memory is extracted knowledge:

const agent = new Agent({
  name: 'assistant',
  model: claude('claude-sonnet-4-6'),
  instructions: 'You are a helpful assistant.',
  memory: Memory.persistent({
    store: Memory.stores.sqlite('./memory.db'),
    namespace: 'user-123',
    auto: { extract: true, recall: true },
  }),
})

// Conversation 1: 50 messages about project setup
// → Memory extracts: "User's project uses Next.js 15, TypeScript, Supabase"
//   Full 50-message history is NOT stored in memory

// Conversation 2: Agent recalls the extracted facts, not the 50 messages
// → Context contains: "User's project uses Next.js 15, TypeScript, Supabase"
//   This is far more token-efficient than replaying the full history

Advanced: Knowledge Networks

For complex use cases, organize memories into interconnected networks:

const memory = Memory.persistent({
  store: Memory.stores.postgres({ connectionString }),
  namespace: 'user-123',
  indexing: {
    /** Create bidirectional links between related memories */
    linkRelated: true,
    /** Use embedding-based similarity for memory retrieval */
    embedding: true,
    /** Decay low-relevance memories over time */
    decay: {
      enabled: true,
      halfLife: '30d',
    },
  },
})

Best Practices

  1. Namespace by user — Each user should have their own memory namespace. Never mix memories between users.

  2. Prefer automatic extraction — Let the SDK extract memories. Manual memory management is more work and less comprehensive.

  3. Limit recall count — Don't inject 100 memories into every context. Use maxRecall to keep context focused (5-15 is typical).

  4. Use semantic search for recall — Instead of loading all memories, search for relevant ones based on the current query.

  5. Separate concerns — Use different namespaces for different types of data (user preferences, project context, learned procedures).

  6. Clean up periodically — Old, irrelevant memories add noise. Enable decay or periodically prune stale entries.