@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.
Files changed (37) hide show
  1. package/CHANGELOG.md +249 -9
  2. package/dist/docs/SKILL.md +4 -3
  3. package/dist/docs/assets/SOURCE_MAP.json +1 -1
  4. package/dist/docs/references/docs-agent-builder-deploying.md +2 -2
  5. package/dist/docs/references/docs-agent-builder-overview.md +3 -1
  6. package/dist/docs/references/docs-agents-agent-approval.md +85 -11
  7. package/dist/docs/references/docs-agents-networks.md +2 -2
  8. package/dist/docs/references/docs-deployment-workers.md +137 -0
  9. package/dist/docs/references/docs-memory-memory-processors.md +10 -10
  10. package/dist/docs/references/docs-memory-message-history.md +29 -5
  11. package/dist/docs/references/docs-memory-multi-user-threads.md +6 -6
  12. package/dist/docs/references/docs-memory-overview.md +10 -10
  13. package/dist/docs/references/docs-memory-semantic-recall.md +3 -3
  14. package/dist/docs/references/docs-memory-working-memory.md +8 -8
  15. package/dist/docs/references/docs-rag-retrieval.md +18 -18
  16. package/dist/docs/references/docs-storage-overview.md +2 -2
  17. package/dist/docs/references/docs-workflows-snapshots.md +3 -3
  18. package/dist/docs/references/reference-core-mastra-class.md +48 -1
  19. package/dist/docs/references/reference-file-based-agents-storage.md +1 -1
  20. package/dist/docs/references/reference-memory-memory-class.md +5 -5
  21. package/dist/docs/references/reference-storage-dynamodb.md +7 -7
  22. package/dist/docs/references/reference-storage-retention.md +33 -33
  23. package/dist/docs/references/reference-vectors-libsql.md +2 -2
  24. package/dist/index.cjs +12742 -13175
  25. package/dist/index.cjs.map +1 -1
  26. package/dist/index.js +12708 -13141
  27. package/dist/index.js.map +1 -1
  28. package/dist/storage/domains/datasets/index.d.ts.map +1 -1
  29. package/dist/storage/domains/experiments/index.d.ts.map +1 -1
  30. package/dist/storage/domains/memory/index.d.ts.map +1 -1
  31. package/dist/storage/factory-storage.d.ts +28 -0
  32. package/dist/storage/factory-storage.d.ts.map +1 -0
  33. package/dist/storage/index.d.ts +2 -9
  34. package/dist/storage/index.d.ts.map +1 -1
  35. package/dist/vector/index.d.ts +1 -7
  36. package/dist/vector/index.d.ts.map +1 -1
  37. 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 dynamically decide what information to retrieve
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 leverage unique features and optimizations of different vector stores.
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—not for database connection setup.
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 dynamically based on context
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.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.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.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.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.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.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.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.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.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.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.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 nuanced relevance. Re-ranking is a more computationally expensive process, but more accurate algorithm that improves results by:
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 sophisticated relevance scoring
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.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, be shared across processes, or be 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.
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.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
- This 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.
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. This ensures full context is available when resuming execution.
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, especially long-running ones, to ensure they're properly resumed.
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; use code registration when setup depends on runtime wiring in `src/mastra/index.ts`.
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 robust 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.
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.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 user's first message. Can be a boolean or an object with custom model and instructions.
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.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.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 scalable and performant NoSQL database solution for Mastra, leveraging a single-table design pattern with [ElectroDB](https://electrodb.dev/).
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 time period. This is useful for:
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** leveraging [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.).
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 scalability compared to mimicking relational models.
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. This allows the adapter to store and retrieve diverse data types efficiently within the same table.
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 particular workflow.
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, back up, and manage.
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 — 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.
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 — 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.
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 — Mastra never runs it for you.
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: creation time for append-only logs, last activity for live state, and completion time for jobs and runs (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) |
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) — schedule definitions are config and aren't pruned.
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 — effective granularity 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.
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 — each batch is its own transaction — so it never takes a long lock or bloats the transaction log. It never runs a `VACUUM`.
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 — for example to skip a domain (keep chat history) or prune more aggressively than the standing config. The store's configured `retention` is unchanged.
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 — 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.
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 — 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.
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` — the loop stops between batches and returns partial results with `done: false`, so the next run resumes cleanly.
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; `prune()` gives more control.
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 — for most users this alone solves the unbounded-growth problem.
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, 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.
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 underscores. The dimension must be a positive integer.
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.5',
282
+ model: 'openai/gpt-5.6-sol',
283
283
  memory: new Memory({
284
284
  storage: new LibSQLStore({
285
285
  id: 'libsql-agent-storage',