Skip to content

SmartMemory

SmartMemory provides four types of memory for AI agents: working memory for active sessions, episodic memory for completed sessions, semantic memory for knowledge storage, and procedural memory for skills and templates. This system lets AI agents remember conversations, learn from experience, and build up knowledge over time.

The memory system works through isolated sessions that contain conversation context and can be organized into timelines. When sessions complete, they move to episodic memory where they become searchable history. Semantic memory stores documents and facts that persist across all sessions, while procedural memory holds reusable templates and procedures.

Prerequisites

  • Raindrop framework installed in your project
  • Understanding of AI agent architecture and memory systems
  • Familiarity with TypeScript and async/await patterns

Configuration

Define SmartMemory in your application manifest:

raindrop.manifest
application "demo-smartmemory" {
smartmemory "memory" {}
}

Basic Usage

Access SmartMemory through environment variables:

// Access the SmartMemory instance
const smartMemory = env.MEMORY;
// Start a new working memory session
const { sessionId, workingMemory } = await smartMemory.startWorkingMemorySession();
// Store a memory
const memoryId = await workingMemory.putMemory({
content: "User prefers morning meetings",
timeline: "preferences"
});
// Search memories
const results = await workingMemory.searchMemory({
terms: "meeting schedule"
});

Core Interfaces

MemoryEntry

Represents a stored memory with metadata:

interface MemoryEntry {
id: string; // Unique memory identifier
in: string; // Session ID containing this memory
timeline: string; // Timeline organization
by: string; // Creator identifier
dueTo: string; // Causal relationship
content: string; // The actual memory content
at: Date; // Creation timestamp
key?: string; // Optional metadata key
agent?: string; // Optional agent identifier
}

NewMemoryEntry

Input for creating new memories:

interface NewMemoryEntry {
content: string; // Required: memory content
timeline?: string; // Optional: defaults to "*defaultTimeline"
key?: string; // Optional: metadata key for filtering
agent?: string; // Optional: agent identifier
sessionId?: string; // Optional: explicit session override
at?: Date; // Optional: custom timestamp
}

WorkingMemoryQuery

Query interface for exact memory retrieval:

interface WorkingMemoryQuery {
timeline?: string; // Filter by timeline
key?: string; // Filter by metadata key
nMostRecent?: number; // Limit results to N most recent
startTime?: Date; // Time range start
endTime?: Date; // Time range end
}

WorkingMemorySearchQuery

Search interface for semantic memory search:

interface WorkingMemorySearchQuery {
timeline?: string; // Optional timeline filter
terms: string; // Required search terms
nMostRecent?: number; // Optional result limit
startTime?: Date; // Optional time range start
endTime?: Date; // Optional time range end
}

ProcedureEntry

Represents stored procedures and templates:

interface ProcedureEntry {
key: string; // Unique procedure identifier
value: string; // Procedure content or template
createdAt: Date; // Creation timestamp
updatedAt: Date; // Last modification timestamp
}

Working Memory

Working memory handles active conversation sessions with timeline organization and search capabilities.

startWorkingMemorySession()

Creates a new isolated memory session:

Returns: Promise<{ sessionId: string, workingMemory: SmartWorkingMemory }>

const { sessionId, workingMemory } = await env.MEMORY.startWorkingMemorySession();
console.log(`Started session: ${sessionId}`);

getWorkingMemorySession()

Retrieves an existing session by ID:

Parameters:

  • sessionId: string - The session identifier

Returns: Promise<SmartWorkingMemory>

const workingMemory = await env.MEMORY.getWorkingMemorySession("session-123");

putMemory()

Stores a new memory entry in the session:

Parameters:

  • entry: NewMemoryEntry - The memory to store

Returns: Promise<string> - The memory entry ID

const memoryId = await workingMemory.putMemory({
content: "User wants daily standup at 9 AM",
timeline: "meetings",
key: "schedule",
agent: "assistant"
});

getMemory()

Retrieves memories using exact filters:

Parameters:

  • query: WorkingMemoryQuery - Query filters

Returns: Promise<MemoryEntry[] | null> - Matching memories

const memories = await workingMemory.getMemory({
timeline: "meetings",
key: "schedule",
nMostRecent: 5
});

searchMemory()

Performs semantic search across memory content:

Parameters:

  • query: WorkingMemorySearchQuery - Search terms and filters

Returns: Promise<MemoryEntry[] | null> - Ranked results

const results = await workingMemory.searchMemory({
terms: "morning meeting schedule",
timeline: "meetings",
nMostRecent: 10
});

deleteMemory()

Removes a memory entry by ID:

Parameters:

  • entryId: string - The memory ID to delete

Returns: Promise<void>

await workingMemory.deleteMemory("memory-456");

endSession()

Ends the session with optional flush to episodic memory. When flush is true, the operation happens asynchronously in the background:

Parameters:

  • flush: boolean - Whether to save to episodic storage

Returns: Promise<void>

// End session and save to episodic memory (background processing)
await workingMemory.endSession(true);
console.log("Session ended, flushing to episodic memory in background");
// End session without saving
await workingMemory.endSession(false);

summarizeMemory()

Generates AI summary of memory entries:

Parameters:

  • memories: MemoryEntry[] - Memories to summarize
  • systemPrompt?: string - Optional custom prompt

Returns: Promise<{ summary: string, entries: Record<string, MemoryEntry[]>, metadata: object }>

const summary = await workingMemory.summarizeMemory(memories,
"Summarize the key decisions and action items from these memories."
);
console.log(summary.summary);
console.log(`Summarized ${summary.metadata.entryCount} entries`);

Episodic Memory

Episodic memory stores completed sessions as searchable history that can be restored.

searchEpisodicMemory()

Search across historical sessions:

Parameters:

  • terms: string - Search terms
  • options?: object - Optional filters

Returns: Promise<{ results: Array<object>, pagination: object }>

const results = await env.MEMORY.searchEpisodicMemory("project planning", {
nMostRecent: 10,
startTime: new Date('2024-01-01'),
endTime: new Date('2024-12-31')
});
results.results.forEach(session => {
console.log(`Session ${session.sessionId}: ${session.summary}`);
console.log(`${session.entryCount} entries, ${session.duration}ms duration`);
});

rehydrateSession()

Restore a previous session from episodic memory. This operation is asynchronous and may take time for large sessions:

Parameters:

  • sessionId: string - Session to restore
  • summaryOnly?: boolean - Restore only summary or full session

Returns: Promise<{ sessionId: string, workingMemory: SmartWorkingMemory, success: boolean, message: string, entriesRestored?: number }>

const restored = await env.MEMORY.rehydrateSession("session-789", false);
if (restored.success) {
console.log(`Restored ${restored.entriesRestored} entries`);
// Continue working with restored.workingMemory
} else {
console.error(`Rehydration failed: ${restored.message}`);
}

getRehydrationStatus()

Monitor the progress of an ongoing rehydration operation:

Returns: Promise<{ status: 'initiated' | 'processing' | 'completed' | 'failed', sessionId?: string, entriesRestored?: number, error?: string, initiatedAt?: string, completedAt?: string } | null>

// Start rehydration
const restored = await env.MEMORY.rehydrateSession("session-789", false);
// Check status periodically for large sessions
const checkStatus = async () => {
const status = await workingMemory.getRehydrationStatus();
if (status) {
console.log(`Status: ${status.status}`);
if (status.status === 'completed') {
console.log(`Successfully restored ${status.entriesRestored} entries`);
} else if (status.status === 'failed') {
console.error(`Rehydration failed: ${status.error}`);
} else if (status.status === 'processing') {
console.log('Still processing...');
setTimeout(checkStatus, 1000); // Check again in 1 second
}
}
};
if (restored.success) {
await checkStatus();
}

Search Behavior

SmartMemory uses vector-based semantic search powered by AI embeddings for intelligent content discovery.

Working memory search uses AI embeddings to understand semantic meaning:

// These searches will find semantically similar content
await workingMemory.searchMemory({ terms: "user authentication" });
// ^ Finds memories about "login security", "access control", etc.
await workingMemory.searchMemory({ terms: "morning meeting" });
// ^ Finds memories about "daily standup", "9 AM call", etc.

Automatic Fallback

When vector search fails, the system automatically falls back to text-based search:

// Search works even if AI embeddings are unavailable
const results = await workingMemory.searchMemory({
terms: "project deadline"
});
// Results come from either:
// 1. Vector search (semantic understanding) - preferred
// 2. Text search (keyword matching) - fallback

Performance Characteristics

  • Vector Search: Fast semantic matching across thousands of memories
  • Text Search: Reliable keyword matching when vector search unavailable
  • Session Isolation: Each session’s search is completely independent
  • Timeline Filtering: Search within specific timelines for better performance

Semantic Memory

Semantic memory stores documents and knowledge that persist across all sessions.

putSemanticMemory()

Store a document in semantic memory:

Parameters:

  • document: Record<string, unknown> - Document to store

Returns: Promise<{ success: boolean, objectId?: string, error?: string }>

const result = await workingMemory.putSemanticMemory({
title: "Project Requirements",
content: "The system should handle 1000 concurrent users...",
type: "requirements",
tags: ["project", "specs", "performance"]
});
if (result.success) {
console.log(`Stored document: ${result.objectId}`);
} else {
console.error(`Failed to store document: ${result.error}`);
}

getSemanticMemory()

Retrieve a specific document by ID:

Parameters:

  • objectId: string - Document identifier

Returns: Promise<{ success: boolean, document?: Record<string, unknown>, error?: string }>

const result = await workingMemory.getSemanticMemory("doc-123");
if (result.success && result.document) {
console.log(`Title: ${result.document.title}`);
console.log(`Content: ${result.document.content}`);
}

searchSemanticMemory()

Search across semantic memory documents:

Parameters:

  • needle: string - Search query

Returns: Promise<{ success: boolean, documentSearchResponse?: object, error?: string }>

const results = await workingMemory.searchSemanticMemory("user authentication");
if (results.success && results.documentSearchResponse) {
results.documentSearchResponse.results.forEach(match => {
console.log(`Score: ${match.score}, Text: ${match.text}`);
});
} else {
console.error(`Search failed: ${results.error}`);
}

deleteSemanticMemory()

Remove a document from semantic memory:

Parameters:

  • objectId: string - Document to delete

Returns: Promise<{ success: boolean, error?: string }>

const result = await workingMemory.deleteSemanticMemory("doc-456");
if (result.success) {
console.log("Document deleted successfully");
}

Procedural Memory

Procedural memory stores reusable procedures, templates, and skills.

getProceduralMemory()

Get access to procedural memory:

Parameters:

  • id?: string - Optional procedural memory identifier

Returns: Promise<SmartProceduralMemory>

const proceduralMemory = await env.MEMORY.getProceduralMemory();

putProcedure()

Store a procedure or template:

Parameters:

  • key: string - Unique procedure identifier
  • value: string - Procedure content

Returns: Promise<void>

await proceduralMemory.putProcedure("email-template",
"Subject: Update on {{project}}\n\nHi {{name}},\n\nI wanted to update you on..."
);
await proceduralMemory.putProcedure("code-review-checklist",
"1. Check for security vulnerabilities\n2. Verify test coverage\n3. Review performance implications"
);

getProcedure()

Retrieve a procedure by key:

Parameters:

  • key: string - Procedure identifier

Returns: Promise<string | null>

const template = await proceduralMemory.getProcedure("email-template");
if (template) {
const personalizedEmail = template
.replace("{{project}}", "Website Redesign")
.replace("{{name}}", "Sarah");
}

listProcedures()

Get all stored procedures:

Returns: Promise<ProcedureEntry[]>

const allProcedures = await proceduralMemory.listProcedures();
allProcedures.forEach(proc => {
console.log(`${proc.key}: ${proc.value.substring(0, 50)}...`);
console.log(`Created: ${proc.createdAt}, Updated: ${proc.updatedAt}`);
});

searchProcedures()

Search procedures by content:

Parameters:

  • query: ProceduralMemorySearchQuery - Search parameters

Returns: Promise<ProcedureEntry[] | null>

const results = await proceduralMemory.searchProcedures({
terms: "email template",
nMostRecent: 5,
searchKeys: true,
searchValues: true
});

deleteProcedure()

Remove a procedure:

Parameters:

  • key: string - Procedure to delete

Returns: Promise<boolean>

const deleted = await proceduralMemory.deleteProcedure("old-template");
if (deleted) {
console.log("Procedure deleted successfully");
}

Complete Example

Here’s how to use all memory types together with proper async handling and error checking:

try {
// Start a working memory session
const { sessionId, workingMemory } = await env.MEMORY.startWorkingMemorySession();
console.log(`Started session: ${sessionId}`);
// Store some conversation memories
await workingMemory.putMemory({
content: "User is working on a React project with authentication needs",
timeline: "project-context",
key: "requirements"
});
await workingMemory.putMemory({
content: "Discussed using JWT tokens for session management",
timeline: "technical-decisions",
key: "auth-strategy"
});
// Search working memory (uses vector search with text fallback)
const authMemories = await workingMemory.searchMemory({
terms: "authentication JWT tokens",
nMostRecent: 5
});
if (authMemories) {
console.log(`Found ${authMemories.length} related memories`);
}
// Store knowledge in semantic memory with error handling
const semanticResult = await workingMemory.putSemanticMemory({
title: "JWT Best Practices",
content: "Always use HTTPS, set appropriate expiration times, validate tokens on every request...",
type: "security-guide",
topic: "authentication"
});
if (semanticResult.success) {
console.log(`Stored knowledge document: ${semanticResult.objectId}`);
} else {
console.error(`Failed to store knowledge: ${semanticResult.error}`);
}
// Store reusable templates in procedural memory
const proceduralMemory = await env.MEMORY.getProceduralMemory();
await proceduralMemory.putProcedure("jwt-middleware-template", `
const jwt = require('jsonwebtoken');
function authenticateToken(req, res, next) {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) {
return res.sendStatus(401);
}
jwt.verify(token, process.env.ACCESS_TOKEN_SECRET, (err, user) => {
if (err) return res.sendStatus(403);
req.user = user;
next();
});
}
`);
// End session and flush to episodic memory (happens in background)
await workingMemory.endSession(true);
console.log("Session ended, flushing to episodic memory in background");
// Later, search episodic memory for similar conversations
const pastSessions = await env.MEMORY.searchEpisodicMemory("React authentication project");
if (pastSessions.results.length > 0) {
console.log(`Found ${pastSessions.results.length} similar past sessions`);
// Rehydrate a previous session with status monitoring
const restored = await env.MEMORY.rehydrateSession(pastSessions.results[0].sessionId);
if (restored.success) {
console.log("Rehydration initiated, checking status...");
// Monitor rehydration progress for large sessions
const checkStatus = async () => {
const status = await workingMemory.getRehydrationStatus();
if (status?.status === 'completed') {
console.log(`Successfully restored ${status.entriesRestored} memories`);
} else if (status?.status === 'failed') {
console.error(`Rehydration failed: ${status.error}`);
} else if (status?.status === 'processing') {
setTimeout(checkStatus, 1000); // Check again in 1 second
}
};
await checkStatus();
} else {
console.error(`Rehydration failed: ${restored.message}`);
}
}
} catch (error) {
console.error("SmartMemory operation failed:", error);
}

This example demonstrates:

  • Working Memory: Active conversation storage and semantic search
  • Semantic Memory: Persistent knowledge with error handling
  • Procedural Memory: Reusable code templates
  • Episodic Memory: Searchable session history
  • Async Operations: Background flushing and rehydration with status monitoring
  • Error Handling: Proper checking of success/failure states