@mastra/libsql 1.14.3 → 1.15.0-alpha.1

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 (61) hide show
  1. package/CHANGELOG.md +165 -0
  2. package/dist/docs/SKILL.md +2 -1
  3. package/dist/docs/assets/SOURCE_MAP.json +1 -1
  4. package/dist/docs/references/docs-agent-builder-deploying.md +2 -0
  5. package/dist/docs/references/docs-agent-builder-overview.md +2 -0
  6. package/dist/docs/references/docs-agents-agent-approval.md +2 -0
  7. package/dist/docs/references/docs-agents-networks.md +2 -0
  8. package/dist/docs/references/docs-memory-memory-processors.md +4 -0
  9. package/dist/docs/references/docs-memory-message-history.md +2 -0
  10. package/dist/docs/references/docs-memory-multi-user-threads.md +2 -0
  11. package/dist/docs/references/docs-memory-overview.md +5 -1
  12. package/dist/docs/references/docs-memory-semantic-recall.md +8 -0
  13. package/dist/docs/references/docs-memory-storage.md +6 -0
  14. package/dist/docs/references/docs-memory-working-memory.md +2 -0
  15. package/dist/docs/references/docs-rag-retrieval.md +2 -0
  16. package/dist/docs/references/docs-workflows-snapshots.md +2 -0
  17. package/dist/docs/references/guides-agent-frameworks-ai-sdk.md +2 -0
  18. package/dist/docs/references/reference-core-getMemory.md +2 -0
  19. package/dist/docs/references/reference-core-listMemory.md +2 -0
  20. package/dist/docs/references/reference-core-mastra-class.md +2 -0
  21. package/dist/docs/references/reference-memory-memory-class.md +3 -0
  22. package/dist/docs/references/reference-storage-composite.md +2 -0
  23. package/dist/docs/references/reference-storage-dynamodb.md +2 -0
  24. package/dist/docs/references/reference-storage-libsql.md +2 -0
  25. package/dist/docs/references/reference-storage-retention.md +180 -0
  26. package/dist/docs/references/reference-vectors-libsql.md +2 -0
  27. package/dist/index.cjs +578 -61
  28. package/dist/index.cjs.map +1 -1
  29. package/dist/index.js +579 -62
  30. package/dist/index.js.map +1 -1
  31. package/dist/storage/db/index.d.ts +51 -0
  32. package/dist/storage/db/index.d.ts.map +1 -1
  33. package/dist/storage/domains/background-tasks/index.d.ts +9 -0
  34. package/dist/storage/domains/background-tasks/index.d.ts.map +1 -1
  35. package/dist/storage/domains/datasets/index.d.ts +7 -7
  36. package/dist/storage/domains/datasets/index.d.ts.map +1 -1
  37. package/dist/storage/domains/experiments/index.d.ts +24 -1
  38. package/dist/storage/domains/experiments/index.d.ts.map +1 -1
  39. package/dist/storage/domains/harness/index.d.ts +5 -1
  40. package/dist/storage/domains/harness/index.d.ts.map +1 -1
  41. package/dist/storage/domains/memory/index.d.ts +15 -1
  42. package/dist/storage/domains/memory/index.d.ts.map +1 -1
  43. package/dist/storage/domains/notifications/index.d.ts +5 -1
  44. package/dist/storage/domains/notifications/index.d.ts.map +1 -1
  45. package/dist/storage/domains/observability/index.d.ts +8 -1
  46. package/dist/storage/domains/observability/index.d.ts.map +1 -1
  47. package/dist/storage/domains/schedules/index.d.ts +9 -1
  48. package/dist/storage/domains/schedules/index.d.ts.map +1 -1
  49. package/dist/storage/domains/scores/index.d.ts +15 -5
  50. package/dist/storage/domains/scores/index.d.ts.map +1 -1
  51. package/dist/storage/domains/thread-state/index.d.ts +9 -0
  52. package/dist/storage/domains/thread-state/index.d.ts.map +1 -1
  53. package/dist/storage/domains/utils.d.ts +34 -0
  54. package/dist/storage/domains/utils.d.ts.map +1 -0
  55. package/dist/storage/domains/workflows/index.d.ts +8 -1
  56. package/dist/storage/domains/workflows/index.d.ts.map +1 -1
  57. package/dist/storage/index.d.ts +8 -1
  58. package/dist/storage/index.d.ts.map +1 -1
  59. package/dist/storage/retention.d.ts +77 -0
  60. package/dist/storage/retention.d.ts.map +1 -0
  61. package/package.json +4 -4
package/CHANGELOG.md CHANGED
@@ -1,5 +1,170 @@
1
1
  # @mastra/libsql
2
2
 
3
+ ## 1.15.0-alpha.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Added optional tenancy arguments to `getDataset`, `updateDataset`, and `deleteDataset`. ([#18750](https://github.com/mastra-ai/mastra/pull/18750))
8
+
9
+ You can now pass `organizationId` and `projectId` to scope dataset reads, updates, and deletes to a specific tenant. Reads and updates against a dataset in a different tenant throw `DATASET_NOT_FOUND` (surfaced as a 404 over HTTP). Deletes silently no-op on a tenancy mismatch — matching the existing "delete non-existent id is a no-op" semantics so cross-tenant existence is never leaked via error timing or status.
10
+
11
+ **Example**
12
+
13
+ ```ts
14
+ // Before
15
+ await client.getDataset('abc123');
16
+ await client.deleteDataset('abc123');
17
+ await client.updateDataset({ id: 'abc123', name: 'renamed' });
18
+
19
+ // After — scope to a tenant
20
+ await client.getDataset('abc123', { organizationId: 'org_a', projectId: 'proj_1' });
21
+ await client.deleteDataset('abc123', { organizationId: 'org_a' });
22
+ await client.updateDataset({ id: 'abc123', name: 'renamed', organizationId: 'org_a' });
23
+ ```
24
+
25
+ - Pushed remaining dataset read filters and pagination down to storage. ([#18710](https://github.com/mastra-ai/mastra/pull/18710))
26
+
27
+ `DatasetsManager.list({ filters })` now accepts `targetType`, `targetIds` (overlap/union semantics), and `name` (substring, case-insensitive) in addition to the existing tenancy and candidate filters. Filtering is pushed down to the storage layer so callers no longer have to post-filter results.
28
+
29
+ Storage adapters must also be upgraded to the versions listed below to honor the new filters. If a caller is on this version of `@mastra/core` but on an older storage adapter, the new `targetType`/`targetIds`/`name` filter keys are silently ignored by the adapter — no runtime error, but the filter has no effect and every dataset in the tenancy is returned.
30
+
31
+ `Dataset.listItems({ version, search, page, perPage })` now applies `search` and pagination at the storage layer when `version` is provided alongside any of those. Previously they were silently dropped whenever `version` was set. The return shape is unchanged: passing only `version` still returns a bare `DatasetItem[]` snapshot; passing `search`, `page`, or `perPage` (with or without `version`) returns the paginated `{ items, pagination }` shape. The bare-array branch is marked `@deprecated`; prefer passing `page` / `perPage` to always receive the paginated shape.
32
+
33
+ - Fixed a double-encoding bug where `createDataset` stored `targetIds` and `scorerIds` as JSON-encoded strings instead of arrays. This caused the new `listDatasets({ filters: { targetIds } })` overlap query to never match. ([#18710](https://github.com/mastra-ai/mastra/pull/18710))
34
+
35
+ Existing rows written before this fix are still double-encoded and will not be matched by the new `targetIds` filter. They self-heal on the next `updateDataset` call. Deployments with a long tail of pre-existing datasets should run a one-time backfill (re-encoding `targetIds` and `scorerIds` on affected rows) rather than rely on incidental writes; this can be tracked as a follow-up if needed.
36
+
37
+ - Tenancy-scope experiments `getById` and `delete*` on `ExperimentsStorage`. ([#18770](https://github.com/mastra-ai/mastra/pull/18770))
38
+
39
+ `ExperimentsStorage.getExperimentById`, `getExperimentResultById`, `deleteExperiment`, and `deleteExperimentResults` used to key on the primary id alone, so any caller who knew the id could read or delete the row regardless of tenant. All four now accept an optional `filters: { organizationId?, projectId? }` argument that is enforced on every adapter (inmemory, libsql, pg, mysql, mongodb, spanner):
40
+ - On tenancy mismatch, `get*` returns `null` at the storage layer.
41
+ - On tenancy mismatch, `delete*` is a silent no-op.
42
+ - The tenancy predicate is folded into the destructive DML itself (scoped `WHERE` on the DELETE, an atomic gate + delete inside a transaction, or a scoped subquery for the results cascade). A concurrent tenant swap of the same id between a pre-check and the DELETE cannot let a scoped delete hit another tenant's row.
43
+
44
+ Both behaviors match how a missing id already responds, so existence does not leak through error timing or messages.
45
+
46
+ The same atomic-DML pattern is also applied to `DatasetsStorage.deleteDataset` across all 5 store adapters, closing a TOCTOU window between the pre-check and the parent DELETE that was introduced when tenancy filters were originally added.
47
+
48
+ `Dataset.getExperiment` and the shared experiment-ownership gate on `Dataset` now forward the dataset's tenancy scope to storage, so experiment reads and downstream mutations (list results, update result, delete experiment) reached through a dataset handle are automatically scoped to the owning tenant.
49
+
50
+ Legacy calls that omit `filters` are unchanged, so this is fully backwards-compatible.
51
+
52
+ ```ts
53
+ // Before: any caller who knew the id could read/delete across tenants.
54
+ await store.experiments.getExperimentById({ id: experimentId });
55
+ await store.experiments.deleteExperiment({ id: experimentId });
56
+
57
+ // After: pass the caller's scope; wrong tenant gets null / silent no-op.
58
+ await store.experiments.getExperimentById({
59
+ id: experimentId,
60
+ filters: { organizationId, projectId },
61
+ });
62
+ await store.experiments.deleteExperiment({
63
+ id: experimentId,
64
+ filters: { organizationId, projectId },
65
+ });
66
+ ```
67
+
68
+ - Fixed a cross-tenant data-access issue on datasets by scoping `DatasetsManager.get` and `DatasetsManager.delete` to tenancy filters. ([#18750](https://github.com/mastra-ai/mastra/pull/18750))
69
+
70
+ Previously `get({ id })` and `delete({ id })` looked up a dataset by its primary key alone. Any caller who knew a dataset id could read or delete it regardless of which `organizationId` / `projectId` it belonged to. This is now closed at the storage layer via a scoped SQL predicate (option (a) — no fetch-then-assert).
71
+
72
+ **What changed**
73
+ - `DatasetsManager.get` and `DatasetsManager.delete` accept optional `organizationId` and `projectId`.
74
+ - The tenancy is stashed on the returned `Dataset` handle and forwarded to every downstream storage call (`getDetails`, `update`, `addItem`, item batch ops, `startExperimentAsync`).
75
+ - The abstract storage contract (`getDatasetById`, `deleteDataset`) gained an optional `filters?: DatasetTenancyFilters` arg.
76
+ - Item-mutation inputs (`AddDatasetItemInput`, `UpdateDatasetItemInput`, `BatchInsertItemsInput`, `BatchDeleteItemsInput`) and `UpdateDatasetInput` accept optional `filters` for the internal existence check.
77
+
78
+ **Behavior**
79
+ - Omitting tenancy preserves the existing behavior (no predicate added) — fully backwards compatible.
80
+ - On tenancy mismatch, `get` throws NOT_FOUND (returns null at the storage layer) and `delete` is a silent no-op — matching how a missing id already behaves, so existence does not leak through error timing or messages.
81
+
82
+ **Example**
83
+
84
+ ```ts
85
+ // Before
86
+ const ds = await mastra.datasets.get({ id });
87
+ await mastra.datasets.delete({ id });
88
+
89
+ // After — scope to a tenant
90
+ const ds = await mastra.datasets.get({ id, organizationId, projectId });
91
+ await mastra.datasets.delete({ id, organizationId, projectId });
92
+ ```
93
+
94
+ - Add optional `batchId`, `datasetId`, and `datasetItemId` fields to persisted scores so saved baseline scores can be grouped as one scoring pass and joined back to the dataset items they came from. ([#18331](https://github.com/mastra-ai/mastra/pull/18331))
95
+ - `scoreTrace()` accepts top-level `batchId`, `datasetId`, and `datasetItemId` when persisting a score for a stored trace.
96
+ - `ScoreRowData` and score save payloads now include nullable `batchId`, `datasetId`, and `datasetItemId`.
97
+ - Built-in stores with explicit score schema or attribute mappings now persist these provenance fields on saved scores.
98
+ - D1, DSQL, MSSQL, and Upstash score stores now apply additive provenance migrations or deterministic score ordering for persisted score reads.
99
+
100
+ ```ts
101
+ await scoreTrace({
102
+ storage,
103
+ scorer,
104
+ target: { traceId },
105
+ batchId: 'baseline-batch-1',
106
+ datasetId,
107
+ datasetItemId,
108
+ });
109
+ ```
110
+
111
+ - Added optional `organizationId` and `projectId` fields to scores for multi-tenant isolation. Scores can now be saved with tenancy metadata and the `listScoresBy*` methods accept a `filters` option to scope results by organization and project. ([#18331](https://github.com/mastra-ai/mastra/pull/18331))
112
+
113
+ ```ts
114
+ await storage.saveScore({ ...score, organizationId: 'org-a', projectId: 'proj-1' });
115
+
116
+ const result = await storage.listScoresByScorerId({
117
+ scorerId,
118
+ filters: { organizationId: 'org-a', projectId: 'proj-1' },
119
+ });
120
+ ```
121
+
122
+ `projectId` identifies the project scope, separate from `resourceId` which continues to mean the agent memory resource.
123
+
124
+ - Scoped `getDatasetById` and `deleteDataset` to tenancy filters when the caller passes `organizationId` / `projectId`. ([#18750](https://github.com/mastra-ai/mastra/pull/18750))
125
+
126
+ The adapters now push the tenancy predicate into the SQL/query when the new optional `filters` argument is present. Legacy calls that omit tenancy are unchanged. On mismatch, `getDatasetById` returns `null` and `deleteDataset` is a silent no-op — the cascade delete (dataset items and versions) is gated by a scoped parent pre-check, so cross-tenant data is never touched.
127
+
128
+ - Added optional `organizationId` and `projectId` query parameters to the dataset routes. ([#18750](https://github.com/mastra-ai/mastra/pull/18750))
129
+
130
+ `GET /datasets/:datasetId`, `PATCH /datasets/:datasetId`, and `DELETE /datasets/:datasetId` now accept optional tenancy query parameters. When provided, they are forwarded to `mastra.datasets.get` / `.delete` and the operation returns 404 if the dataset does not belong to the requested tenant. Requests that omit the query parameters keep their existing behavior.
131
+
132
+ **Example**
133
+
134
+ ```
135
+ GET /datasets/abc123?organizationId=org_a&projectId=proj_1
136
+ DELETE /datasets/abc123?organizationId=org_a
137
+ ```
138
+
139
+ - Updated dependencies [[`9250acd`](https://github.com/mastra-ai/mastra/commit/9250acd1357f0f1f33d0dcca16f9655084c58eca), [`215f9b0`](https://github.com/mastra-ai/mastra/commit/215f9b0f3f3f6fc165edad360582dd4d3d7ea748), [`c64c2a8`](https://github.com/mastra-ai/mastra/commit/c64c2a8503a50252f9ca6b8e8c54cadee31b92a2), [`06e2680`](https://github.com/mastra-ai/mastra/commit/06e26806b51d2cbd858afdc66daa2b86ff3ba64a), [`1240f05`](https://github.com/mastra-ai/mastra/commit/1240f051c8e5371f1c014448bf37b1a1b9a05e47), [`215f9b0`](https://github.com/mastra-ai/mastra/commit/215f9b0f3f3f6fc165edad360582dd4d3d7ea748), [`24c10d3`](https://github.com/mastra-ai/mastra/commit/24c10d333e6649ac06075903aeeee13a933db3b3), [`24c10d3`](https://github.com/mastra-ai/mastra/commit/24c10d333e6649ac06075903aeeee13a933db3b3), [`24c10d3`](https://github.com/mastra-ai/mastra/commit/24c10d333e6649ac06075903aeeee13a933db3b3), [`24c10d3`](https://github.com/mastra-ai/mastra/commit/24c10d333e6649ac06075903aeeee13a933db3b3), [`215f9b0`](https://github.com/mastra-ai/mastra/commit/215f9b0f3f3f6fc165edad360582dd4d3d7ea748), [`215f9b0`](https://github.com/mastra-ai/mastra/commit/215f9b0f3f3f6fc165edad360582dd4d3d7ea748)]:
140
+ - @mastra/core@1.49.0-alpha.5
141
+
142
+ ## 1.15.0-alpha.0
143
+
144
+ ### Minor Changes
145
+
146
+ - Added storage retention support to libSQL. When you set a `retention` config, `LibSQLStore` can prune old rows from every growth-table domain: `memory` (threads, messages, resources by `createdAt`), `threadState` (by `updatedAt`), `observability` (spans by `startedAt`), `scores` (by `createdAt`), `workflows` (run snapshots by `updatedAt`), `backgroundTasks` (by `completedAt`, so in-flight tasks are never pruned), `experiments` (whole runs by `completedAt`, results cascade with their parent), `notifications` and `harness` sessions (by `createdAt`), and `schedules` fire history (by `actual_fire_at`). ([#18733](https://github.com/mastra-ai/mastra/pull/18733))
147
+
148
+ Deletes run in batches so they stay safe on large tables. Anchor-column indexes are created lazily on the first `prune()` call — never at init — so deployments that don't configure retention pay no extra index overhead. `prune()` only deletes rows; reclaiming disk (for example a `VACUUM` on self-hosted libSQL) is left to you to run in a maintenance window.
149
+
150
+ ```typescript
151
+ const storage = new LibSQLStore({
152
+ id: 'mastra-storage',
153
+ url: 'file:./mastra.db',
154
+ retention: {
155
+ memory: { messages: { maxAge: '30d' } },
156
+ observability: { spans: { maxAge: '7d' } },
157
+ },
158
+ });
159
+
160
+ await storage.prune();
161
+ ```
162
+
163
+ ### Patch Changes
164
+
165
+ - Updated dependencies [[`700619b`](https://github.com/mastra-ai/mastra/commit/700619b61d572e592cbaaf758121d168844ca4d2), [`0c3d4bc`](https://github.com/mastra-ai/mastra/commit/0c3d4bcae13ea3699d379403e6f350d5cf4efe9f), [`17369b2`](https://github.com/mastra-ai/mastra/commit/17369b25250561e9ed994ae509be1d15bfb33bcb), [`bcae929`](https://github.com/mastra-ai/mastra/commit/bcae929945cbf265bd9f327cc715ecafa072b5b9), [`b33822e`](https://github.com/mastra-ai/mastra/commit/b33822e8d470884954b02f7b0745407ee4ef74b1), [`d5c11e3`](https://github.com/mastra-ai/mastra/commit/d5c11e3ba5045969caa7272a7bd1fd141c93ab6c), [`ff80671`](https://github.com/mastra-ai/mastra/commit/ff8067185e208b27198b4e5b71803013175c3643), [`dab1257`](https://github.com/mastra-ai/mastra/commit/dab1257b64e4ed576dc5038bb7a3f7072338bc9f), [`705ff39`](https://github.com/mastra-ai/mastra/commit/705ff3969e57214ff2fdaf3815d751dd558886ed), [`e6fbd5b`](https://github.com/mastra-ai/mastra/commit/e6fbd5bfdc28e92c0c0433f29aa1bc152d3430f6), [`6f2026c`](https://github.com/mastra-ai/mastra/commit/6f2026cdf114ff1e21e49133ca774ec7d5085059), [`f890eda`](https://github.com/mastra-ai/mastra/commit/f890eda2c8a2ae83d9b30bc6d85842f93b6c266b)]:
166
+ - @mastra/core@1.49.0-alpha.3
167
+
3
168
  ## 1.14.3
4
169
 
5
170
  ### Patch Changes
@@ -3,7 +3,7 @@ name: mastra-libsql
3
3
  description: Documentation for @mastra/libsql. Use when working with @mastra/libsql APIs, configuration, or implementation.
4
4
  metadata:
5
5
  package: "@mastra/libsql"
6
- version: "1.14.3"
6
+ version: "1.15.0-alpha.1"
7
7
  ---
8
8
 
9
9
  ## When to use
@@ -43,6 +43,7 @@ Read the individual reference documents for detailed explanations and code examp
43
43
  - [Reference: Composite storage](references/reference-storage-composite.md) - Documentation for combining multiple storage backends in Mastra.
44
44
  - [Reference: DynamoDB storage](references/reference-storage-dynamodb.md) - Documentation for the DynamoDB storage implementation in Mastra, using a single-table design with ElectroDB.
45
45
  - [Reference: libSQL storage](references/reference-storage-libsql.md) - Documentation for the libSQL storage implementation in Mastra.
46
+ - [Reference: Storage retention (prune)](references/reference-storage-retention.md) - API reference for retention policies and prune() on Mastra storage.
46
47
  - [Reference: libSQL vector store](references/reference-vectors-libsql.md) - Documentation for the LibSQLVector class in Mastra, which provides vector search using libSQL with vector extensions.
47
48
 
48
49
 
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.14.3",
2
+ "version": "1.15.0-alpha.1",
3
3
  "package": "@mastra/libsql",
4
4
  "exports": {},
5
5
  "modules": {}
@@ -1,3 +1,5 @@
1
+ > Discover all available pages from the documentation index: https://mastra.ai/llms.txt
2
+
1
3
  # Deploying
2
4
 
3
5
  > **Note:** The Agent Builder is part of the Mastra Enterprise Edition. Production deployments require a valid EE license. [Contact sales](https://mastra.ai/contact) for more information.
@@ -1,3 +1,5 @@
1
+ > Discover all available pages from the documentation index: https://mastra.ai/llms.txt
2
+
1
3
  # Agent Builder overview
2
4
 
3
5
  > **Note:** The Agent Builder is part of the Mastra Enterprise Edition. Production deployments require a valid EE license. [Contact sales](https://mastra.ai/contact) for more information.
@@ -1,3 +1,5 @@
1
+ > Discover all available pages from the documentation index: https://mastra.ai/llms.txt
2
+
1
3
  # Agent approval
2
4
 
3
5
  Agents sometimes require the same [human-in-the-loop](https://mastra.ai/docs/workflows/human-in-the-loop) oversight used in workflows when calling tools that handle sensitive operations, like deleting resources or running long processes. With agent approval you can suspend a tool call before it executes so a human can approve or decline it, or let tools suspend themselves to request additional context from the user.
@@ -1,3 +1,5 @@
1
+ > Discover all available pages from the documentation index: https://mastra.ai/llms.txt
2
+
1
3
  # Agent networks
2
4
 
3
5
  > **Deprecated — Use supervisor agents:** Agent networks are deprecated and will be removed in a future major release. [Supervisor agents](https://mastra.ai/docs/agents/supervisor-agents) using `agent.stream()` or `agent.generate()` are now the recommended approach. It provides the same multi-agent coordination with better control, a simpler API, and easier debugging.
@@ -1,3 +1,5 @@
1
+ > Discover all available pages from the documentation index: https://mastra.ai/llms.txt
2
+
1
3
  # Memory processors
2
4
 
3
5
  Memory processors transform and filter messages as they pass through an agent with memory enabled. They manage context window limits, remove unnecessary content, and optimize the information sent to the language model.
@@ -248,6 +250,7 @@ const contentBlocker = {
248
250
  }
249
251
 
250
252
  const agent = new Agent({
253
+ id: 'safe-agent',
251
254
  name: 'safe-agent',
252
255
  instructions: 'You are a helpful assistant',
253
256
  model: 'openai/gpt-5.5',
@@ -287,6 +290,7 @@ const inputValidator = {
287
290
  }
288
291
 
289
292
  const agent = new Agent({
293
+ id: 'validated-agent',
290
294
  name: 'validated-agent',
291
295
  instructions: 'You are a helpful assistant',
292
296
  model: 'openai/gpt-5.5',
@@ -1,3 +1,5 @@
1
+ > Discover all available pages from the documentation index: https://mastra.ai/llms.txt
2
+
1
3
  # Message history
2
4
 
3
5
  Message history is the most basic and important form of memory. It gives the LLM a view of recent messages in the context window, enabling your agent to reference earlier exchanges and respond coherently.
@@ -1,3 +1,5 @@
1
+ > Discover all available pages from the documentation index: https://mastra.ai/llms.txt
2
+
1
3
  # Multi-user threads
2
4
 
3
5
  A single Mastra thread can be shared by multiple users, each with their own name and functional role. You carry speaker identity in the message body so the agent can tell users apart while reading from a single shared thread.
@@ -1,3 +1,5 @@
1
+ > Discover all available pages from the documentation index: https://mastra.ai/llms.txt
2
+
1
3
  # Memory
2
4
 
3
5
  Memory enables your agent to remember user messages, agent replies, and tool results across interactions, giving it the context it needs to stay consistent, maintain conversation flow, and produce better answers over time.
@@ -174,7 +176,9 @@ Each delegation creates a fresh `threadId` and a deterministic `resourceId` for
174
176
 
175
177
  - **Thread ID**: Unique per delegation. The subagent starts with a clean message history every time it's called.
176
178
  - **Resource ID**: Derived as `{parentResourceId}-{agentName}`. Because the resource ID is stable across delegations, resource-scoped memory persists between calls. A subagent remembers facts from previous delegations by the same user.
177
- - **Memory instance**: If a subagent has no memory configured, it inherits the supervisor's `Memory` instance. If the subagent defines its own, that takes precedence.
179
+ - **Memory instance**: If a subagent has no memory configured, it inherits the supervisor's `Memory` instance, including all of its options. If the subagent defines its own, that takes precedence.
180
+
181
+ > **Note:** Title generation (`generateTitle`) is a top-level thread concern and is **not** applied to inherited subagent threads. Because each delegation creates an ephemeral thread that no one sees, running title generation for it would waste an LLM call per delegation. To generate titles for a subagent's own threads, give that subagent its own memory configuration.
178
182
 
179
183
  The supervisor forwards its conversation context to the subagent so it has enough background to complete the task. Only the delegation prompt and the subagent's response are saved — the full parent conversation isn't stored. You can control which messages reach the subagent with the [`messageFilter`](https://mastra.ai/docs/agents/supervisor-agents) callback.
180
184
 
@@ -1,3 +1,5 @@
1
+ > Discover all available pages from the documentation index: https://mastra.ai/llms.txt
2
+
1
3
  # Semantic recall
2
4
 
3
5
  If you ask your friend what they did last weekend, they will search in their memory for events associated with "last weekend" and then tell you what they did. That's sort of like how semantic recall works in Mastra.
@@ -130,6 +132,7 @@ The following options control semantic recall behavior:
130
132
 
131
133
  ```typescript
132
134
  const agent = new Agent({
135
+ id: 'agent',
133
136
  memory: new Memory({
134
137
  options: {
135
138
  semanticRecall: {
@@ -151,6 +154,7 @@ The `filter` option restricts semantic recall results to messages with matching
151
154
 
152
155
  ```typescript
153
156
  const agent = new Agent({
157
+ id: 'agent',
154
158
  memory: new Memory({
155
159
  options: {
156
160
  semanticRecall: {
@@ -217,6 +221,7 @@ import { Agent } from '@mastra/core/agent'
217
221
  import { ModelRouterEmbeddingModel } from '@mastra/core/llm'
218
222
 
219
223
  const agent = new Agent({
224
+ id: 'agent',
220
225
  memory: new Memory({
221
226
  embedder: new ModelRouterEmbeddingModel('openai/text-embedding-3-small'),
222
227
  options: {
@@ -238,6 +243,7 @@ import { Memory } from '@mastra/memory'
238
243
  import { ModelRouterEmbeddingModel } from '@mastra/core/llm'
239
244
 
240
245
  const agent = new Agent({
246
+ id: 'agent',
241
247
  memory: new Memory({
242
248
  embedder: new ModelRouterEmbeddingModel({
243
249
  providerId: 'openrouter',
@@ -259,6 +265,7 @@ import { Agent } from '@mastra/core/agent'
259
265
  import { ModelRouterEmbeddingModel } from '@mastra/core/llm'
260
266
 
261
267
  const agent = new Agent({
268
+ id: 'agent',
262
269
  memory: new Memory({
263
270
  embedder: new ModelRouterEmbeddingModel('openai/text-embedding-3-small'),
264
271
  }),
@@ -301,6 +308,7 @@ import { Agent } from '@mastra/core/agent'
301
308
  import { fastembed } from '@mastra/fastembed'
302
309
 
303
310
  const agent = new Agent({
311
+ id: 'agent',
304
312
  memory: new Memory({
305
313
  embedder: fastembed,
306
314
  }),
@@ -1,3 +1,5 @@
1
+ > Discover all available pages from the documentation index: https://mastra.ai/llms.txt
2
+
1
3
  # Storage
2
4
 
3
5
  For agents to remember previous interactions, Mastra needs a storage adapter. Use one of the [supported providers](#supported-providers) and pass it to your Mastra instance.
@@ -100,6 +102,10 @@ export const mastra = new Mastra({
100
102
 
101
103
  This is useful when different types of data have different performance or operational requirements, such as low-latency storage for memory, durable storage for workflows, and high-throughput storage for observability.
102
104
 
105
+ #### Retention
106
+
107
+ Storage grows without bound by default. To cap growth, declare per-table `maxAge` policies in the `retention` config and call `storage.prune()` from your own scheduler to delete old rows. Anything you don't configure is kept forever. See [Storage retention](https://mastra.ai/reference/storage/retention) for `prune()` and the full config shape.
108
+
103
109
  ### Agent-level storage
104
110
 
105
111
  Agent-level storage overrides storage configured at the instance level. Add storage to a specific agent when you need to keep data separate or use different providers per agent.
@@ -1,3 +1,5 @@
1
+ > Discover all available pages from the documentation index: https://mastra.ai/llms.txt
2
+
1
3
  # Working Memory
2
4
 
3
5
  While [message history](https://mastra.ai/docs/memory/message-history) and [semantic recall](https://mastra.ai/docs/memory/semantic-recall) help agents remember conversations, working memory allows them to maintain persistent information about users across interactions.
@@ -1,3 +1,5 @@
1
+ > Discover all available pages from the documentation index: https://mastra.ai/llms.txt
2
+
1
3
  # Retrieval in RAG systems
2
4
 
3
5
  After storing embeddings, you need to retrieve relevant chunks to answer user queries.
@@ -1,3 +1,5 @@
1
+ > Discover all available pages from the documentation index: https://mastra.ai/llms.txt
2
+
1
3
  # Snapshots
2
4
 
3
5
  In Mastra, a snapshot is a serializable representation of a workflow's complete execution state at a specific point in time. Snapshots capture all the information needed to resume a workflow from exactly where it left off, including:
@@ -1,3 +1,5 @@
1
+ > Discover all available pages from the documentation index: https://mastra.ai/llms.txt
2
+
1
3
  # AI SDK
2
4
 
3
5
  If you're already using the [Vercel AI SDK](https://sdk.vercel.ai) directly and want to add Mastra capabilities like [processors](https://mastra.ai/docs/agents/processors) or [memory](https://mastra.ai/docs/memory/memory-processors) without switching to the full Mastra agent API, [`withMastra()`](https://mastra.ai/reference/ai-sdk/with-mastra) lets you wrap any AI SDK model with these features. This is useful when you want to keep your existing AI SDK code but add input/output processing, conversation persistence, or content filtering.
@@ -1,3 +1,5 @@
1
+ > Discover all available pages from the documentation index: https://mastra.ai/llms.txt
2
+
1
3
  # Mastra.getMemory()
2
4
 
3
5
  The `.getMemory()` method retrieves a memory instance from the Mastra registry by its key. Memory instances are registered in the Mastra constructor and can be referenced by stored agents.
@@ -1,3 +1,5 @@
1
+ > Discover all available pages from the documentation index: https://mastra.ai/llms.txt
2
+
1
3
  # Mastra.listMemory()
2
4
 
3
5
  The `.listMemory()` method returns all memory instances registered with the Mastra instance.
@@ -1,3 +1,5 @@
1
+ > Discover all available pages from the documentation index: https://mastra.ai/llms.txt
2
+
1
3
  # Mastra class
2
4
 
3
5
  The `Mastra` class is the central orchestrator in any Mastra application, managing agents, workflows, storage, logging, observability, and more. Typically, you create a single instance of `Mastra` to coordinate your application.
@@ -1,3 +1,5 @@
1
+ > Discover all available pages from the documentation index: https://mastra.ai/llms.txt
2
+
1
3
  # Memory class
2
4
 
3
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.
@@ -9,6 +11,7 @@ import { Memory } from '@mastra/memory'
9
11
  import { Agent } from '@mastra/core/agent'
10
12
 
11
13
  export const agent = new Agent({
14
+ id: 'test-agent',
12
15
  name: 'test-agent',
13
16
  instructions: 'You are an agent with memory.',
14
17
  model: 'openai/gpt-5.5',
@@ -1,3 +1,5 @@
1
+ > Discover all available pages from the documentation index: https://mastra.ai/llms.txt
2
+
1
3
  # Composite storage
2
4
 
3
5
  `MastraCompositeStore` can compose storage domains from different providers. Use it when you need different databases for different purposes. For example, use LibSQL for memory and PostgreSQL for workflows.
@@ -1,3 +1,5 @@
1
+ > Discover all available pages from the documentation index: https://mastra.ai/llms.txt
2
+
1
3
  # DynamoDB storage
2
4
 
3
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/).
@@ -1,3 +1,5 @@
1
+ > Discover all available pages from the documentation index: https://mastra.ai/llms.txt
2
+
1
3
  # libSQL storage
2
4
 
3
5
  [libSQL](https://docs.turso.tech/libsql) is an open-source, SQLite-compatible database that supports both local and remote deployments. It can be used to store message history, workflow snapshots, traces, and eval scores.
@@ -0,0 +1,180 @@
1
+ > Discover all available pages from the documentation index: https://mastra.ai/llms.txt
2
+
3
+ # Storage retention
4
+
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
+
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
+
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 are not valid retention keys.
10
+
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
+
13
+ ## Usage example
14
+
15
+ Declare `retention` on any `MastraCompositeStore` (or an adapter that extends it, such as `LibSQLStore`), then call `prune()` from your own scheduler.
16
+
17
+ ```typescript
18
+ import { LibSQLStore } from '@mastra/libsql'
19
+
20
+ const storage = new LibSQLStore({
21
+ id: 'mastra-storage',
22
+ url: 'file:./mastra.db',
23
+ retention: {
24
+ memory: {
25
+ messages: { maxAge: '30d' },
26
+ threads: { maxAge: '90d', batchSize: 500 },
27
+ },
28
+ observability: {
29
+ spans: { maxAge: '7d' },
30
+ },
31
+ },
32
+ })
33
+
34
+ // Wire this to your own cron/scheduler — Mastra never runs it for you.
35
+ const results = await storage.prune()
36
+ ```
37
+
38
+ `retention` is fully typed. Keys must be real domain keys, and each table key must be one the domain declares as retention-eligible. Passing the object straight into a store config type-checks it; if you build it standalone, use `satisfies RetentionConfig` so unknown domains or tables are compile errors:
39
+
40
+ ```typescript
41
+ import type { RetentionConfig } from '@mastra/core/storage'
42
+
43
+ const retention = {
44
+ memory: {
45
+ messages: { maxAge: '30d' }, // ok
46
+ bogus: { maxAge: '30d' }, // Error: not a memory retention table
47
+ },
48
+ bogusDomain: {}, // Error: not a storage domain
49
+ } satisfies RetentionConfig
50
+ ```
51
+
52
+ ## Retention config
53
+
54
+ Set the `retention` field on the store config.
55
+
56
+ **retention** (`RetentionConfig`): Per-domain, per-table age policies. Unset domains and tables are kept forever.
57
+
58
+ **retention.\[domain]** (`Record<TableKey, TableRetentionPolicy>`): A real storage domain key (e.g. \`memory\`, \`observability\`). Maps that domain's retention-eligible table keys to their policies.
59
+
60
+ ### TableRetentionPolicy
61
+
62
+ **maxAge** (`Duration`): Maximum age to keep rows. Rows whose anchor timestamp is strictly older than \`Date.now() - maxAge\` are eligible for deletion. A number is milliseconds, or a string with a unit suffix: \`ms\`, \`s\`, \`m\`, \`h\`, \`d\`, \`w\` (e.g. \`'30d'\`, \`'12h'\`).
63
+
64
+ **batchSize** (`number`): Rows deleted per batch. Each batch is its own transaction, which bounds lock duration and WAL growth on large tables. (Default: `1000`)
65
+
66
+ ### Retention-eligible tables
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) |
88
+
89
+ > **Note:**
90
+ >
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
+ > - 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. There is no separate `results` key.
93
+ > - For `schedules`, the growth table is the fire history (`schedule_triggers`, one row per fire) — schedule definitions are config and are not pruned.
94
+ > - On PostgreSQL, timestamp anchors use the timezone-aware mirror columns (for example `createdAtZ`, `completedAtZ`).
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.
97
+
98
+ ## Methods
99
+
100
+ ### Retention
101
+
102
+ #### `prune(options?)`
103
+
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
+
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
+
108
+ 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.
109
+
110
+ ```typescript
111
+ const results = await storage.prune({
112
+ maxRows: 50_000, // cap work this call
113
+ pauseMs: 50, // breathe between batches
114
+ })
115
+
116
+ for (const r of results) {
117
+ console.log(`${r.domain}.${r.table}: deleted ${r.deleted}, done=${r.done}`)
118
+ }
119
+ ```
120
+
121
+ Returns: `Promise<PruneResult[]>`
122
+
123
+ ##### PruneOptions
124
+
125
+ **maxBatches** (`number`): Maximum delete batches per table per call. When reached, that table's result is returned with \`done: false\`.
126
+
127
+ **maxRows** (`number`): Maximum rows deleted per table per call. When reached, that table's result is returned with \`done: false\`.
128
+
129
+ **pauseMs** (`number`): Delay in milliseconds between batches, to avoid starving live traffic.
130
+
131
+ **signal** (`AbortSignal`): Cooperative cancellation. The batch loop checks it between batches and stops cleanly, returning partial results with \`done: false\`.
132
+
133
+ ##### PruneResult
134
+
135
+ Each result describes one table's progress:
136
+
137
+ ```typescript
138
+ interface PruneResult {
139
+ domain: string // e.g. 'memory'
140
+ table: string // physical table name, e.g. 'mastra_messages'
141
+ deleted: number // rows deleted during this call
142
+ done: boolean // false => eligible rows remain; call prune() again
143
+ }
144
+ ```
145
+
146
+ ## Running prune on a schedule
147
+
148
+ `prune()` has no built-in scheduler — you decide when it runs. Because it is 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.
149
+
150
+ ```typescript
151
+ // Runs on your own cron (node-cron, a workflow schedule, an external job, etc.).
152
+ async function retentionTick() {
153
+ const results = await storage.prune({ maxRows: 100_000, pauseMs: 25 })
154
+ const incomplete = results.filter(r => !r.done)
155
+ if (incomplete.length) {
156
+ // Rows remain; the next scheduled tick will continue where this one stopped.
157
+ console.log(
158
+ 'retention still draining:',
159
+ incomplete.map(r => `${r.domain}.${r.table}`),
160
+ )
161
+ }
162
+ }
163
+ ```
164
+
165
+ 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.
166
+
167
+ ## Reclaiming disk
168
+
169
+ `prune()` deletes rows but does not 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.
170
+
171
+ Handing that free space back to the OS is a separate concern that Mastra does not 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.
172
+
173
+ > **LibSQL and Turso:** [Turso Cloud](https://mastra.ai/reference/storage/libsql) manages storage compaction for you, so there's nothing to reclaim manually. This applies only to self-hosted libSQL files.
174
+
175
+ ## Related
176
+
177
+ - [libSQL storage](https://mastra.ai/reference/storage/libsql)
178
+ - [PostgreSQL storage](https://mastra.ai/reference/storage/postgresql)
179
+ - [Composite storage](https://mastra.ai/reference/storage/composite)
180
+ - [Storage overview](https://mastra.ai/reference/storage/overview)
@@ -1,3 +1,5 @@
1
+ > Discover all available pages from the documentation index: https://mastra.ai/llms.txt
2
+
1
3
  # libSQL vector store
2
4
 
3
5
  The libSQL storage implementation provides a SQLite-compatible vector search [libSQL](https://github.com/tursodatabase/libsql), a fork of SQLite with vector extensions, and [Turso](https://turso.tech/) with vector extensions, offering a lightweight and efficient vector database solution. It's part of the `@mastra/libsql` package and offers efficient vector similarity search with metadata filtering.