Skip to main content

Overview

The memory system provides persistent storage for user information using Anthropic’s native memory tool. It implements a filesystem-like interface where the agent can create, view, edit, and delete virtual files in a /memories directory.

Architecture

Database Schema

// apps/web/src/db/schema/memory.ts
export const memory = pgTable("memory", {
  id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
  userId: text("user_id").notNull().references(() => user.id, { onDelete: "cascade" }),
  path: text("path").notNull(),  // e.g., /memories/notes.txt
  content: text("content").notNull(),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().$onUpdate(() => new Date()).notNull(),
});

Tool Configuration

// apps/web/src/ai/agents/general.ts
memory: anthropic.tools.memory_20250818({
  execute: async (action) => {
    return await executeMemoryCommand(ctx.userId, action);
  },
})

Memory Commands

All memory operations are performed through a single memory tool with different commands.

VIEW

View directory contents or file contents.
command
string
required
"view"
path
string
required
Path to file or directory (must start with /memories)
view_range
[number, number]
Optional line range for viewing files: [startLine, endLine]
Examples:
{
  "command": "view",
  "path": "/memories"
}
Returns:
  • For directories: File listing with sizes up to 2 levels deep
  • For files: Content with line numbers (6 chars, right-aligned, space-padded)

CREATE

Create a new file.
command
string
required
"create"
path
string
required
Path for new file (must start with /memories)
file_text
string
required
Content of the new file
Example:
{
  "command": "create",
  "path": "/memories/preferences.txt",
  "file_text": "Favorite subject: Mathematics\nPreferred study time: Evening\nLearning style: Visual"
}
Returns: Success message or error if file already exists

STR_REPLACE

Replace text in a file.
command
string
required
"str_replace"
path
string
required
Path to file
old_str
string
required
Exact string to find (must be unique in file)
new_str
string
required
Replacement string
Example:
{
  "command": "str_replace",
  "path": "/memories/preferences.txt",
  "old_str": "Preferred study time: Evening",
  "new_str": "Preferred study time: Morning"
}
Returns:
  • Success: Snippet of edited content with line numbers
  • Error: If old_str not found or appears multiple times

INSERT

Insert text at a specific line.
command
string
required
"insert"
path
string
required
Path to file
insert_line
number
required
Line number to insert at (0-based)
insert_text
string
required
Text to insert
Example:
{
  "command": "insert",
  "path": "/memories/preferences.txt",
  "insert_line": 3,
  "insert_text": "Goals: Improve calculus grade to A"
}

DELETE

Delete a file or directory.
command
string
required
"delete"
path
string
required
Path to file or directory
Example:
{
  "command": "delete",
  "path": "/memories/old-notes.txt"
}
Note: Deleting a directory removes all contents recursively.

RENAME

Rename or move a file/directory.
command
string
required
"rename"
old_path
string
required
Current path
new_path
string
required
New path (must not already exist)
Example:
{
  "command": "rename",
  "old_path": "/memories/draft.txt",
  "new_path": "/memories/final.txt"
}

Implementation Details

Path Validation

All paths must:
  • Start with /memories
  • Not contain directory traversal patterns (../, ..\\, %2e%2e)
function validatePath(path: string): void {
  if (!path.startsWith("/memories")) {
    throw new Error("All memory paths must start with /memories");
  }

  if (path.includes("../") || path.includes("..\\") || path.includes("%2e%2e")) {
    throw new Error("Invalid path: directory traversal detected");
  }
}

File vs Directory Detection

function isFilePath(path: string): boolean {
  const parts = path.split("/");
  const lastPart = parts[parts.length - 1];
  return lastPart.includes(".");
}

Size Formatting

function formatSize(bytes: number): string {
  if (bytes < 1024) return `${bytes}B`;
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}K`;
  return `${(bytes / (1024 * 1024)).toFixed(1)}M`;
}

Usage Patterns

Storing User Preferences

// Agent learns preferences and creates memory file
{
  "command": "create",
  "path": "/memories/preferences.txt",
  "file_text": `User Preferences
================

Favorite subjects: Mathematics, Physics
Preferred study time: Evening (7-9 PM)
Learning style: Visual learner, prefers diagrams
Weakness areas: Organic chemistry, essay writing
Strengths: Problem-solving, analytical thinking

Goals for this semester:
- Raise calculus grade from B+ to A
- Complete physics project early
- Improve writing skills
`
}

Organizing by Category

/memories/
  preferences.txt
  academic/
    calculus-notes.txt
    physics-formulas.txt
  personal/
    interests.txt
    schedule.txt
  canvas/
    class-nicknames.txt

Storing Important Facts

/memories/facts.txt

Important Facts About User
==========================

Name: Alex Johnson
School: Boston University
Major: Computer Science (sophomore)

Canvas Preferences:
- Calls "MATH 225 - Calculus II" -> "Calc"
- Calls "CS 112 - Data Structures" -> "DS"
- Calls "PHYS 201 - Physics I" -> "Physics"

Study Habits:
- Studies best with music
- Takes breaks every 45 minutes
- Prefers evening study sessions

Known Issues:
- Canvas API sometimes slow for Physics class
- Prefers not to schedule todos before 9am

System Prompt Guidance

From buildSystemPrompt():
10. **memory**: Store and retrieve important information about the user
    - Use to remember user preferences, facts, goals, etc.
    - Check memory before asking users to repeat information
    - Proactively save important facts the user shares
Behavioral notes:
- **Use memory to avoid asking redundant questions** - check stored memory before asking for more information
- **Proactively save important facts** about the user to memory

Implementation Reference

// apps/web/src/ai/utils/memory-helpers.ts
export async function executeMemoryCommand(
  userId: string,
  action: any,
): Promise<string> {
  try {
    switch (action.command) {
      case "view":
        return await viewMemory(userId, action.path, action.view_range);
      case "create":
        return await createMemory(userId, action.path, action.file_text);
      case "str_replace":
        return await replaceInMemory(
          userId,
          action.path,
          action.old_str,
          action.new_str,
        );
      case "insert":
        return await insertInMemory(
          userId,
          action.path,
          action.insert_line,
          action.insert_text,
        );
      case "delete":
        return await deleteMemory(userId, action.path);
      case "rename":
        return await renameMemory(userId, action.old_path, action.new_path);
      default:
        return `Error: Unknown command ${action.command}`;
    }
  } catch (error) {
    console.error("Memory tool error:", error);
    return `Error: ${error instanceof Error ? error.message : String(error)}`;
  }
}

Best Practices

Always view memory files before asking the user to repeat information:
# Agent should do this before asking about preferences
result = memory({"command": "view", "path": "/memories/preferences.txt"})
if "does not exist" not in result:
    # Use existing preferences
    preferences = result
else:
    # Ask user for preferences and save them
When users share important information, save it immediately:
User: "I prefer studying in the evening, usually around 7pm"

Agent: [Saves to memory]
{
  "command": "create",
  "path": "/memories/preferences.txt",
  "file_text": "Preferred study time: Evening (around 7pm)\n"
}

[Then responds] "Got it! I'll remember that you prefer evening study sessions around 7pm."
Use subdirectories for better organization:
  • /memories/preferences.txt - Core user preferences
  • /memories/academic/ - Academic-related notes
  • /memories/personal/ - Personal facts and interests
  • /memories/canvas/ - Canvas-specific customizations
Create separate files for different categories rather than one large file:✅ Good:
  • /memories/study-preferences.txt
  • /memories/course-nicknames.txt
  • /memories/academic-goals.txt
❌ Avoid:
  • /memories/everything.txt (too broad)

Persistence Across Conversations

Memory persists indefinitely across all conversations for a user:
  1. Conversation 1: User shares preferences → Agent saves to /memories/preferences.txt
  2. Conversation 2 (next day): Agent reads /memories/preferences.txt → Uses preferences without asking
  3. Conversation 3 (next week): User updates preferences → Agent updates file with str_replace
This enables truly personalized, context-aware assistance that improves over time.

Next Steps

General Agent

Learn how memory is used in the system prompt

Available Tools

Explore other AI tools and capabilities

Build docs developers (and LLMs) love