@mastra/libsql 1.15.1-alpha.0 → 1.16.0-alpha.0
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 +22 -0
- package/dist/docs/SKILL.md +4 -2
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/docs/references/docs-agent-builder-overview.md +4 -3
- package/dist/docs/references/docs-agents-agent-approval.md +25 -2
- package/dist/docs/references/docs-agents-networks.md +1 -1
- package/dist/docs/references/docs-memory-memory-processors.md +67 -0
- package/dist/docs/references/docs-memory-message-history.md +56 -2
- package/dist/docs/references/docs-memory-overview.md +5 -3
- package/dist/docs/references/docs-memory-semantic-recall.md +36 -2
- package/dist/docs/references/docs-memory-working-memory.md +3 -3
- package/dist/docs/references/docs-storage-overview.md +214 -0
- package/dist/docs/references/docs-workflows-snapshots.md +1 -1
- package/dist/docs/references/reference-core-mastra-class.md +1 -1
- package/dist/docs/references/reference-file-based-agents-memory.md +58 -0
- package/dist/docs/references/reference-file-based-agents-storage.md +30 -0
- package/dist/docs/references/reference-memory-memory-class.md +1 -1
- package/dist/docs/references/reference-storage-composite.md +64 -6
- package/dist/docs/references/reference-storage-dynamodb.md +1 -1
- package/dist/docs/references/reference-storage-retention.md +74 -6
- package/dist/index.cjs +25 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -1
- package/dist/vector/index.d.ts +12 -0
- package/dist/vector/index.d.ts.map +1 -1
- package/package.json +8 -8
- package/dist/docs/references/docs-memory-storage.md +0 -267
|
@@ -45,7 +45,7 @@ export const mastra = new Mastra({
|
|
|
45
45
|
})
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
-
`notifications.dispatch.enabled`
|
|
48
|
+
`notifications.dispatch.enabled` allows an internal dispatcher workflow to run with the default cron `*/1 * * * *`. The dispatcher reads due notification records from storage, groups summaries by `agentId`, `resourceId`, and `threadId`, and emits signals through the agent thread runtime. It isn't a user-facing entrypoint. The dispatch schedule (and the workflow scheduler backing it) activates lazily on the first deferred or summarized notification, so apps that never defer notifications don't run a scheduler at all.
|
|
49
49
|
|
|
50
50
|
## Constructor parameters
|
|
51
51
|
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
> Discover all available pages from the documentation index: https://mastra.ai/llms.txt
|
|
2
|
+
|
|
3
|
+
# Memory
|
|
4
|
+
|
|
5
|
+
A file-based agent gets [memory](https://mastra.ai/docs/memory/overview) from a `memory.ts` file that default-exports a [`Memory`](https://mastra.ai/reference/memory/memory-class) instance. Use this page for the file-based convention; use the memory docs for message history, semantic recall, storage, and processors.
|
|
6
|
+
|
|
7
|
+
Without `memory.ts` or `config.memory`, the agent has no memory by default. Each `generate()` or `stream()` call starts without remembered conversation state unless you pass the prior context yourself.
|
|
8
|
+
|
|
9
|
+
## Quickstart
|
|
10
|
+
|
|
11
|
+
Create `memory.ts` next to the agent's `config.ts`:
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { Memory } from '@mastra/memory'
|
|
15
|
+
|
|
16
|
+
export default new Memory()
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The exported instance becomes the agent's `memory`. If your app configures a storage provider on the main Mastra instance, memory data is stored there. See [storage](https://mastra.ai/docs/storage/overview) for more information.
|
|
20
|
+
|
|
21
|
+
Use the same `resource` and `thread` values when calling the agent to continue a conversation:
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
const response = await weatherAgent.generate('Remember that I prefer Celsius.', {
|
|
25
|
+
memory: {
|
|
26
|
+
resource: 'user-123',
|
|
27
|
+
thread: 'weather-chat',
|
|
28
|
+
},
|
|
29
|
+
})
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Configure memory
|
|
33
|
+
|
|
34
|
+
Pass options to `new Memory()` when the default behavior isn't enough.
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
import { Memory } from '@mastra/memory'
|
|
38
|
+
|
|
39
|
+
export default new Memory({
|
|
40
|
+
options: {
|
|
41
|
+
lastMessages: 20,
|
|
42
|
+
workingMemory: {
|
|
43
|
+
enabled: true,
|
|
44
|
+
scope: 'resource',
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
})
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Visit the [`Memory` reference](https://mastra.ai/reference/memory/memory-class) for constructor options. Use these pages for related memory features:
|
|
51
|
+
|
|
52
|
+
- [Storage](https://mastra.ai/docs/storage/overview): configure persistence for memory data.
|
|
53
|
+
- [Semantic recall](https://mastra.ai/docs/memory/semantic-recall): retrieve relevant past messages by semantic meaning.
|
|
54
|
+
- [Memory processors](https://mastra.ai/docs/memory/memory-processors): filter, trim, or transform messages before memory adds them to model context.
|
|
55
|
+
|
|
56
|
+
## Precedence with config
|
|
57
|
+
|
|
58
|
+
`config.memory` wins over `memory.ts`. If neither is present, the assembled file-based agent has no memory. See [`config.ts` precedence](https://mastra.ai/reference/file-based-agents/config) for the full merge table.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
> Discover all available pages from the documentation index: https://mastra.ai/llms.txt
|
|
2
|
+
|
|
3
|
+
# Storage
|
|
4
|
+
|
|
5
|
+
Mastra sets the project's default [storage](https://mastra.ai/docs/storage/overview) from a `storage.ts` file directly under `src/mastra/`. The file default-exports a store, which replaces the built-in in-memory store used for memory, workflows, observability, and other storage domains.
|
|
6
|
+
|
|
7
|
+
Use this page for the file-based convention. For backend choice, storage domains, retention, and provider details, see [storage overview](https://mastra.ai/docs/storage/overview).
|
|
8
|
+
|
|
9
|
+
## Quickstart
|
|
10
|
+
|
|
11
|
+
Use [`LibSQLStore`](https://mastra.ai/reference/storage/libsql) for a local file-backed store:
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { LibSQLStore } from '@mastra/libsql'
|
|
15
|
+
|
|
16
|
+
export default new LibSQLStore({
|
|
17
|
+
id: 'mastra-storage',
|
|
18
|
+
url: 'file:./mastra.db',
|
|
19
|
+
})
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Mastra registers the store before file-based agents and workflows, so storage-dependent primitives bind to this store instead of the default in-memory store.
|
|
23
|
+
|
|
24
|
+
## Production backends
|
|
25
|
+
|
|
26
|
+
`storage.ts` can export any Mastra storage adapter, such as LibSQL, PostgreSQL, or MongoDB. For setup patterns, provider support, and schema details, see [storage overview](https://mastra.ai/docs/storage/overview), [observability storage](https://mastra.ai/docs/observability/storage), and the [storage reference](https://mastra.ai/reference/storage/overview).
|
|
27
|
+
|
|
28
|
+
## Precedence with code
|
|
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`.
|
|
@@ -37,7 +37,7 @@ export const agent = new Agent({
|
|
|
37
37
|
|
|
38
38
|
**options** (`MemoryConfig`): Memory configuration options.
|
|
39
39
|
|
|
40
|
-
**options.lastMessages** (`number | false`): Number of most recent messages to include in context. Set to false to disable
|
|
40
|
+
**options.lastMessages** (`number | false`): Number of most recent messages to include in context. Set to false to disable the message history feature entirely (messages are not loaded into context or saved). Use Number.MAX\_SAFE\_INTEGER to retrieve all messages with no limit. To load messages without saving new ones, use the readOnly option.
|
|
41
41
|
|
|
42
42
|
**options.readOnly** (`boolean`): When true, prevents memory from saving new messages and provides working memory as read-only context (without the updateWorkingMemory tool). Useful for read-only operations like previews, internal routing agents, or sub agents that should reference but not modify memory.
|
|
43
43
|
|
|
@@ -37,25 +37,25 @@ You'll also need to install the storage providers you want to compose:
|
|
|
37
37
|
**npm**:
|
|
38
38
|
|
|
39
39
|
```bash
|
|
40
|
-
npm install @mastra/pg@latest @mastra/libsql@latest
|
|
40
|
+
npm install @mastra/pg@latest @mastra/libsql@latest @mastra/mongodb@latest
|
|
41
41
|
```
|
|
42
42
|
|
|
43
43
|
**pnpm**:
|
|
44
44
|
|
|
45
45
|
```bash
|
|
46
|
-
pnpm add @mastra/pg@latest @mastra/libsql@latest
|
|
46
|
+
pnpm add @mastra/pg@latest @mastra/libsql@latest @mastra/mongodb@latest
|
|
47
47
|
```
|
|
48
48
|
|
|
49
49
|
**Yarn**:
|
|
50
50
|
|
|
51
51
|
```bash
|
|
52
|
-
yarn add @mastra/pg@latest @mastra/libsql@latest
|
|
52
|
+
yarn add @mastra/pg@latest @mastra/libsql@latest @mastra/mongodb@latest
|
|
53
53
|
```
|
|
54
54
|
|
|
55
55
|
**Bun**:
|
|
56
56
|
|
|
57
57
|
```bash
|
|
58
|
-
bun add @mastra/pg@latest @mastra/libsql@latest
|
|
58
|
+
bun add @mastra/pg@latest @mastra/libsql@latest @mastra/mongodb@latest
|
|
59
59
|
```
|
|
60
60
|
|
|
61
61
|
## Storage domains
|
|
@@ -124,6 +124,64 @@ export const mastra = new Mastra({
|
|
|
124
124
|
})
|
|
125
125
|
```
|
|
126
126
|
|
|
127
|
+
### Mixed backends
|
|
128
|
+
|
|
129
|
+
Use domain classes from each storage package to route different domains to different backends. The following example stores memory and workflow state in MongoDB, then routes observability to ClickHouse:
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
import { Mastra } from '@mastra/core'
|
|
133
|
+
import { MastraCompositeStore } from '@mastra/core/storage'
|
|
134
|
+
import { ObservabilityStorageClickhouse } from '@mastra/clickhouse'
|
|
135
|
+
import { MemoryStorageMongoDB, WorkflowsStorageMongoDB } from '@mastra/mongodb'
|
|
136
|
+
|
|
137
|
+
export const mastra = new Mastra({
|
|
138
|
+
storage: new MastraCompositeStore({
|
|
139
|
+
id: 'composite',
|
|
140
|
+
domains: {
|
|
141
|
+
memory: new MemoryStorageMongoDB({
|
|
142
|
+
uri: process.env.MONGODB_URI,
|
|
143
|
+
dbName: 'mastra_memory',
|
|
144
|
+
}),
|
|
145
|
+
workflows: new WorkflowsStorageMongoDB({
|
|
146
|
+
uri: process.env.MONGODB_URI,
|
|
147
|
+
dbName: 'mastra_workflows',
|
|
148
|
+
}),
|
|
149
|
+
observability: new ObservabilityStorageClickhouse({
|
|
150
|
+
url: process.env.CLICKHOUSE_URL,
|
|
151
|
+
username: process.env.CLICKHOUSE_USERNAME,
|
|
152
|
+
password: process.env.CLICKHOUSE_PASSWORD,
|
|
153
|
+
}),
|
|
154
|
+
},
|
|
155
|
+
}),
|
|
156
|
+
})
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Disabling a domain
|
|
160
|
+
|
|
161
|
+
Set a domain to `false` to disable it. A disabled domain doesn't fall back to `default`, so data for that domain isn't persisted:
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
import { MastraCompositeStore } from '@mastra/core/storage'
|
|
165
|
+
import { PostgresStore } from '@mastra/pg'
|
|
166
|
+
import { Mastra } from '@mastra/core'
|
|
167
|
+
|
|
168
|
+
const pgStore = new PostgresStore({
|
|
169
|
+
id: 'pg',
|
|
170
|
+
connectionString: process.env.DATABASE_URL,
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
export const mastra = new Mastra({
|
|
174
|
+
storage: new MastraCompositeStore({
|
|
175
|
+
id: 'composite',
|
|
176
|
+
default: pgStore,
|
|
177
|
+
domains: {
|
|
178
|
+
// don't persist traces and spans
|
|
179
|
+
observability: false,
|
|
180
|
+
},
|
|
181
|
+
}),
|
|
182
|
+
})
|
|
183
|
+
```
|
|
184
|
+
|
|
127
185
|
## Options
|
|
128
186
|
|
|
129
187
|
**id** (`string`): Unique identifier for this storage instance.
|
|
@@ -132,7 +190,7 @@ export const mastra = new Mastra({
|
|
|
132
190
|
|
|
133
191
|
**disableInit** (`boolean`): When true, automatic initialization is disabled. You must call init() explicitly.
|
|
134
192
|
|
|
135
|
-
**domains** (`object`): Individual domain overrides. Each domain can come from a different storage adapter. These take precedence over both editor and default storage.
|
|
193
|
+
**domains** (`object`): Individual domain overrides. Each domain can come from a different storage adapter. These take precedence over both editor and default storage. Set a domain to false to disable it entirely; a disabled domain does not fall back to editor or default.
|
|
136
194
|
|
|
137
195
|
**domains.memory** (`MemoryStorage`): Storage for threads, messages, and resources.
|
|
138
196
|
|
|
@@ -274,6 +332,6 @@ const storage = new MastraCompositeStore({
|
|
|
274
332
|
})
|
|
275
333
|
```
|
|
276
334
|
|
|
277
|
-
|
|
335
|
+
Don't set `replication` on ClickHouse Cloud. Cloud rewrites `MergeTree` to `SharedMergeTree` server-side. See the [ClickHouse storage reference](https://mastra.ai/reference/storage/clickhouse) for the full config shape and operator notes.
|
|
278
336
|
|
|
279
337
|
> **Info:** This approach is also required when using storage providers that don't support observability (like Convex, DynamoDB, or Cloudflare). See the [MastraStorageExporter documentation](https://mastra.ai/docs/observability/integrations/exporters/mastra-storage) for the full list of supported providers.
|
|
@@ -6,7 +6,7 @@ The DynamoDB storage implementation provides a scalable and performant NoSQL dat
|
|
|
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
|
|
|
9
|
-
> **Item Size Limit:** DynamoDB enforces a **400 KB maximum item size**. This limit can be exceeded when storing messages with base64-encoded attachments such as images. See [Handling large attachments](https://mastra.ai/docs/memory/
|
|
9
|
+
> **Item Size Limit:** DynamoDB enforces a **400 KB maximum item size**. This limit can be exceeded when storing messages with base64-encoded attachments such as images. See [Handling large attachments](https://mastra.ai/docs/memory/memory-processors) for workarounds including uploading attachments to external storage.
|
|
10
10
|
|
|
11
11
|
## Features
|
|
12
12
|
|
|
@@ -6,7 +6,7 @@ Storage grows without bound by default. Retention is an opt-in, age-based cleanu
|
|
|
6
6
|
|
|
7
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
|
|
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
|
|
|
@@ -89,8 +89,8 @@ Each domain declares which of its tables can be age-pruned and which timestamp c
|
|
|
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
|
-
> - 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.
|
|
93
|
-
> - For `schedules`, the growth table is the fire history (`schedule_triggers`, one row per fire) — schedule definitions are config and
|
|
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.
|
|
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
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.
|
|
@@ -105,6 +105,8 @@ Deletes rows older than their configured `maxAge` across every domain that has a
|
|
|
105
105
|
|
|
106
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.
|
|
109
|
+
|
|
108
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.
|
|
109
111
|
|
|
110
112
|
```typescript
|
|
@@ -116,6 +118,13 @@ const results = await storage.prune({
|
|
|
116
118
|
for (const r of results) {
|
|
117
119
|
console.log(`${r.domain}.${r.table}: deleted ${r.deleted}, done=${r.done}`)
|
|
118
120
|
}
|
|
121
|
+
|
|
122
|
+
// One-off pass with different policies (configured retention untouched):
|
|
123
|
+
await storage.prune({
|
|
124
|
+
retention: {
|
|
125
|
+
observability: { spans: { maxAge: '1d' } },
|
|
126
|
+
},
|
|
127
|
+
})
|
|
119
128
|
```
|
|
120
129
|
|
|
121
130
|
Returns: `Promise<PruneResult[]>`
|
|
@@ -130,6 +139,8 @@ Returns: `Promise<PruneResult[]>`
|
|
|
130
139
|
|
|
131
140
|
**signal** (`AbortSignal`): Cooperative cancellation. The batch loop checks it between batches and stops cleanly, returning partial results with done: false.
|
|
132
141
|
|
|
142
|
+
**retention** (`RetentionConfig`): Replaces the store's configured retention policies for this call only — e.g. to skip a domain or prune more aggressively. The configured retention is unchanged.
|
|
143
|
+
|
|
133
144
|
##### PruneResult
|
|
134
145
|
|
|
135
146
|
Each result describes one table's progress:
|
|
@@ -145,7 +156,7 @@ interface PruneResult {
|
|
|
145
156
|
|
|
146
157
|
## Running prune on a schedule
|
|
147
158
|
|
|
148
|
-
`prune()` has no built-in scheduler — you decide when it runs. Because it
|
|
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.
|
|
149
160
|
|
|
150
161
|
```typescript
|
|
151
162
|
// Runs on your own cron (node-cron, a workflow schedule, an external job, etc.).
|
|
@@ -164,11 +175,68 @@ async function retentionTick() {
|
|
|
164
175
|
|
|
165
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.
|
|
166
177
|
|
|
178
|
+
## MongoDB TTL indexes (alternative to prune)
|
|
179
|
+
|
|
180
|
+
MongoDB offers native [TTL (Time-To-Live) indexes](https://www.mongodb.com/docs/manual/core/index-ttl/) that automatically delete expired documents without requiring manual `prune()` calls. This is a database-level feature that runs as a background thread.
|
|
181
|
+
|
|
182
|
+
> **When to use TTL vs prune():** **Use MongoDB TTL indexes when:**
|
|
183
|
+
>
|
|
184
|
+
> - You want automated, zero-maintenance deletion
|
|
185
|
+
> - Your retention periods are fixed (e.g., "always 30 days")
|
|
186
|
+
> - You prefer database-native solutions
|
|
187
|
+
>
|
|
188
|
+
> **Use `prune()` when:**
|
|
189
|
+
>
|
|
190
|
+
> - You need fine-grained control over deletion timing
|
|
191
|
+
> - You want to cap deletion rate during business hours
|
|
192
|
+
> - You need resumable, cancellable cleanup operations
|
|
193
|
+
> - You're using composite storage with multiple databases
|
|
194
|
+
>
|
|
195
|
+
> Both approaches are valid. TTL is simpler; `prune()` gives more control.
|
|
196
|
+
|
|
197
|
+
### Setting up TTL indexes on MongoDB
|
|
198
|
+
|
|
199
|
+
TTL indexes work on date fields. MongoDB checks the index every 60 seconds and deletes documents where the date field + TTL duration < current time.
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
import { MongoDBStore } from '@mastra/mongodb'
|
|
203
|
+
|
|
204
|
+
const storage = new MongoDBStore({
|
|
205
|
+
id: 'mongodb-storage',
|
|
206
|
+
uri: process.env.MONGODB_URI!,
|
|
207
|
+
dbName: process.env.MONGODB_DB_NAME!,
|
|
208
|
+
indexes: [
|
|
209
|
+
// Messages expire after 30 days
|
|
210
|
+
{
|
|
211
|
+
collection: 'mastra_messages',
|
|
212
|
+
keys: { createdAt: 1 },
|
|
213
|
+
options: { expireAfterSeconds: 30 * 24 * 60 * 60 }, // 30 days
|
|
214
|
+
},
|
|
215
|
+
// Threads expire after 90 days
|
|
216
|
+
{
|
|
217
|
+
collection: 'mastra_threads',
|
|
218
|
+
keys: { createdAt: 1 },
|
|
219
|
+
options: { expireAfterSeconds: 90 * 24 * 60 * 60 }, // 90 days
|
|
220
|
+
},
|
|
221
|
+
// Spans expire after 7 days
|
|
222
|
+
{
|
|
223
|
+
collection: 'mastra_ai_spans',
|
|
224
|
+
keys: { startedAt: 1 },
|
|
225
|
+
options: { expireAfterSeconds: 7 * 24 * 60 * 60 }, // 7 days
|
|
226
|
+
},
|
|
227
|
+
],
|
|
228
|
+
})
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
> **Tip:** TTL indexes delete documents shortly after they expire (background thread runs every \~60 seconds), but the exact timing is not guaranteed. For precise, immediate cleanup, use `prune()` instead.
|
|
232
|
+
|
|
167
233
|
## Reclaiming disk
|
|
168
234
|
|
|
169
|
-
`prune()` deletes rows but
|
|
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
|
+
|
|
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.
|
|
170
238
|
|
|
171
|
-
|
|
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.
|
|
172
240
|
|
|
173
241
|
> **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
242
|
|
package/dist/index.cjs
CHANGED
|
@@ -545,6 +545,31 @@ var LibSQLVector = class extends vector.MastraVector {
|
|
|
545
545
|
}
|
|
546
546
|
this.vectorIndexes = this.isMemoryDb ? Promise.resolve(/* @__PURE__ */ new Set()) : this.discoverVectorIndexes();
|
|
547
547
|
}
|
|
548
|
+
/**
|
|
549
|
+
* Closes the underlying libsql client, releasing all OS file handles.
|
|
550
|
+
*
|
|
551
|
+
* For local file databases, first runs PRAGMA wal_checkpoint(TRUNCATE) and
|
|
552
|
+
* switches back to journal_mode=DELETE so the -wal and -shm sidecar files
|
|
553
|
+
* are released promptly (mirrors LibSQLStore.close()).
|
|
554
|
+
*
|
|
555
|
+
* Remote (Turso) databases skip the WAL pragmas and just close the client.
|
|
556
|
+
*
|
|
557
|
+
* Safe to call more than once; subsequent calls are no-ops.
|
|
558
|
+
*/
|
|
559
|
+
async close() {
|
|
560
|
+
if (this.turso.closed) {
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
if (this.turso.protocol === "file" && !this.isMemoryDb) {
|
|
564
|
+
try {
|
|
565
|
+
await this.turso.execute("PRAGMA wal_checkpoint(TRUNCATE);");
|
|
566
|
+
await this.turso.execute("PRAGMA journal_mode=DELETE;");
|
|
567
|
+
} catch (err) {
|
|
568
|
+
this.logger.warn("LibSQLVector: Failed to checkpoint WAL before close.", err);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
this.turso.close();
|
|
572
|
+
}
|
|
548
573
|
async discoverVectorIndexes() {
|
|
549
574
|
try {
|
|
550
575
|
const result = await this.turso.execute({
|