Memory.cloneThread()
The .cloneThread() method creates a copy of an existing conversation thread, including all its messages. This enables creating divergent conversation paths from a specific point in a conversation. When semantic recall is enabled, the method also creates vector embeddings for the cloned messages.
Usage exampleDirect link to Usage example
The following example creates a Memory instance and clones an existing thread.
import { Memory } from '@mastra/memory'
import { LibSQLStore } from '@mastra/libsql'
const memory = new Memory({
storage: new LibSQLStore({ id: 'memory-store', url: 'file:./memory.db' }),
})
const { thread, clonedMessages } = await memory.cloneThread({
sourceThreadId: 'original-thread-123',
})
ParametersDirect link to Parameters
sourceThreadId:
newThreadId?:
resourceId?:
title?:
Clone of ${sourceThread.title} when the source thread has a title. Otherwise, the title is empty.metadata?:
options?:
messageLimit?:
messageFilter?:
startDate?:
endDate?:
messageIds?:
ReturnsDirect link to Returns
thread:
clonedMessages:
messageIdMap?:
Clone metadataDirect link to Clone metadata
The cloned thread's metadata includes a clone property with:
sourceThreadId:
clonedAt:
lastMessageId?:
Extended usage exampleDirect link to Extended usage example
import { mastra } from './mastra'
const agent = mastra.getAgent('agent')
const memory = await agent.getMemory()
// Clone a thread with all messages
const { thread: fullClone } = await memory.cloneThread({
sourceThreadId: 'original-thread-123',
title: 'Alternative Conversation Path',
})
// Clone with a custom ID
const { thread: customIdClone } = await memory.cloneThread({
sourceThreadId: 'original-thread-123',
newThreadId: 'my-custom-clone-id',
})
// Clone only the last 5 messages
const { thread: partialClone, clonedMessages } = await memory.cloneThread({
sourceThreadId: 'original-thread-123',
options: {
messageLimit: 5,
},
})
// Clone messages from a specific date range
const { thread: dateFilteredClone } = await memory.cloneThread({
sourceThreadId: 'original-thread-123',
options: {
messageFilter: {
startDate: new Date('2024-01-01'),
endDate: new Date('2024-01-31'),
},
},
})
// Clone specific messages
const { thread: selectedMessagesClone } = await memory.cloneThread({
sourceThreadId: 'original-thread-123',
options: {
messageFilter: {
messageIds: ['message-1', 'message-2'],
},
},
})
// Continue conversation on the cloned thread
const response = await agent.generate('Try a different approach', {
memory: {
thread: fullClone.id,
resource: fullClone.resourceId,
},
})
Pass the cloned thread.id and thread.resourceId to agent.generate() to continue the conversation from the cloned thread.
Vector embeddingsDirect link to Vector embeddings
When the Memory instance has semantic recall enabled with a vector store and embedder configured, cloneThread() automatically creates vector embeddings for all cloned messages. This ensures that semantic search works correctly on the cloned thread.
In this example, embeddingModel is the embedding model configured for the project.
import { Memory } from '@mastra/memory'
import { LibSQLStore, LibSQLVector } from '@mastra/libsql'
const memory = new Memory({
storage: new LibSQLStore({ id: 'memory-store', url: 'file:./memory.db' }),
vector: new LibSQLVector({ id: 'vector-store', url: 'file:./vector.db' }),
embedder: embeddingModel,
options: {
semanticRecall: true,
},
})
// Clone will also create embeddings for cloned messages
const { thread } = await memory.cloneThread({
sourceThreadId: 'original-thread',
})
// Semantic search works on the cloned thread
const results = await memory.recall({
threadId: thread.id,
vectorSearchString: 'search query',
})
Working memoryDirect link to Working memory
When working memory is enabled, cloneThread() copies or shares working memory based on the working-memory scope and the clone's resourceId:
- Thread-scoped working memory: The working memory is copied to the cloned thread.
- Resource-scoped working memory with the same
resourceId: The working memory is shared because the source and cloned threads belong to the same resource. - Resource-scoped working memory with a different
resourceId: The working memory is copied to the cloned thread's resource.
Observational MemoryDirect link to Observational Memory
When Observational Memory is enabled, cloneThread() automatically clones the OM records associated with the source thread. The behavior depends on the OM scope:
- Thread-scoped OM: The OM record is cloned to the new thread. All internal message ID references are remapped to point to the cloned messages.
- Resource-scoped OM (same
resourceId): The OM record is shared between the source and cloned threads since they belong to the same resource. No duplication occurs. - Resource-scoped OM (different
resourceId): The OM record is cloned to the new resource. Message IDs are remapped and any thread-identifying tags within observations are updated to reference the cloned thread.
Only the current (most recent) OM generation is cloned — older history generations aren't copied. Transient processing state (observation/reflection in-progress flags) is reset on the cloned record.