@mastra/libsql 0.0.0-fix-thread-subscription-multi-instance-redis-streams-20260720232722 → 0.0.0-fix-thread-subscription-multi-instance-redis-streams-20260730204150
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +249 -9
- package/dist/docs/SKILL.md +4 -3
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/docs/references/docs-agent-builder-deploying.md +2 -2
- package/dist/docs/references/docs-agent-builder-overview.md +3 -1
- package/dist/docs/references/docs-agents-agent-approval.md +85 -11
- package/dist/docs/references/docs-agents-networks.md +2 -2
- package/dist/docs/references/docs-deployment-workers.md +137 -0
- package/dist/docs/references/docs-memory-memory-processors.md +10 -10
- package/dist/docs/references/docs-memory-message-history.md +29 -5
- package/dist/docs/references/docs-memory-multi-user-threads.md +6 -6
- package/dist/docs/references/docs-memory-overview.md +10 -10
- package/dist/docs/references/docs-memory-semantic-recall.md +3 -3
- package/dist/docs/references/docs-memory-working-memory.md +8 -8
- package/dist/docs/references/docs-rag-retrieval.md +18 -18
- package/dist/docs/references/docs-storage-overview.md +2 -2
- package/dist/docs/references/docs-workflows-snapshots.md +3 -3
- package/dist/docs/references/reference-core-mastra-class.md +48 -1
- package/dist/docs/references/reference-file-based-agents-storage.md +1 -1
- package/dist/docs/references/reference-memory-memory-class.md +5 -5
- package/dist/docs/references/reference-storage-dynamodb.md +7 -7
- package/dist/docs/references/reference-storage-retention.md +33 -33
- package/dist/docs/references/reference-vectors-libsql.md +2 -2
- package/dist/index.cjs +12742 -13175
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +12708 -13141
- package/dist/index.js.map +1 -1
- package/dist/storage/domains/datasets/index.d.ts.map +1 -1
- package/dist/storage/domains/experiments/index.d.ts.map +1 -1
- package/dist/storage/domains/memory/index.d.ts.map +1 -1
- package/dist/storage/factory-storage.d.ts +28 -0
- package/dist/storage/factory-storage.d.ts.map +1 -0
- package/dist/storage/index.d.ts +2 -9
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/vector/index.d.ts +1 -7
- package/dist/vector/index.d.ts.map +1 -1
- package/package.json +13 -12
|
@@ -160,15 +160,15 @@ When creating the tool, pay special attention to the tool's name and description
|
|
|
160
160
|
|
|
161
161
|
This is particularly useful when:
|
|
162
162
|
|
|
163
|
-
- Your agent needs to
|
|
163
|
+
- Your agent needs to decide at runtime what information to retrieve
|
|
164
164
|
- The retrieval process requires complex decision-making
|
|
165
165
|
- You want the agent to combine multiple retrieval strategies based on context
|
|
166
166
|
|
|
167
167
|
#### Database-Specific Configurations
|
|
168
168
|
|
|
169
|
-
The Vector Query Tool supports database-specific configurations that enable you to
|
|
169
|
+
The Vector Query Tool supports database-specific configurations that enable you to use unique features and optimizations of different vector stores.
|
|
170
170
|
|
|
171
|
-
> **Note:** These configurations are for **query-time options** like namespaces, performance tuning, and filtering
|
|
171
|
+
> **Note:** These configurations are for **query-time options** like namespaces, performance tuning, and filtering, not for database connection setup.
|
|
172
172
|
>
|
|
173
173
|
> Connection credentials (URLs, auth tokens) are configured when you instantiate the vector store class (e.g., `new LibSQLVector({ url: '...' })`).
|
|
174
174
|
|
|
@@ -235,7 +235,7 @@ const lanceQueryTool = createVectorQueryTool({
|
|
|
235
235
|
- **pgVector optimization**: Control search accuracy and speed with ef/probes parameters
|
|
236
236
|
- **Quality filtering**: Set minimum similarity thresholds to improve result relevance
|
|
237
237
|
- **LanceDB tables**: Separate data into tables for better organization and performance
|
|
238
|
-
- **Runtime flexibility**: Override configurations
|
|
238
|
+
- **Runtime flexibility**: Override configurations at runtime based on context
|
|
239
239
|
|
|
240
240
|
**Common Use Cases:**
|
|
241
241
|
|
|
@@ -274,7 +274,7 @@ import { PGVECTOR_PROMPT } from '@mastra/pg'
|
|
|
274
274
|
export const ragAgent = new Agent({
|
|
275
275
|
id: 'rag-agent',
|
|
276
276
|
name: 'RAG Agent',
|
|
277
|
-
model: 'openai/gpt-5.
|
|
277
|
+
model: 'openai/gpt-5.6-sol',
|
|
278
278
|
instructions: `
|
|
279
279
|
Process queries using the provided context. Structure responses to be concise and relevant.
|
|
280
280
|
${PGVECTOR_PROMPT}
|
|
@@ -291,7 +291,7 @@ import { PINECONE_PROMPT } from '@mastra/pinecone'
|
|
|
291
291
|
export const ragAgent = new Agent({
|
|
292
292
|
id: 'rag-agent',
|
|
293
293
|
name: 'RAG Agent',
|
|
294
|
-
model: 'openai/gpt-5.
|
|
294
|
+
model: 'openai/gpt-5.6-sol',
|
|
295
295
|
instructions: `
|
|
296
296
|
Process queries using the provided context. Structure responses to be concise and relevant.
|
|
297
297
|
${PINECONE_PROMPT}
|
|
@@ -308,7 +308,7 @@ import { QDRANT_PROMPT } from '@mastra/qdrant'
|
|
|
308
308
|
export const ragAgent = new Agent({
|
|
309
309
|
id: 'rag-agent',
|
|
310
310
|
name: 'RAG Agent',
|
|
311
|
-
model: 'openai/gpt-5.
|
|
311
|
+
model: 'openai/gpt-5.6-sol',
|
|
312
312
|
instructions: `
|
|
313
313
|
Process queries using the provided context. Structure responses to be concise and relevant.
|
|
314
314
|
${QDRANT_PROMPT}
|
|
@@ -325,7 +325,7 @@ import { CHROMA_PROMPT } from '@mastra/chroma'
|
|
|
325
325
|
export const ragAgent = new Agent({
|
|
326
326
|
id: 'rag-agent',
|
|
327
327
|
name: 'RAG Agent',
|
|
328
|
-
model: 'openai/gpt-5.
|
|
328
|
+
model: 'openai/gpt-5.6-sol',
|
|
329
329
|
instructions: `
|
|
330
330
|
Process queries using the provided context. Structure responses to be concise and relevant.
|
|
331
331
|
${CHROMA_PROMPT}
|
|
@@ -342,7 +342,7 @@ import { ASTRA_PROMPT } from '@mastra/astra'
|
|
|
342
342
|
export const ragAgent = new Agent({
|
|
343
343
|
id: 'rag-agent',
|
|
344
344
|
name: 'RAG Agent',
|
|
345
|
-
model: 'openai/gpt-5.
|
|
345
|
+
model: 'openai/gpt-5.6-sol',
|
|
346
346
|
instructions: `
|
|
347
347
|
Process queries using the provided context. Structure responses to be concise and relevant.
|
|
348
348
|
${ASTRA_PROMPT}
|
|
@@ -359,7 +359,7 @@ import { LIBSQL_PROMPT } from '@mastra/libsql'
|
|
|
359
359
|
export const ragAgent = new Agent({
|
|
360
360
|
id: 'rag-agent',
|
|
361
361
|
name: 'RAG Agent',
|
|
362
|
-
model: 'openai/gpt-5.
|
|
362
|
+
model: 'openai/gpt-5.6-sol',
|
|
363
363
|
instructions: `
|
|
364
364
|
Process queries using the provided context. Structure responses to be concise and relevant.
|
|
365
365
|
${LIBSQL_PROMPT}
|
|
@@ -376,7 +376,7 @@ import { UPSTASH_PROMPT } from '@mastra/upstash'
|
|
|
376
376
|
export const ragAgent = new Agent({
|
|
377
377
|
id: 'rag-agent',
|
|
378
378
|
name: 'RAG Agent',
|
|
379
|
-
model: 'openai/gpt-5.
|
|
379
|
+
model: 'openai/gpt-5.6-sol',
|
|
380
380
|
instructions: `
|
|
381
381
|
Process queries using the provided context. Structure responses to be concise and relevant.
|
|
382
382
|
${UPSTASH_PROMPT}
|
|
@@ -393,7 +393,7 @@ import { VECTORIZE_PROMPT } from '@mastra/vectorize'
|
|
|
393
393
|
export const ragAgent = new Agent({
|
|
394
394
|
id: 'rag-agent',
|
|
395
395
|
name: 'RAG Agent',
|
|
396
|
-
model: 'openai/gpt-5.
|
|
396
|
+
model: 'openai/gpt-5.6-sol',
|
|
397
397
|
instructions: `
|
|
398
398
|
Process queries using the provided context. Structure responses to be concise and relevant.
|
|
399
399
|
${VECTORIZE_PROMPT}
|
|
@@ -410,7 +410,7 @@ import { MONGODB_PROMPT } from '@mastra/mongodb'
|
|
|
410
410
|
export const ragAgent = new Agent({
|
|
411
411
|
id: 'rag-agent',
|
|
412
412
|
name: 'RAG Agent',
|
|
413
|
-
model: 'openai/gpt-5.
|
|
413
|
+
model: 'openai/gpt-5.6-sol',
|
|
414
414
|
instructions: `
|
|
415
415
|
Process queries using the provided context. Structure responses to be concise and relevant.
|
|
416
416
|
${MONGODB_PROMPT}
|
|
@@ -427,7 +427,7 @@ import { OPENSEARCH_PROMPT } from '@mastra/opensearch'
|
|
|
427
427
|
export const ragAgent = new Agent({
|
|
428
428
|
id: 'rag-agent',
|
|
429
429
|
name: 'RAG Agent',
|
|
430
|
-
model: 'openai/gpt-5.
|
|
430
|
+
model: 'openai/gpt-5.6-sol',
|
|
431
431
|
instructions: `
|
|
432
432
|
Process queries using the provided context. Structure responses to be concise and relevant.
|
|
433
433
|
${OPENSEARCH_PROMPT}
|
|
@@ -444,7 +444,7 @@ import { S3VECTORS_PROMPT } from '@mastra/s3vectors'
|
|
|
444
444
|
export const ragAgent = new Agent({
|
|
445
445
|
id: 'rag-agent',
|
|
446
446
|
name: 'RAG Agent',
|
|
447
|
-
model: 'openai/gpt-5.
|
|
447
|
+
model: 'openai/gpt-5.6-sol',
|
|
448
448
|
instructions: `
|
|
449
449
|
Process queries using the provided context. Structure responses to be concise and relevant.
|
|
450
450
|
${S3VECTORS_PROMPT}
|
|
@@ -455,10 +455,10 @@ export const ragAgent = new Agent({
|
|
|
455
455
|
|
|
456
456
|
### Re-ranking
|
|
457
457
|
|
|
458
|
-
Initial vector similarity search can sometimes miss
|
|
458
|
+
Initial vector similarity search can sometimes miss detailed relevance. Re-ranking is a more computationally expensive process, but more accurate algorithm that improves results by:
|
|
459
459
|
|
|
460
460
|
- Considering word order and exact matches
|
|
461
|
-
- Applying more
|
|
461
|
+
- Applying more advanced relevance scoring
|
|
462
462
|
- Using a method called cross-attention between query and documents
|
|
463
463
|
|
|
464
464
|
Here's how to use re-ranking:
|
|
@@ -476,7 +476,7 @@ const initialResults = await pgVector.query({
|
|
|
476
476
|
// Create a relevance scorer
|
|
477
477
|
const relevanceProvider = new MastraAgentRelevanceScorer(
|
|
478
478
|
'relevance-scorer',
|
|
479
|
-
'openai/gpt-5.
|
|
479
|
+
'openai/gpt-5.6-sol',
|
|
480
480
|
)
|
|
481
481
|
|
|
482
482
|
// Re-rank the results
|
|
@@ -14,7 +14,7 @@ Storage powers:
|
|
|
14
14
|
|
|
15
15
|
## When to configure storage
|
|
16
16
|
|
|
17
|
-
Configure a persistent storage adapter when state must survive restarts
|
|
17
|
+
Configure a persistent storage adapter when state must survive restarts or be shared across processes. Persistent storage also keeps state visible in Studio across sessions. The default in-memory store is useful for tests and short local experiments, but it loses data when the process exits.
|
|
18
18
|
|
|
19
19
|
Use storage when your application needs any of these behaviors:
|
|
20
20
|
|
|
@@ -144,7 +144,7 @@ export const supportAgent = new Agent({
|
|
|
144
144
|
id: 'support-agent',
|
|
145
145
|
name: 'Support agent',
|
|
146
146
|
instructions: 'Answer customer support questions.',
|
|
147
|
-
model: 'openai/gpt-5.
|
|
147
|
+
model: 'openai/gpt-5.6-sol',
|
|
148
148
|
memory: new Memory({
|
|
149
149
|
storage: new PostgresStore({
|
|
150
150
|
id: 'support-agent-storage',
|
|
@@ -24,11 +24,11 @@ Snapshots are the key mechanism enabling Mastra's suspend and resume capabilitie
|
|
|
24
24
|
5. Later, when `resume()` is called on the suspended step, the snapshot is retrieved
|
|
25
25
|
6. The workflow execution resumes from exactly where it left off
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
The mechanism provides a powerful way to implement human-in-the-loop workflows, handle rate limiting, wait for external resources, and implement complex branching workflows that may need to pause for extended periods.
|
|
28
28
|
|
|
29
29
|
## Snapshot anatomy
|
|
30
30
|
|
|
31
|
-
Each snapshot includes the `runId`, input, step status (`success`, `suspended`, etc.), any suspend and resume payloads, and the final output.
|
|
31
|
+
Each snapshot includes the `runId`, input, step status (`success`, `suspended`, etc.), any suspend and resume payloads, and the final output. As a result, full context is available when resuming execution.
|
|
32
32
|
|
|
33
33
|
```json
|
|
34
34
|
{
|
|
@@ -152,7 +152,7 @@ export const mastra = new Mastra({
|
|
|
152
152
|
1. **Ensure Serializability**: Any data that needs to be included in the snapshot must be serializable (convertible to JSON).
|
|
153
153
|
2. **Minimize Snapshot Size**: Avoid storing large data objects directly in the workflow context. Instead, store references to them (like IDs) and retrieve the data when needed.
|
|
154
154
|
3. **Handle Resume Context Carefully**: When resuming a workflow, carefully consider what context to provide. This will be merged with the existing snapshot data.
|
|
155
|
-
4. **Set Up Proper Monitoring**: Implement monitoring for suspended workflows
|
|
155
|
+
4. **Set Up Proper Monitoring**: Implement monitoring for suspended workflows and especially long-running ones, plus to ensure they're properly resumed.
|
|
156
156
|
5. **Consider Storage Scaling**: For applications with many suspended workflows, ensure your storage solution is appropriately scaled.
|
|
157
157
|
|
|
158
158
|
## Custom snapshot metadata
|
|
@@ -103,4 +103,51 @@ Visit the [Configuration reference](https://mastra.ai/reference/configuration) f
|
|
|
103
103
|
|
|
104
104
|
**versions.agents.versionId** (`string`): The ID of a specific version to use.
|
|
105
105
|
|
|
106
|
-
**versions.agents.status** (`'draft' | 'published'`): Select the latest version with this publication status.
|
|
106
|
+
**versions.agents.status** (`'draft' | 'published'`): Select the latest version with this publication status.
|
|
107
|
+
|
|
108
|
+
**workers** (`MastraWorker[] | false`): Configure which workers run in this Mastra instance. When omitted, Mastra auto-creates default workers based on your PubSub and config. Pass false to disable all event processing (useful when running standalone workers separately). Pass a MastraWorker\[] to add custom workers — they are merged with the auto-created defaults, and a custom worker with the same name as a default replaces it.
|
|
109
|
+
|
|
110
|
+
**backgroundTasks** (`BackgroundTaskManagerConfig`): Configure background task execution for agents. See background tasks configuration reference for all options.
|
|
111
|
+
|
|
112
|
+
**backgroundTasks.enabled** (`boolean`): Enable background task dispatch.
|
|
113
|
+
|
|
114
|
+
**backgroundTasks.globalConcurrency** (`number`): Max concurrent tasks across all agents.
|
|
115
|
+
|
|
116
|
+
**backgroundTasks.perAgentConcurrency** (`number`): Max concurrent tasks per agent.
|
|
117
|
+
|
|
118
|
+
**backgroundTasks.backpressure** (`'queue' | 'reject' | 'fallback-sync'`): Behavior when concurrency limit is reached.
|
|
119
|
+
|
|
120
|
+
**backgroundTasks.defaultTimeoutMs** (`number`): Default task timeout in milliseconds.
|
|
121
|
+
|
|
122
|
+
**backgroundTasks.defaultRetries** (`RetryConfig`): Default retry configuration.
|
|
123
|
+
|
|
124
|
+
**scheduler** (`object`): Configure the scheduler worker for cron-driven workflow triggers. Auto-enables when any workflow declares a schedule. See Scheduled workflows.
|
|
125
|
+
|
|
126
|
+
**scheduler.enabled** (`boolean`): Explicitly enable or disable the scheduler.
|
|
127
|
+
|
|
128
|
+
**recovery** (`MastraRecoveryConfig`): Boot-time recovery behavior for orphaned agent and workflow runs. See Crash recovery. (Default: `{ durableAgents: 'off' }`)
|
|
129
|
+
|
|
130
|
+
**recovery.durableAgents** (`'auto' | 'off'`): Set to 'auto' to automatically re-drive orphaned RUNNING durable agent runs on server boot. Recovery re-issues LLM calls and re-executes tool calls, so tools must be idempotent. See Crash recovery.
|
|
131
|
+
|
|
132
|
+
## Methods
|
|
133
|
+
|
|
134
|
+
### `recoverAllDurableAgents()`
|
|
135
|
+
|
|
136
|
+
Re-drives every orphaned `running` durable-agent run across all registered durable agents. Called automatically on boot when `recovery.durableAgents` is `'auto'`. You can also call it directly for manual recovery or from a scheduled task.
|
|
137
|
+
|
|
138
|
+
Requires persistent storage — with an in-memory store there is nothing to recover after a process restart.
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
const result = await mastra.recoverAllDurableAgents()
|
|
142
|
+
// { agents: 2, recovered: 3, succeeded: 3, failed: 0 }
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Returns:
|
|
146
|
+
|
|
147
|
+
**agents** (`number`): Number of durable agents scanned.
|
|
148
|
+
|
|
149
|
+
**recovered** (`number`): Total number of runs that were re-driven.
|
|
150
|
+
|
|
151
|
+
**succeeded** (`number`): Runs that restarted successfully.
|
|
152
|
+
|
|
153
|
+
**failed** (`number`): Runs whose restart threw an error.
|
|
@@ -27,4 +27,4 @@ Mastra registers the store before file-based agents and workflows, so storage-de
|
|
|
27
27
|
|
|
28
28
|
## Precedence with code
|
|
29
29
|
|
|
30
|
-
Code-registered storage wins over `storage.ts`. Use `storage.ts` when one project-wide store is enough
|
|
30
|
+
Code-registered storage wins over `storage.ts`. Use `storage.ts` when one project-wide store is enough. Use code registration when setup depends on runtime wiring in `src/mastra/index.ts`.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# Memory class
|
|
4
4
|
|
|
5
|
-
The `Memory` class provides a
|
|
5
|
+
The `Memory` class provides a reliable system for managing conversation history and thread-based message storage in Mastra. It enables persistent storage of conversations, semantic search capabilities, and efficient message retrieval. You must configure a storage provider for conversation history, and if you enable semantic recall you will also need to provide a vector store and embedder.
|
|
6
6
|
|
|
7
7
|
## Usage example
|
|
8
8
|
|
|
@@ -14,7 +14,7 @@ export const agent = new Agent({
|
|
|
14
14
|
id: 'test-agent',
|
|
15
15
|
name: 'test-agent',
|
|
16
16
|
instructions: 'You are an agent with memory.',
|
|
17
|
-
model: 'openai/gpt-5.
|
|
17
|
+
model: 'openai/gpt-5.6-sol',
|
|
18
18
|
memory: new Memory({
|
|
19
19
|
options: {
|
|
20
20
|
workingMemory: {
|
|
@@ -47,7 +47,7 @@ export const agent = new Agent({
|
|
|
47
47
|
|
|
48
48
|
**options.observationalMemory** (`boolean | ObservationalMemoryOptions`): Enable Observational Memory for long-context agentic memory. Set to true for defaults, or pass a config object to customize token budgets, models, and scope. See Observational Memory reference for configuration details.
|
|
49
49
|
|
|
50
|
-
**options.generateTitle** (`boolean | { model: DynamicArgument<MastraLanguageModel>; instructions?: DynamicArgument<string> }`): Controls automatic thread title generation from the
|
|
50
|
+
**options.generateTitle** (`boolean | { model: DynamicArgument<MastraLanguageModel>; instructions?: DynamicArgument<string> }`): Controls automatic thread title generation from the conversation transcript. Can be a boolean or an object with custom model and instructions.
|
|
51
51
|
|
|
52
52
|
## Returns
|
|
53
53
|
|
|
@@ -63,7 +63,7 @@ import { LibSQLStore, LibSQLVector } from '@mastra/libsql'
|
|
|
63
63
|
export const agent = new Agent({
|
|
64
64
|
name: 'test-agent',
|
|
65
65
|
instructions: 'You are an agent with memory.',
|
|
66
|
-
model: 'openai/gpt-5.
|
|
66
|
+
model: 'openai/gpt-5.6-sol',
|
|
67
67
|
memory: new Memory({
|
|
68
68
|
storage: new LibSQLStore({
|
|
69
69
|
id: 'test-agent-storage',
|
|
@@ -100,7 +100,7 @@ import { PgStore, PgVector } from '@mastra/pg'
|
|
|
100
100
|
export const agent = new Agent({
|
|
101
101
|
name: 'pg-agent',
|
|
102
102
|
instructions: 'You are an agent with optimized PostgreSQL memory.',
|
|
103
|
-
model: 'openai/gpt-5.
|
|
103
|
+
model: 'openai/gpt-5.6-sol',
|
|
104
104
|
memory: new Memory({
|
|
105
105
|
storage: new PgStore({
|
|
106
106
|
id: 'pg-agent-storage',
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# DynamoDB storage
|
|
4
4
|
|
|
5
|
-
The DynamoDB storage implementation provides a
|
|
5
|
+
The DynamoDB storage implementation provides a high-capacity and performant NoSQL database solution for Mastra, using a single-table design pattern with [ElectroDB](https://electrodb.dev/).
|
|
6
6
|
|
|
7
7
|
> **Observability Not Supported:** DynamoDB storage **doesn't support the observability domain**. Traces from the `MastraStorageExporter` can't be persisted to DynamoDB, and [Studio's](https://mastra.ai/docs/studio/overview) observability features won't work with DynamoDB as your only storage provider. To enable observability, use [composite storage](https://mastra.ai/reference/storage/composite) to route observability data to a supported provider like ClickHouse.
|
|
8
8
|
|
|
@@ -124,7 +124,7 @@ For local development, you can use [DynamoDB Local](https://docs.aws.amazon.com/
|
|
|
124
124
|
|
|
125
125
|
## TTL (time to live) configuration
|
|
126
126
|
|
|
127
|
-
DynamoDB TTL allows you to automatically delete items after a specified
|
|
127
|
+
DynamoDB TTL allows you to automatically delete items after a specified duration for these use cases:
|
|
128
128
|
|
|
129
129
|
- **Cost optimization**: Automatically remove old data to reduce storage costs
|
|
130
130
|
- **Data lifecycle management**: Implement retention policies for compliance
|
|
@@ -259,19 +259,19 @@ Before diving into the architectural details, keep these key points in mind when
|
|
|
259
259
|
|
|
260
260
|
## Architectural approach
|
|
261
261
|
|
|
262
|
-
This storage adapter utilizes a **single-table design pattern**
|
|
262
|
+
This storage adapter utilizes a **single-table design pattern** with [ElectroDB](https://electrodb.dev/), a common and recommended approach for DynamoDB. This differs architecturally from relational database adapters (like `@mastra/pg` or `@mastra/libsql`) that typically use multiple tables, each dedicated to a specific entity (threads, messages, etc.).
|
|
263
263
|
|
|
264
264
|
Key aspects of this approach:
|
|
265
265
|
|
|
266
|
-
- **DynamoDB Native:** The single-table design is optimized for DynamoDB's key-value and query capabilities, often leading to better performance and
|
|
266
|
+
- **DynamoDB Native:** The single-table design is optimized for DynamoDB's key-value and query capabilities, often leading to better performance and capacity compared to mimicking relational models.
|
|
267
267
|
- **External Table Management:** Unlike some adapters that might offer helper functions to create tables via code, this adapter **expects the DynamoDB table and its associated Global Secondary Indexes (GSIs) to be provisioned externally** before use. Please refer to [TABLE\_SETUP.md](https://github.com/mastra-ai/mastra/blob/main/stores/dynamodb/TABLE_SETUP.md) for detailed instructions using tools like AWS CloudFormation or CDK. The adapter focuses solely on interacting with the pre-existing table structure.
|
|
268
268
|
- **Consistency via Interface:** While the underlying storage model differs, this adapter adheres to the same `MastraStorage` interface as other adapters, ensuring it can be used interchangeably within the Mastra `Memory` component.
|
|
269
269
|
|
|
270
270
|
### Mastra Data in the Single Table
|
|
271
271
|
|
|
272
|
-
Within the single DynamoDB table, different Mastra data entities (such as Threads, Messages, Traces, Evals, and Workflows) are managed and distinguished using ElectroDB. ElectroDB defines specific models for each entity type, which include unique key structures and attributes.
|
|
272
|
+
Within the single DynamoDB table, different Mastra data entities (such as Threads, Messages, Traces, Evals, and Workflows) are managed and distinguished using ElectroDB. ElectroDB defines specific models for each entity type, which include unique key structures and attributes. It allows the adapter to store and retrieve diverse data types efficiently within the same table.
|
|
273
273
|
|
|
274
|
-
For example, a `Thread` item might have a primary key like `THREAD#<threadId>`, while a `Message` item belonging to that thread might use `THREAD#<threadId>` as a partition key and `MESSAGE#<messageId>` as a sort key. The Global Secondary Indexes (GSIs), detailed in `TABLE_SETUP.md`, are strategically designed to support common access patterns across these different entities, such as fetching all messages for a thread or querying traces associated with a
|
|
274
|
+
For example, a `Thread` item might have a primary key like `THREAD#<threadId>`, while a `Message` item belonging to that thread might use `THREAD#<threadId>` as a partition key and `MESSAGE#<messageId>` as a sort key. The Global Secondary Indexes (GSIs), detailed in `TABLE_SETUP.md`, are strategically designed to support common access patterns across these different entities, such as fetching all messages for a thread or querying traces associated with a workflow.
|
|
275
275
|
|
|
276
276
|
### Advantages of Single-Table Design
|
|
277
277
|
|
|
@@ -279,6 +279,6 @@ This implementation uses a single-table design pattern with ElectroDB, which off
|
|
|
279
279
|
|
|
280
280
|
1. **Lower cost (potentially):** Fewer tables can simplify Read/Write Capacity Unit (RCU/WCU) provisioning and management, especially with on-demand capacity.
|
|
281
281
|
2. **Better performance:** Related data can be co-located or accessed efficiently through GSIs, enabling fast lookups for common access patterns.
|
|
282
|
-
3. **Simplified administration:** Fewer distinct tables to monitor
|
|
282
|
+
3. **Simplified administration:** Fewer distinct tables to monitor and back up, with less to manage.
|
|
283
283
|
4. **Reduced complexity in access patterns:** ElectroDB helps manage the complexity of item types and access patterns on a single table.
|
|
284
284
|
5. **Transaction support:** DynamoDB transactions can be used across different "entity" types stored within the same table if needed.
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
Storage grows without bound by default. Retention is an opt-in, age-based cleanup system: you declare per-table `maxAge` policies in the `retention` config, then call `storage.prune()` to delete rows older than their configured age. Anything you don't configure is kept forever, so there is no behavior change until you opt in.
|
|
6
6
|
|
|
7
|
-
`prune()` deletes rows. It caps growth and is safe to run against large tables (batched, bounded, resumable, cancellable). It never reclaims disk
|
|
7
|
+
`prune()` deletes rows. It caps growth and is safe to run against large tables (batched, bounded, resumable, cancellable). It never reclaims disk: on SQLite/libSQL the freed pages are reused by future writes so the file stops growing, but handing disk back to the OS (for example a `VACUUM`) is left to the underlying database and the operator to manage.
|
|
8
8
|
|
|
9
|
-
Retention covers **growth tables** only
|
|
9
|
+
Retention covers **growth tables** only: tables that accumulate rows unbounded as a side effect of normal operation (conversation history, telemetry, job and run records, schedule fire history, event feeds). User-authored artifacts and config (agents, skills, workspaces, prompt blocks, datasets, schedule definitions, channel installations, and so on) grow with user intent and are edited or deleted explicitly, so they're not valid retention keys.
|
|
10
10
|
|
|
11
11
|
The reference implementations are [libSQL](https://mastra.ai/reference/storage/libsql), [PostgreSQL](https://mastra.ai/reference/storage/postgresql), and [MongoDB](https://mastra.ai/reference/storage/mongodb). Other adapters keep rows forever until they implement retention.
|
|
12
12
|
|
|
@@ -31,7 +31,7 @@ const storage = new LibSQLStore({
|
|
|
31
31
|
},
|
|
32
32
|
})
|
|
33
33
|
|
|
34
|
-
// Wire this to your own cron/scheduler
|
|
34
|
+
// Wire this to your own cron/scheduler: Mastra never runs it for you.
|
|
35
35
|
const results = await storage.prune()
|
|
36
36
|
```
|
|
37
37
|
|
|
@@ -65,35 +65,35 @@ Set the `retention` field on the store config.
|
|
|
65
65
|
|
|
66
66
|
### Retention-eligible tables
|
|
67
67
|
|
|
68
|
-
Each domain declares which of its tables can be age-pruned and which timestamp column anchors the comparison. The anchor is chosen so `maxAge` means what you'd expect for that data
|
|
69
|
-
|
|
70
|
-
| Domain | Table key | Anchor column | `maxAge` measures
|
|
71
|
-
| ----------------- | ------------------ | ---------------- |
|
|
72
|
-
| `memory` | `threads` | `createdAt` | Thread age
|
|
73
|
-
| `memory` | `messages` | `createdAt` | Message age
|
|
74
|
-
| `memory` | `resources` | `createdAt` | Resource age
|
|
75
|
-
| `threadState` | `threadState` | `updatedAt` | Inactivity
|
|
76
|
-
| `observability` | `spans` | `startedAt` | Span age
|
|
77
|
-
| `observability` | `metrics` | `timestamp` | Metric event age (v-next only)
|
|
78
|
-
| `observability` | `logs` | `timestamp` | Log event age (v-next only)
|
|
79
|
-
| `observability` | `scores` | `timestamp` | Score event age (v-next only)
|
|
80
|
-
| `observability` | `feedback` | `timestamp` | Feedback event age (v-next only)
|
|
81
|
-
| `scores` | `scorers` | `createdAt` | Score record age
|
|
82
|
-
| `workflows` | `workflowSnapshot` | `updatedAt` | Inactivity
|
|
83
|
-
| `backgroundTasks` | `backgroundTasks` | `completedAt` | Time since completion
|
|
84
|
-
| `experiments` | `experiments` | `completedAt` | Time since completion
|
|
85
|
-
| `notifications` | `notifications` | `createdAt` | Notification age
|
|
86
|
-
| `harness` | `sessions` | `createdAt` | Session record age
|
|
87
|
-
| `schedules` | `triggers` | `actual_fire_at` | Fire-history age (epoch-ms column)
|
|
68
|
+
Each domain declares which of its tables can be age-pruned and which timestamp column anchors the comparison. The anchor is chosen so `maxAge` means what you'd expect for that data. Append-only logs use creation time, and live state uses last activity. Jobs and runs use completion time, so in-flight work is never pruned.
|
|
69
|
+
|
|
70
|
+
| Domain | Table key | Anchor column | `maxAge` measures |
|
|
71
|
+
| ----------------- | ------------------ | ---------------- | ---------------------------------------------------------------- |
|
|
72
|
+
| `memory` | `threads` | `createdAt` | Thread age |
|
|
73
|
+
| `memory` | `messages` | `createdAt` | Message age |
|
|
74
|
+
| `memory` | `resources` | `createdAt` | Resource age |
|
|
75
|
+
| `threadState` | `threadState` | `updatedAt` | Inactivity: state for still-active threads survives |
|
|
76
|
+
| `observability` | `spans` | `startedAt` | Span age |
|
|
77
|
+
| `observability` | `metrics` | `timestamp` | Metric event age (v-next only) |
|
|
78
|
+
| `observability` | `logs` | `timestamp` | Log event age (v-next only) |
|
|
79
|
+
| `observability` | `scores` | `timestamp` | Score event age (v-next only) |
|
|
80
|
+
| `observability` | `feedback` | `timestamp` | Feedback event age (v-next only) |
|
|
81
|
+
| `scores` | `scorers` | `createdAt` | Score record age |
|
|
82
|
+
| `workflows` | `workflowSnapshot` | `updatedAt` | Inactivity, suspended or long-running workflows survive |
|
|
83
|
+
| `backgroundTasks` | `backgroundTasks` | `completedAt` | Time since completion, in-flight tasks (`NULL`) are never pruned |
|
|
84
|
+
| `experiments` | `experiments` | `completedAt` | Time since completion, running experiments are never pruned |
|
|
85
|
+
| `notifications` | `notifications` | `createdAt` | Notification age |
|
|
86
|
+
| `harness` | `sessions` | `createdAt` | Session record age |
|
|
87
|
+
| `schedules` | `triggers` | `actual_fire_at` | Fire-history age (epoch-ms column) |
|
|
88
88
|
|
|
89
89
|
> **Note:**
|
|
90
90
|
>
|
|
91
91
|
> - The memory `observational_memory` table has no timestamp anchor, so it can't be age-pruned and isn't a valid retention key.
|
|
92
92
|
> - Experiments prune as whole units: an aged experiment's result rows are deleted together with it (results cascade with their parent), so a run is never left partially deleted. Retention doesn't have a separate `results` key.
|
|
93
|
-
> - For `schedules`, the growth table is the fire history (`schedule_triggers`, one row per fire)
|
|
93
|
+
> - For `schedules`, the growth table is the fire history (`schedule_triggers`, one row per fire): schedule definitions are config and aren't pruned.
|
|
94
94
|
> - On PostgreSQL, timestamp anchors use the timezone-aware mirror columns (for example `createdAtZ`, `completedAtZ`).
|
|
95
95
|
> - LibSQL supports all domains above; PostgreSQL and MongoDB support all except `threadState` and `harness`, which they don't implement.
|
|
96
|
-
> - The v-next PostgreSQL observability domain stores signal events in day-partitioned tables (`spans`, `metrics`, `logs`, `scores`, `feedback`). For it, `prune()` drops whole day partitions (or TimescaleDB chunks) that are entirely older than the cutoff instead of deleting rows
|
|
96
|
+
> - The v-next PostgreSQL observability domain stores signal events in day-partitioned tables (`spans`, `metrics`, `logs`, `scores`, `feedback`). For it, `prune()` drops whole day partitions (or TimescaleDB chunks) that are entirely older than the cutoff instead of deleting rows: effective level of detail is one day, and a partition is only dropped once its entire day is past `maxAge`. `PruneResult.deleted` reports the number of rows in the dropped partitions.
|
|
97
97
|
|
|
98
98
|
## Methods
|
|
99
99
|
|
|
@@ -103,11 +103,11 @@ Each domain declares which of its tables can be age-pruned and which timestamp c
|
|
|
103
103
|
|
|
104
104
|
Deletes rows older than their configured `maxAge` across every domain that has a policy in `retention`. Returns one `PruneResult` per table touched. With no `retention` configured it's a no-op returning `[]`.
|
|
105
105
|
|
|
106
|
-
`prune()` is designed to be safe on tables with millions of rows. It deletes in bounded, batched chunks
|
|
106
|
+
`prune()` is designed to be safe on tables with millions of rows. It deletes in bounded, batched chunks (each batch is its own transaction) so it never takes a long lock or bloats the transaction log. It never runs a `VACUUM`.
|
|
107
107
|
|
|
108
|
-
Pass `options.retention` to replace the configured policies for that call only
|
|
108
|
+
Pass `options.retention` to replace the configured policies for that call only: for example to skip a domain (keep chat history) or prune more aggressively than the standing config. The store's configured `retention` is unchanged.
|
|
109
109
|
|
|
110
|
-
Anchor-column indexes are created lazily on the first `prune()` call for each table with a policy
|
|
110
|
+
Anchor-column indexes are created lazily on the first `prune()` call for each table with a policy (never at `init()`) so deployments that don't configure retention pay no extra index write or disk overhead. The first prune of an existing large table pays a one-time index build. Subsequent prunes reuse the index.
|
|
111
111
|
|
|
112
112
|
```typescript
|
|
113
113
|
const results = await storage.prune({
|
|
@@ -156,7 +156,7 @@ interface PruneResult {
|
|
|
156
156
|
|
|
157
157
|
## Running prune on a schedule
|
|
158
158
|
|
|
159
|
-
`prune()` has no built-in scheduler
|
|
159
|
+
`prune()` has no built-in scheduler: you decide when it runs. Because it's bounded, a single call may not delete everything. When any result has `done: false`, eligible rows remain and you call again on the next tick. This keeps each invocation short and lets a large backlog drain over several runs.
|
|
160
160
|
|
|
161
161
|
```typescript
|
|
162
162
|
// Runs on your own cron (node-cron, a workflow schedule, an external job, etc.).
|
|
@@ -173,7 +173,7 @@ async function retentionTick() {
|
|
|
173
173
|
}
|
|
174
174
|
```
|
|
175
175
|
|
|
176
|
-
You can also cancel a long-running prune with an `AbortSignal
|
|
176
|
+
You can also cancel a long-running prune with an `AbortSignal`: the loop stops between batches and returns partial results with `done: false`, so the next run resumes cleanly.
|
|
177
177
|
|
|
178
178
|
## MongoDB TTL indexes (alternative to prune)
|
|
179
179
|
|
|
@@ -192,7 +192,7 @@ MongoDB offers native [TTL (Time-To-Live) indexes](https://www.mongodb.com/docs/
|
|
|
192
192
|
> - You need resumable, cancellable cleanup operations
|
|
193
193
|
> - You're using composite storage with multiple databases
|
|
194
194
|
>
|
|
195
|
-
> Both approaches are valid. TTL is simpler
|
|
195
|
+
> Both approaches are valid. TTL is simpler. `prune()` gives more control.
|
|
196
196
|
|
|
197
197
|
### Setting up TTL indexes on MongoDB
|
|
198
198
|
|
|
@@ -232,9 +232,9 @@ const storage = new MongoDBStore({
|
|
|
232
232
|
|
|
233
233
|
## Reclaiming disk
|
|
234
234
|
|
|
235
|
-
`prune()` deletes rows but doesn't shrink the database file. On SQLite/libSQL the freed pages go on a freelist and are reused by future writes, so the file stops growing
|
|
235
|
+
`prune()` deletes rows but doesn't shrink the database file. On SQLite/libSQL the freed pages go on a freelist and are reused by future writes, so the file stops growing: for most users this alone solves the unbounded-growth problem.
|
|
236
236
|
|
|
237
|
-
Handing that free space back to the OS is a separate concern that Mastra doesn't manage. If you specifically need to shrink the file, run the underlying database's compaction (for example `VACUUM` on self-hosted libSQL) yourself
|
|
237
|
+
Handing that free space back to the OS is a separate concern that Mastra doesn't manage. If you specifically need to shrink the file, run the underlying database's compaction (for example `VACUUM` on self-hosted libSQL) yourself in a maintenance window. A full `VACUUM` locks the file and needs roughly twice the file size in free disk. On PostgreSQL, autovacuum reclaims dead tuples for reuse automatically; a manual `VACUUM FULL` is only needed if you must return disk to the OS.
|
|
238
238
|
|
|
239
239
|
For MongoDB, deleted documents are reused by future insertions. To reclaim disk space, run [`db.runCommand({ compact: "collection_name" })`](https://www.mongodb.com/docs/manual/reference/command/compact/) during a maintenance window.
|
|
240
240
|
|
|
@@ -85,7 +85,7 @@ const results = await store.query({
|
|
|
85
85
|
|
|
86
86
|
### `createIndex()`
|
|
87
87
|
|
|
88
|
-
Creates a new vector collection. The index name must start with a letter or underscore and can only contain letters, numbers, and
|
|
88
|
+
Creates a new vector collection. The index name must start with a letter or underscore and can only contain letters, numbers, and underscore characters. The dimension must be a positive integer.
|
|
89
89
|
|
|
90
90
|
**indexName** (`string`): Name of the index to create
|
|
91
91
|
|
|
@@ -279,7 +279,7 @@ export const libsqlAgent = new Agent({
|
|
|
279
279
|
name: 'libSQL Agent',
|
|
280
280
|
instructions:
|
|
281
281
|
'You are an AI agent with the ability to automatically recall memories from previous interactions.',
|
|
282
|
-
model: 'openai/gpt-5.
|
|
282
|
+
model: 'openai/gpt-5.6-sol',
|
|
283
283
|
memory: new Memory({
|
|
284
284
|
storage: new LibSQLStore({
|
|
285
285
|
id: 'libsql-agent-storage',
|