Skip to main content

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 example
Direct 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',
})

Parameters
Direct link to Parameters

sourceThreadId:

string
The ID of the thread to clone

newThreadId?:

string
Optional custom ID for the cloned thread. If not provided, one will be generated.

resourceId?:

string
Optional resource ID for the cloned thread. Defaults to the source thread's resourceId.

title?:

string
Optional title for the cloned thread. If omitted, the clone uses Clone of ${sourceThread.title} when the source thread has a title. Otherwise, the title is empty.

metadata?:

Record<string, unknown>
Optional metadata to merge with the source thread's metadata. Clone metadata is automatically added.

options?:

CloneOptions
Optional filtering options for the clone operation.
CloneOptions

messageLimit?:

number
Maximum number of messages to clone. When set, clones the most recent N messages.

messageFilter?:

MessageFilter
Filter criteria for selecting which messages to clone.
MessageFilter

startDate?:

Date
Only clone messages created on or after this date.

endDate?:

Date
Only clone messages created on or before this date.

messageIds?:

string[]
Only clone messages with these specific IDs.

Returns
Direct link to Returns

thread:

StorageThreadType
The newly created cloned thread with clone metadata.

clonedMessages:

MastraDBMessage[]
Array of the cloned messages with new IDs assigned to the new thread.

messageIdMap?:

Record<string, string>
A mapping from source message IDs to their corresponding cloned message IDs.

Clone metadata
Direct link to Clone metadata

The cloned thread's metadata includes a clone property with:

sourceThreadId:

string
The ID of the original thread that was cloned.

clonedAt:

Date
Timestamp when the clone was created.

lastMessageId?:

string
The ID of the last message in the source thread at the time of cloning.

Extended usage example
Direct link to Extended usage example

src/test-clone.ts
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 embeddings
Direct 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 memory
Direct 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 Memory
Direct 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.