Skip to main content
Working memory provides agents with a persistent, structured record of user information that evolves throughout conversations. It enables personalization and context continuity across sessions.

How It Works

Working memory operates through two components:
  1. WorkingMemory Processor: Injects memory data as a system message and provides instructions
  2. updateWorkingMemory Tool: Allows agents to update the memory structure
The processor is input-only - it retrieves and injects memory, while updates happen via tool calls.

Basic Configuration

Enable working memory with a template:
import { Memory } from '@mastra/core';
import { LibSQLStore } from '@mastra/store-libsql';

const memory = new Memory({
  storage: new LibSQLStore({
    id: 'agent-memory',
    url: 'file:./memory.db'
  }),
  options: {
    workingMemory: {
      enabled: true,
      scope: 'resource', // Shared across all user threads
      template: `
# User Profile
- **First Name**: 
- **Last Name**: 
- **Location**: 
- **Occupation**: 
- **Interests**: 
- **Goals**: 
- **Preferences**: 
      `
    }
  }
});

Configuration Options

enabled
boolean
required
Enable or disable working memory
scope
'thread' | 'resource'
default:"'resource'"
Memory scope:
  • resource: Shared across all threads for a user (recommended)
  • thread: Isolated per conversation thread
template
string
Markdown template defining the memory structure
schema
ZodObject | JSONSchema7
JSON schema for structured memory (alternative to template)
version
'stable' | 'vnext'
default:"'stable'"
Memory update behavior version:
  • stable: Updates on every relevant message
  • vnext: Only updates when information changes

Template-Based Memory

Define memory structure with a Markdown template:
const memory = new Memory({
  storage,
  options: {
    workingMemory: {
      enabled: true,
      template: `
# Customer Profile

## Personal Information
- **Name**: 
- **Email**: 
- **Phone**: 

## Preferences
- **Communication Style**: 
- **Preferred Contact Time**: 
- **Topics of Interest**: 

## Interaction History
- **Last Issue**: 
- **Resolution Status**: 
- **Follow-up Required**: 
      `
    }
  }
});
The agent will automatically update this structure as it learns information:
const agent = new Agent({
  name: 'Support Agent',
  model: 'openai/gpt-4o',
  memory
});

// User provides information
const result = await agent.generate(
  "Hi, I'm John Smith. I prefer email communication and I'm interested in your enterprise plans.",
  {
    threadId: 'thread-123',
    resourceId: 'user-123'
  }
);

// Agent automatically updates working memory:
/*
# Customer Profile

## Personal Information
- **Name**: John Smith
- **Email**: 
- **Phone**: 

## Preferences
- **Communication Style**: Email preferred
- **Preferred Contact Time**: 
- **Topics of Interest**: Enterprise plans

## Interaction History
- **Last Issue**: 
- **Resolution Status**: 
- **Follow-up Required**: 
*/

Schema-Based Memory

Use a Zod schema for type-safe structured memory:
import { z } from 'zod';

const memory = new Memory({
  storage,
  options: {
    workingMemory: {
      enabled: true,
      schema: z.object({
        personalInfo: z.object({
          name: z.string(),
          email: z.string().email().optional(),
          phone: z.string().optional()
        }),
        preferences: z.object({
          communicationStyle: z.string(),
          topics: z.array(z.string())
        }),
        context: z.object({
          lastIssue: z.string().optional(),
          resolutionStatus: z.enum(['open', 'in-progress', 'resolved']).optional()
        })
      })
    }
  }
});

Memory Scopes

Memory persists across all threads for a user:
const memory = new Memory({
  storage,
  options: {
    workingMemory: {
      enabled: true,
      scope: 'resource' // Shared across all threads
    }
  }
});

// Thread 1: User provides preferences
await agent.generate("I prefer morning meetings", {
  threadId: 'thread-1',
  resourceId: 'user-123'
});

// Thread 2: Agent remembers from resource memory
await agent.generate("When can we schedule a call?", {
  threadId: 'thread-2', // Different thread
  resourceId: 'user-123' // Same resource
});
// Agent: "Based on your preference for morning meetings, how about 10 AM?"

Thread Scope

Memory is isolated to a single thread:
const memory = new Memory({
  storage,
  options: {
    workingMemory: {
      enabled: true,
      scope: 'thread' // Per-thread memory
    }
  }
});

// Each thread maintains its own working memory

Update Behavior Versions

Stable (Default)

Agent updates memory on every relevant message:
const memory = new Memory({
  storage,
  options: {
    workingMemory: {
      enabled: true,
      // version: 'stable' (default)
    }
  }
});

VNext

Agent only updates when information actually changes:
const memory = new Memory({
  storage,
  options: {
    workingMemory: {
      enabled: true,
      version: 'vnext'
    }
  }
});
VNext reduces unnecessary updates and tool calls, improving performance for long conversations.

Read-Only Mode

Provide memory context without allowing updates:
const routingAgent = new Agent({
  name: 'Router',
  model: 'openai/gpt-4o-mini',
  memory,
  memoryConfig: {
    readOnly: true // Can read working memory but not update
  }
});
In read-only mode:
  • Memory data is still injected as context
  • No updateWorkingMemory tool is provided
  • No update instructions are included

Retrieving Working Memory

Access working memory programmatically:
// Get working memory for a thread
const threadMemory = await memory.getWorkingMemory({
  threadId: 'thread-123',
  resourceId: 'user-123'
});

console.log(threadMemory);
/*
# User Profile
- **First Name**: John
- **Last Name**: Smith
- **Location**: San Francisco
- **Occupation**: Software Engineer
*/

// Get working memory template
const template = await memory.getWorkingMemoryTemplate({
  memoryConfig: {
    workingMemory: {
      enabled: true
    }
  }
});

Manual Updates

Update working memory directly:
await memory.updateWorkingMemory({
  threadId: 'thread-123',
  resourceId: 'user-123',
  workingMemory: `
# User Profile
- **First Name**: John
- **Last Name**: Smith
- **Location**: San Francisco
- **Occupation**: Software Engineer
- **Interests**: AI, Machine Learning
  `
});

Custom Instructions

Provide domain-specific guidance for memory updates:
const memory = new Memory({
  storage,
  options: {
    workingMemory: {
      enabled: true,
      template: `
# Sales Lead Profile
- **Company**: 
- **Industry**: 
- **Budget Range**: 
- **Decision Timeline**: 
- **Pain Points**: 
      `
    }
  }
});

const agent = new Agent({
  name: 'Sales Agent',
  model: 'openai/gpt-4o',
  instructions: `You are a sales agent helping qualify leads.

Focus on understanding:
1. Company background and size
2. Budget constraints
3. Decision-making timeline
4. Key pain points and challenges

Update working memory as you gather this information.`,
  memory
});

Implementation Details

The WorkingMemory processor injects memory as a system message:
class WorkingMemory implements Processor {
  readonly id = 'working-memory';
  
  async processInput(args) {
    const { messageList, requestContext } = args;
    
    // Get memory context
    const memoryContext = parseMemoryRequestContext(requestContext);
    const threadId = memoryContext?.thread?.id;
    const resourceId = memoryContext?.resourceId;
    
    if (!threadId && !resourceId) return messageList;
    
    // Determine scope
    const scope = this.options.scope || 'resource';
    
    // Retrieve working memory
    let workingMemoryData: string | null = null;
    
    if (scope === 'thread' && threadId) {
      const thread = await this.options.storage.getThreadById({ threadId });
      workingMemoryData = thread?.metadata?.workingMemory || null;
    } else if (scope === 'resource' && resourceId) {
      const resource = await this.options.storage.getResourceById({ resourceId });
      workingMemoryData = resource?.workingMemory || null;
    }
    
    // Get template
    const template = this.options.template || {
      format: 'markdown',
      content: this.defaultWorkingMemoryTemplate
    };
    
    // Check if read-only
    const isReadOnly = this.options.readOnly || 
      memoryContext.memoryConfig?.readOnly;
    
    // Format instruction
    let instruction: string;
    if (isReadOnly) {
      instruction = this.getReadOnlyWorkingMemoryInstruction({
        template,
        data: workingMemoryData
      });
    } else if (this.options.useVNext) {
      instruction = this.getWorkingMemoryToolInstructionVNext({
        template,
        data: workingMemoryData
      });
    } else {
      instruction = this.getWorkingMemoryToolInstruction({
        template,
        data: workingMemoryData
      });
    }
    
    // Add to message list
    if (instruction) {
      messageList.addSystem(instruction, 'memory');
    }
    
    return messageList;
  }
}

Best Practices

Use Resource Scope

Default to resource scope for user-level information that should persist across threads.

Keep Templates Focused

Only include information relevant to the agent’s purpose. Avoid bloated templates.

Use VNext for Long Conversations

Enable version: 'vnext' to reduce unnecessary updates in extended interactions.

Read-Only for Routing

Use read-only mode for orchestration agents to prevent memory pollution.

Use Cases

workingMemory: {
  enabled: true,
  template: `
# Support Ticket Context
- **Issue Category**: 
- **Priority Level**: 
- **Previous Issues**: 
- **Resolution History**: 
- **Customer Sentiment**: 
  `
}
workingMemory: {
  enabled: true,
  template: `
# User Context
- **Name**: 
- **Timezone**: 
- **Work Schedule**: 
- **Current Projects**: 
- **Important Dates**: 
- **Communication Preferences**: 
  `
}
workingMemory: {
  enabled: true,
  template: `
# Lead Information
- **Company Name**: 
- **Industry**: 
- **Company Size**: 
- **Budget Range**: 
- **Decision Timeline**: 
- **Key Pain Points**: 
- **Decision Makers**: 
  `
}

Next Steps

Conversation History

Manage recent message persistence

Semantic Recall

Add RAG-based retrieval for older messages

Memory Overview

Learn about all memory types in Mastra

Build docs developers (and LLMs) love