@mastra/pg 1.20.0-alpha.0 → 1.20.0-alpha.2
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 +38 -0
- package/dist/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/docs/references/reference-storage-composite.md +58 -0
- package/dist/docs/references/reference-storage-retention.md +1 -1
- package/dist/index.cjs +267 -33
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +268 -35
- package/dist/index.js.map +1 -1
- package/dist/storage/domains/memory/index.d.ts +6 -0
- package/dist/storage/domains/memory/index.d.ts.map +1 -1
- package/dist/storage/domains/memory/test-utils.d.ts +30 -0
- package/dist/storage/domains/memory/test-utils.d.ts.map +1 -0
- package/dist/storage/domains/thread-state/index.d.ts +42 -0
- package/dist/storage/domains/thread-state/index.d.ts.map +1 -0
- package/dist/storage/index.d.ts +2 -1
- package/dist/storage/index.d.ts.map +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
# @mastra/pg
|
|
2
2
|
|
|
3
|
+
## 1.20.0-alpha.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Fixed `include` in `listMessages` and `listMessagesByResourceId` so it can no longer return a message that belongs to a different resource. When you pass a `resourceId`, the target message and its surrounding context messages now stay inside that resource. Includes that cross threads inside the same resource keep working, so semantic recall with `scope: 'resource'` is unchanged. ([#20984](https://github.com/mastra-ai/mastra/pull/20984))
|
|
8
|
+
|
|
9
|
+
**Behaviour change in the in-memory store**
|
|
10
|
+
|
|
11
|
+
The in-memory store read the context window from the thread you queried. It now reads the window from the thread that owns the target message, which is what the SQL stores already did. This only changes the result when an `include` entry names a message from another thread.
|
|
12
|
+
|
|
13
|
+
The in-memory store also ignored `include` in `listMessagesByResourceId`. It now returns the included messages, like `@mastra/libsql` and `@mastra/pg` do.
|
|
14
|
+
|
|
15
|
+
Fixes #20604.
|
|
16
|
+
|
|
17
|
+
- Add a persistent `threadState` domain to `PostgresStore` ([#20994](https://github.com/mastra-ai/mastra/pull/20994))
|
|
18
|
+
|
|
19
|
+
`PostgresStore` did not register a `threadState` domain, so durable task and goal
|
|
20
|
+
state fell back to in-memory storage and was lost on restart. Anything relying on
|
|
21
|
+
thread state (task tools, long-running agent goals) could not be backed by Postgres
|
|
22
|
+
alone, and required composing a second adapter through `MastraCompositeStore`.
|
|
23
|
+
|
|
24
|
+
`ThreadStatePG` now stores state as JSONB keyed by `(threadId, type)` with an atomic
|
|
25
|
+
upsert that preserves `createdAt`, participates in `exportSchemas()` and `init()`, and
|
|
26
|
+
supports retention pruning anchored on `updatedAtZ` so state for still-active threads
|
|
27
|
+
survives age-based pruning.
|
|
28
|
+
|
|
29
|
+
- Updated dependencies [[`6445eba`](https://github.com/mastra-ai/mastra/commit/6445eba6020abac681aba1cc9289f446cb400cbe), [`df31eb0`](https://github.com/mastra-ai/mastra/commit/df31eb0c7087d782a0d9346e467f9a4af4b0eef6), [`fcd0667`](https://github.com/mastra-ai/mastra/commit/fcd0667a4e378be35c9a1b1eb19cce78fbfd7282), [`bab06b1`](https://github.com/mastra-ai/mastra/commit/bab06b18923873a584bdfc71a6b4ec7fb4727fb7)]:
|
|
30
|
+
- @mastra/core@1.58.0-alpha.5
|
|
31
|
+
|
|
32
|
+
## 1.20.0-alpha.1
|
|
33
|
+
|
|
34
|
+
### Patch Changes
|
|
35
|
+
|
|
36
|
+
- Improved chat response time with PostgresStore. Message history now reads a page of messages and its total count in one query instead of two. When semantic recall is on, the recall read also starts at the same time as the page read. Each agent turn therefore makes fewer database round-trips, which is most noticeable on remote Postgres. ([#20979](https://github.com/mastra-ai/mastra/pull/20979))
|
|
37
|
+
|
|
38
|
+
- Updated dependencies [[`cdd5c33`](https://github.com/mastra-ai/mastra/commit/cdd5c33ac6c7118a9f139e6dc0e14e6a8ae31658), [`d7cf7fa`](https://github.com/mastra-ai/mastra/commit/d7cf7fafc1ae1b50bd8462dd0e6c671a8606db93), [`0f9a448`](https://github.com/mastra-ai/mastra/commit/0f9a448502157e59f7b76f24360ad497168f5ef8), [`289f4ce`](https://github.com/mastra-ai/mastra/commit/289f4ce16e3293370440172132c52ee787cbc09f), [`4f16ff8`](https://github.com/mastra-ai/mastra/commit/4f16ff824bf2f9b0ddc93f210477c10c8a4fb1ab), [`1c67d85`](https://github.com/mastra-ai/mastra/commit/1c67d85e9da8285662f4dbbf47e0378c3fee0747), [`ba24be6`](https://github.com/mastra-ai/mastra/commit/ba24be662439c331ab23a600041f93803c89eca8), [`842b5fe`](https://github.com/mastra-ai/mastra/commit/842b5fe22b6a7fa811bd14e48eb9af523ac989f2), [`80bdf3a`](https://github.com/mastra-ai/mastra/commit/80bdf3ae16ade6ff63bde0cb16fa2df8ab7dd4dd), [`9ba1247`](https://github.com/mastra-ai/mastra/commit/9ba12470c77f1c03642d720ce67e517e878f666e), [`fd96298`](https://github.com/mastra-ai/mastra/commit/fd96298a8367622f4ebfcaa97b5b6c1fbbd14564), [`6a84954`](https://github.com/mastra-ai/mastra/commit/6a84954a2667f85b6d59da652dab1bbff007ccb0), [`52d8ef0`](https://github.com/mastra-ai/mastra/commit/52d8ef03801f1deb7ee48532fc4190dd4a33916c), [`cdd5c33`](https://github.com/mastra-ai/mastra/commit/cdd5c33ac6c7118a9f139e6dc0e14e6a8ae31658), [`efd5c81`](https://github.com/mastra-ai/mastra/commit/efd5c81cc25fde3c2ddd86fc1178deb4ec176e19), [`0976933`](https://github.com/mastra-ai/mastra/commit/0976933142333ec78451feef265b68bcb45aa5e7), [`242b945`](https://github.com/mastra-ai/mastra/commit/242b94558777bfbdeb42cbfea84afff0b6ad0633), [`fea5cae`](https://github.com/mastra-ai/mastra/commit/fea5caedc7e2cfea51784a15e015952692027abf), [`4b59f78`](https://github.com/mastra-ai/mastra/commit/4b59f786cbc9a7d1ef07a07517dbd4b96865e99d), [`7010c5d`](https://github.com/mastra-ai/mastra/commit/7010c5d15728bf9c5dfe4fb6b1bf80ce23bf143a)]:
|
|
39
|
+
- @mastra/core@1.58.0-alpha.3
|
|
40
|
+
|
|
3
41
|
## 1.20.0-alpha.0
|
|
4
42
|
|
|
5
43
|
### Minor Changes
|
package/dist/docs/SKILL.md
CHANGED
|
@@ -251,6 +251,64 @@ const memoryStore = await storage.getStore('memory')
|
|
|
251
251
|
const thread = await memoryStore?.getThreadById({ threadId: '...' })
|
|
252
252
|
```
|
|
253
253
|
|
|
254
|
+
## Closing connections
|
|
255
|
+
|
|
256
|
+
`close()` releases the connections of the stores a composite was built from: the `default` and `editor` stores, plus any domain that owns its own client. Each store is closed once, even when it backs several domains. When passed to the Mastra class, `close()` is called by `shutdown()`:
|
|
257
|
+
|
|
258
|
+
```typescript
|
|
259
|
+
import { MastraCompositeStore } from '@mastra/core/storage'
|
|
260
|
+
import { PostgresStore } from '@mastra/pg'
|
|
261
|
+
import { Mastra } from '@mastra/core'
|
|
262
|
+
|
|
263
|
+
const pgStore = new PostgresStore({
|
|
264
|
+
id: 'pg-storage',
|
|
265
|
+
connectionString: process.env.DATABASE_URL,
|
|
266
|
+
})
|
|
267
|
+
|
|
268
|
+
export const mastra = new Mastra({
|
|
269
|
+
storage: new MastraCompositeStore({ id: 'composite', default: pgStore }),
|
|
270
|
+
})
|
|
271
|
+
|
|
272
|
+
process.on('SIGTERM', async () => {
|
|
273
|
+
// Releases the Postgres pool, so the process can exit
|
|
274
|
+
await mastra.shutdown()
|
|
275
|
+
})
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
A store you construct only to supply a domain isn't reachable through the composite. Keep a reference to it and close it yourself:
|
|
279
|
+
|
|
280
|
+
```typescript
|
|
281
|
+
import { MastraCompositeStore } from '@mastra/core/storage'
|
|
282
|
+
import { ClickhouseStore } from '@mastra/clickhouse'
|
|
283
|
+
import { PostgresStore } from '@mastra/pg'
|
|
284
|
+
import { Mastra } from '@mastra/core'
|
|
285
|
+
|
|
286
|
+
const pgStore = new PostgresStore({
|
|
287
|
+
id: 'pg-storage',
|
|
288
|
+
connectionString: process.env.DATABASE_URL,
|
|
289
|
+
})
|
|
290
|
+
|
|
291
|
+
const clickhouseStore = new ClickhouseStore({
|
|
292
|
+
id: 'clickhouse-storage',
|
|
293
|
+
url: process.env.CLICKHOUSE_URL,
|
|
294
|
+
username: process.env.CLICKHOUSE_USERNAME,
|
|
295
|
+
password: process.env.CLICKHOUSE_PASSWORD,
|
|
296
|
+
})
|
|
297
|
+
|
|
298
|
+
export const mastra = new Mastra({
|
|
299
|
+
storage: new MastraCompositeStore({
|
|
300
|
+
id: 'composite',
|
|
301
|
+
default: pgStore,
|
|
302
|
+
domains: { observability: clickhouseStore.stores?.observability },
|
|
303
|
+
}),
|
|
304
|
+
})
|
|
305
|
+
|
|
306
|
+
process.on('SIGTERM', async () => {
|
|
307
|
+
await mastra.shutdown()
|
|
308
|
+
await clickhouseStore.close()
|
|
309
|
+
})
|
|
310
|
+
```
|
|
311
|
+
|
|
254
312
|
## Use cases
|
|
255
313
|
|
|
256
314
|
### Separate databases for different workloads
|
|
@@ -92,7 +92,7 @@ Each domain declares which of its tables can be age-pruned and which timestamp c
|
|
|
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
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
|
-
> - LibSQL
|
|
95
|
+
> - LibSQL and PostgreSQL support all domains above except `harness`, which PostgreSQL doesn't implement. MongoDB supports all except `threadState` and `harness`.
|
|
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 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
|
package/dist/index.cjs
CHANGED
|
@@ -9307,7 +9307,13 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
|
|
|
9307
9307
|
return direction === "ASC" ? String(aValue).localeCompare(String(bValue)) : String(bValue).localeCompare(String(aValue));
|
|
9308
9308
|
});
|
|
9309
9309
|
}
|
|
9310
|
-
|
|
9310
|
+
/**
|
|
9311
|
+
* Fetches included messages by ID, discovering their thread automatically.
|
|
9312
|
+
* This handles cross-thread includes where the include item doesn't specify a threadId.
|
|
9313
|
+
* When a resourceId is given, both the target lookup and the surrounding window stay
|
|
9314
|
+
* inside that resource, so an include never leaks another resource's messages.
|
|
9315
|
+
*/
|
|
9316
|
+
async _getIncludedMessages({ include, resourceId }) {
|
|
9311
9317
|
if (!include || include.length === 0) return null;
|
|
9312
9318
|
const tableName = getTableName$3({
|
|
9313
9319
|
indexName: _mastra_core_storage.TABLE_MESSAGES,
|
|
@@ -9317,7 +9323,8 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
|
|
|
9317
9323
|
const targetIds = include.map((inc) => inc.id).filter(Boolean);
|
|
9318
9324
|
if (targetIds.length === 0) return null;
|
|
9319
9325
|
const idPlaceholders = targetIds.map((_, i) => "$" + (i + 1)).join(", ");
|
|
9320
|
-
const
|
|
9326
|
+
const targetResourceCondition = resourceId ? ` AND "resourceId" = $${targetIds.length + 1}` : "";
|
|
9327
|
+
const targetRows = await this.#db.client.manyOrNone(`SELECT id, thread_id, "createdAt" FROM ${tableName} WHERE id IN (${idPlaceholders})${targetResourceCondition}`, resourceId ? [...targetIds, resourceId] : targetIds);
|
|
9321
9328
|
if (targetRows.length === 0) return null;
|
|
9322
9329
|
const targetMap = new Map(targetRows.map((r) => [r.id, {
|
|
9323
9330
|
threadId: r.thread_id,
|
|
@@ -9325,7 +9332,12 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
|
|
|
9325
9332
|
}]));
|
|
9326
9333
|
const unionQueries = [];
|
|
9327
9334
|
const params = [];
|
|
9328
|
-
let
|
|
9335
|
+
let resourceCondition = "";
|
|
9336
|
+
if (resourceId) {
|
|
9337
|
+
params.push(resourceId);
|
|
9338
|
+
resourceCondition = ` AND m."resourceId" = $1`;
|
|
9339
|
+
}
|
|
9340
|
+
let paramIdx = params.length + 1;
|
|
9329
9341
|
for (const inc of include) {
|
|
9330
9342
|
const { id, withPreviousMessages = 0, withNextMessages = 0 } = inc;
|
|
9331
9343
|
const target = targetMap.get(id);
|
|
@@ -9337,7 +9349,7 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
|
|
|
9337
9349
|
SELECT ${selectColumns}
|
|
9338
9350
|
FROM ${tableName} m
|
|
9339
9351
|
WHERE m.thread_id = ${p1}
|
|
9340
|
-
AND m."createdAt" <= ${p2}
|
|
9352
|
+
AND m."createdAt" <= ${p2}${resourceCondition}
|
|
9341
9353
|
ORDER BY m."createdAt" DESC, m.id DESC
|
|
9342
9354
|
LIMIT ${p3}
|
|
9343
9355
|
)`);
|
|
@@ -9351,7 +9363,7 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
|
|
|
9351
9363
|
SELECT ${selectColumns}
|
|
9352
9364
|
FROM ${tableName} m
|
|
9353
9365
|
WHERE m.thread_id = ${p4}
|
|
9354
|
-
AND m."createdAt" > ${p5}
|
|
9366
|
+
AND m."createdAt" > ${p5}${resourceCondition}
|
|
9355
9367
|
ORDER BY m."createdAt" ASC, m.id ASC
|
|
9356
9368
|
LIMIT ${p6}
|
|
9357
9369
|
)`);
|
|
@@ -9413,6 +9425,38 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
|
|
|
9413
9425
|
throw mastraError;
|
|
9414
9426
|
}
|
|
9415
9427
|
}
|
|
9428
|
+
/**
|
|
9429
|
+
* Reads one page of messages together with the total row count.
|
|
9430
|
+
*
|
|
9431
|
+
* `COUNT(*) OVER ()` reports the count over the whole WHERE result on the same
|
|
9432
|
+
* statement as the page, so the page costs one database round-trip instead of
|
|
9433
|
+
* two. The page and the count also come from one snapshot, so the count always
|
|
9434
|
+
* describes the returned rows. A separate `COUNT(*)` runs only when the page is
|
|
9435
|
+
* empty and the caller asked for a page after the last row, because a window
|
|
9436
|
+
* function has no row to carry the count on.
|
|
9437
|
+
*/
|
|
9438
|
+
async #fetchMessagePage({ selectStatement, tableName, whereClause, orderByStatement, queryParams, perPageInput, perPage, offset }) {
|
|
9439
|
+
const limitClause = perPageInput === false ? "" : ` LIMIT $${queryParams.length + 1} OFFSET $${queryParams.length + 2}`;
|
|
9440
|
+
const dataParams = perPageInput === false ? queryParams : [
|
|
9441
|
+
...queryParams,
|
|
9442
|
+
perPage,
|
|
9443
|
+
offset
|
|
9444
|
+
];
|
|
9445
|
+
const rows = await this.#db.client.manyOrNone(`${selectStatement}, COUNT(*) OVER () AS "__total" FROM ${tableName} ${whereClause} ${orderByStatement}${limitClause}`, dataParams) || [];
|
|
9446
|
+
if (rows.length > 0) return {
|
|
9447
|
+
total: Number(rows[0].__total),
|
|
9448
|
+
messages: rows
|
|
9449
|
+
};
|
|
9450
|
+
if (offset === 0) return {
|
|
9451
|
+
total: 0,
|
|
9452
|
+
messages: []
|
|
9453
|
+
};
|
|
9454
|
+
const countResult = await this.#db.client.one(`SELECT COUNT(*) FROM ${tableName} ${whereClause}`, queryParams);
|
|
9455
|
+
return {
|
|
9456
|
+
total: parseInt(countResult.count, 10),
|
|
9457
|
+
messages: []
|
|
9458
|
+
};
|
|
9459
|
+
}
|
|
9416
9460
|
async listMessages(args) {
|
|
9417
9461
|
const { threadId, resourceId, include, filter, perPage: perPageInput, page = 0, orderBy } = args;
|
|
9418
9462
|
const threadIds = (Array.isArray(threadId) ? threadId : [threadId]).filter((id) => typeof id === "string");
|
|
@@ -9469,7 +9513,10 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
|
|
|
9469
9513
|
hasMore: false
|
|
9470
9514
|
};
|
|
9471
9515
|
if (perPage === 0 && include && include.length > 0) {
|
|
9472
|
-
const includeMessages = await this._getIncludedMessages({
|
|
9516
|
+
const includeMessages = await this._getIncludedMessages({
|
|
9517
|
+
include,
|
|
9518
|
+
resourceId
|
|
9519
|
+
});
|
|
9473
9520
|
if (!includeMessages || includeMessages.length === 0) return {
|
|
9474
9521
|
messages: [],
|
|
9475
9522
|
total: 0,
|
|
@@ -9487,23 +9534,30 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
|
|
|
9487
9534
|
hasMore: false
|
|
9488
9535
|
};
|
|
9489
9536
|
}
|
|
9537
|
+
let includeFailure;
|
|
9538
|
+
const includePromise = include && include.length > 0 ? this._getIncludedMessages({
|
|
9539
|
+
include,
|
|
9540
|
+
resourceId
|
|
9541
|
+
}).catch((error) => {
|
|
9542
|
+
includeFailure = error;
|
|
9543
|
+
return null;
|
|
9544
|
+
}) : null;
|
|
9490
9545
|
let total;
|
|
9491
9546
|
let messages;
|
|
9492
9547
|
if (metadataFilter) {
|
|
9493
9548
|
const filteredRows = (await this.#db.client.manyOrNone(`${selectStatement} FROM ${tableName} ${whereClause} ${orderByStatement}`, queryParams) || []).filter((row) => (0, _mastra_core_storage.storageMessageMatchesMetadataFilter)(row.content, metadataFilter));
|
|
9494
9549
|
total = filteredRows.length;
|
|
9495
9550
|
messages = perPageInput === false ? filteredRows : filteredRows.slice(offset, offset + perPage);
|
|
9496
|
-
} else {
|
|
9497
|
-
|
|
9498
|
-
|
|
9499
|
-
|
|
9500
|
-
|
|
9501
|
-
|
|
9502
|
-
|
|
9503
|
-
|
|
9504
|
-
|
|
9505
|
-
|
|
9506
|
-
}
|
|
9551
|
+
} else ({total, messages} = await this.#fetchMessagePage({
|
|
9552
|
+
selectStatement,
|
|
9553
|
+
tableName,
|
|
9554
|
+
whereClause,
|
|
9555
|
+
orderByStatement,
|
|
9556
|
+
queryParams,
|
|
9557
|
+
perPageInput,
|
|
9558
|
+
perPage,
|
|
9559
|
+
offset
|
|
9560
|
+
}));
|
|
9507
9561
|
const primaryPageCount = messages.length;
|
|
9508
9562
|
if (total === 0 && messages.length === 0 && (!include || include.length === 0)) return {
|
|
9509
9563
|
messages: [],
|
|
@@ -9514,7 +9568,8 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
|
|
|
9514
9568
|
};
|
|
9515
9569
|
const messageIds = new Set(messages.map((m) => m.id));
|
|
9516
9570
|
if (include && include.length > 0) {
|
|
9517
|
-
const includeMessages = await
|
|
9571
|
+
const includeMessages = await includePromise;
|
|
9572
|
+
if (includeFailure) throw includeFailure;
|
|
9518
9573
|
if (includeMessages) {
|
|
9519
9574
|
for (const includeMsg of includeMessages) if (!messageIds.has(includeMsg.id)) {
|
|
9520
9575
|
messages.push(includeMsg);
|
|
@@ -9604,7 +9659,10 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
|
|
|
9604
9659
|
hasMore: false
|
|
9605
9660
|
};
|
|
9606
9661
|
if (perPage === 0 && include && include.length > 0) {
|
|
9607
|
-
const includeMessages = await this._getIncludedMessages({
|
|
9662
|
+
const includeMessages = await this._getIncludedMessages({
|
|
9663
|
+
include,
|
|
9664
|
+
resourceId
|
|
9665
|
+
});
|
|
9608
9666
|
if (!includeMessages || includeMessages.length === 0) return {
|
|
9609
9667
|
messages: [],
|
|
9610
9668
|
total: 0,
|
|
@@ -9622,23 +9680,30 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
|
|
|
9622
9680
|
hasMore: false
|
|
9623
9681
|
};
|
|
9624
9682
|
}
|
|
9683
|
+
let includeFailure;
|
|
9684
|
+
const includePromise = include && include.length > 0 ? this._getIncludedMessages({
|
|
9685
|
+
include,
|
|
9686
|
+
resourceId
|
|
9687
|
+
}).catch((error) => {
|
|
9688
|
+
includeFailure = error;
|
|
9689
|
+
return null;
|
|
9690
|
+
}) : null;
|
|
9625
9691
|
let total;
|
|
9626
9692
|
let messages;
|
|
9627
9693
|
if (metadataFilter) {
|
|
9628
9694
|
const filteredRows = (await this.#db.client.manyOrNone(`${selectStatement} FROM ${tableName} ${whereClause} ${orderByStatement}`, queryParams) || []).filter((row) => (0, _mastra_core_storage.storageMessageMatchesMetadataFilter)(row.content, metadataFilter));
|
|
9629
9695
|
total = filteredRows.length;
|
|
9630
9696
|
messages = perPageInput === false ? filteredRows : filteredRows.slice(offset, offset + perPage);
|
|
9631
|
-
} else {
|
|
9632
|
-
|
|
9633
|
-
|
|
9634
|
-
|
|
9635
|
-
|
|
9636
|
-
|
|
9637
|
-
|
|
9638
|
-
|
|
9639
|
-
|
|
9640
|
-
|
|
9641
|
-
}
|
|
9697
|
+
} else ({total, messages} = await this.#fetchMessagePage({
|
|
9698
|
+
selectStatement,
|
|
9699
|
+
tableName,
|
|
9700
|
+
whereClause,
|
|
9701
|
+
orderByStatement,
|
|
9702
|
+
queryParams,
|
|
9703
|
+
perPageInput,
|
|
9704
|
+
perPage,
|
|
9705
|
+
offset
|
|
9706
|
+
}));
|
|
9642
9707
|
if (total === 0 && messages.length === 0 && (!include || include.length === 0)) return {
|
|
9643
9708
|
messages: [],
|
|
9644
9709
|
total: 0,
|
|
@@ -9648,7 +9713,8 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
|
|
|
9648
9713
|
};
|
|
9649
9714
|
const messageIds = new Set(messages.map((m) => m.id));
|
|
9650
9715
|
if (include && include.length > 0) {
|
|
9651
|
-
const includeMessages = await
|
|
9716
|
+
const includeMessages = await includePromise;
|
|
9717
|
+
if (includeFailure) throw includeFailure;
|
|
9652
9718
|
if (includeMessages) {
|
|
9653
9719
|
for (const includeMsg of includeMessages) if (!messageIds.has(includeMsg.id)) {
|
|
9654
9720
|
messages.push(includeMsg);
|
|
@@ -18646,6 +18712,171 @@ var SkillsPG = class SkillsPG extends _mastra_core_storage.SkillsStorage {
|
|
|
18646
18712
|
}
|
|
18647
18713
|
};
|
|
18648
18714
|
//#endregion
|
|
18715
|
+
//#region src/storage/domains/thread-state/index.ts
|
|
18716
|
+
const COMPOSITE_PRIMARY_KEY = ["threadId", "type"];
|
|
18717
|
+
/**
|
|
18718
|
+
* PostgreSQL implementation of {@link ThreadStateStorage}.
|
|
18719
|
+
*
|
|
18720
|
+
* Stores per-thread, per-type state in `mastra_thread_state`, keyed by the
|
|
18721
|
+
* composite primary key `(threadId, type)`. The `value` column is `jsonb`, so
|
|
18722
|
+
* payloads (the task list for `type = 'task'`, the goal objective for
|
|
18723
|
+
* `type = 'goal'`) come back already parsed.
|
|
18724
|
+
*/
|
|
18725
|
+
var ThreadStatePG = class ThreadStatePG extends _mastra_core_storage.ThreadStateStorage {
|
|
18726
|
+
#db;
|
|
18727
|
+
#schema;
|
|
18728
|
+
static MANAGED_TABLES = [_mastra_core_storage.TABLE_THREAD_STATE];
|
|
18729
|
+
/**
|
|
18730
|
+
* `thread_state` grows as a side effect of thread activity (one row per
|
|
18731
|
+
* thread per state type). It anchors on `updatedAtZ` (last activity), so
|
|
18732
|
+
* state for a thread that is still being written to is not pruned by
|
|
18733
|
+
* creation age.
|
|
18734
|
+
*/
|
|
18735
|
+
static retentionTables = { threadState: {
|
|
18736
|
+
table: _mastra_core_storage.TABLE_THREAD_STATE,
|
|
18737
|
+
column: "updatedAtZ",
|
|
18738
|
+
indexed: true
|
|
18739
|
+
} };
|
|
18740
|
+
constructor(config) {
|
|
18741
|
+
super();
|
|
18742
|
+
const { client, schemaName, skipDefaultIndexes } = resolvePgConfig(config);
|
|
18743
|
+
this.#db = new PgDB({
|
|
18744
|
+
client,
|
|
18745
|
+
schemaName,
|
|
18746
|
+
skipDefaultIndexes
|
|
18747
|
+
});
|
|
18748
|
+
this.#schema = schemaName || "public";
|
|
18749
|
+
}
|
|
18750
|
+
static getExportDDL(schemaName) {
|
|
18751
|
+
return [generateTableSQL({
|
|
18752
|
+
tableName: _mastra_core_storage.TABLE_THREAD_STATE,
|
|
18753
|
+
schema: _mastra_core_storage.TABLE_SCHEMAS[_mastra_core_storage.TABLE_THREAD_STATE],
|
|
18754
|
+
schemaName,
|
|
18755
|
+
compositePrimaryKey: COMPOSITE_PRIMARY_KEY,
|
|
18756
|
+
includeAllConstraints: true
|
|
18757
|
+
})];
|
|
18758
|
+
}
|
|
18759
|
+
async init() {
|
|
18760
|
+
await this.#db.createTable({
|
|
18761
|
+
tableName: _mastra_core_storage.TABLE_THREAD_STATE,
|
|
18762
|
+
schema: _mastra_core_storage.TABLE_SCHEMAS[_mastra_core_storage.TABLE_THREAD_STATE],
|
|
18763
|
+
compositePrimaryKey: COMPOSITE_PRIMARY_KEY
|
|
18764
|
+
});
|
|
18765
|
+
}
|
|
18766
|
+
get #table() {
|
|
18767
|
+
return getTableName$5({
|
|
18768
|
+
indexName: _mastra_core_storage.TABLE_THREAD_STATE,
|
|
18769
|
+
schemaName: getSchemaName$5(this.#schema)
|
|
18770
|
+
});
|
|
18771
|
+
}
|
|
18772
|
+
/**
|
|
18773
|
+
* Create the retention index on demand, mirroring the other PG domains: only
|
|
18774
|
+
* deployments that configure retention pay for the extra index. Best-effort —
|
|
18775
|
+
* a failure here leaves pruning correct, just slower.
|
|
18776
|
+
*/
|
|
18777
|
+
async #ensureRetentionIndexes(policies) {
|
|
18778
|
+
const prefix = this.#schema && this.#schema !== "public" ? `${this.#schema}_` : "";
|
|
18779
|
+
for (const [key, entry] of Object.entries(ThreadStatePG.retentionTables)) {
|
|
18780
|
+
if (!entry.indexed || !policies[key]) continue;
|
|
18781
|
+
try {
|
|
18782
|
+
await this.#db.ensureIndex({
|
|
18783
|
+
indexName: `${prefix}mastra_${key}_retention_idx`,
|
|
18784
|
+
tableName: entry.table,
|
|
18785
|
+
column: entry.column
|
|
18786
|
+
});
|
|
18787
|
+
} catch (error) {
|
|
18788
|
+
this.logger?.warn?.(`Failed to create retention index for ${entry.table}:`, error);
|
|
18789
|
+
}
|
|
18790
|
+
}
|
|
18791
|
+
}
|
|
18792
|
+
/** Delete thread state older than the `threadState` policy's `maxAge`, batched. */
|
|
18793
|
+
async prune(policies, options) {
|
|
18794
|
+
await this.#ensureRetentionIndexes(policies);
|
|
18795
|
+
const targets = resolveTargets({
|
|
18796
|
+
policies,
|
|
18797
|
+
descriptor: ThreadStatePG.retentionTables,
|
|
18798
|
+
order: ["threadState"]
|
|
18799
|
+
});
|
|
18800
|
+
return runPrune({
|
|
18801
|
+
db: this.#db,
|
|
18802
|
+
domain: "threadState",
|
|
18803
|
+
targets,
|
|
18804
|
+
options
|
|
18805
|
+
});
|
|
18806
|
+
}
|
|
18807
|
+
async dangerouslyClearAll() {
|
|
18808
|
+
try {
|
|
18809
|
+
await this.#db.client.none(`DELETE FROM ${this.#table}`);
|
|
18810
|
+
} catch (error) {
|
|
18811
|
+
throw new _mastra_core_error.MastraError({
|
|
18812
|
+
id: (0, _mastra_core_storage.createStorageErrorId)("PG", "THREAD_STATE_CLEAR_ALL", "FAILED"),
|
|
18813
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
18814
|
+
category: _mastra_core_error.ErrorCategory.THIRD_PARTY
|
|
18815
|
+
}, error);
|
|
18816
|
+
}
|
|
18817
|
+
}
|
|
18818
|
+
async getState({ threadId, type }) {
|
|
18819
|
+
try {
|
|
18820
|
+
const row = await this.#db.client.oneOrNone(`SELECT "value" FROM ${this.#table} WHERE "threadId" = $1 AND "type" = $2 LIMIT 1`, [threadId, type]);
|
|
18821
|
+
if (!row || row.value === null || row.value === void 0) return void 0;
|
|
18822
|
+
return typeof row.value === "string" ? JSON.parse(row.value) : row.value;
|
|
18823
|
+
} catch (error) {
|
|
18824
|
+
throw new _mastra_core_error.MastraError({
|
|
18825
|
+
id: (0, _mastra_core_storage.createStorageErrorId)("PG", "THREAD_STATE_GET", "FAILED"),
|
|
18826
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
18827
|
+
category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
|
|
18828
|
+
details: {
|
|
18829
|
+
threadId,
|
|
18830
|
+
type
|
|
18831
|
+
}
|
|
18832
|
+
}, error);
|
|
18833
|
+
}
|
|
18834
|
+
}
|
|
18835
|
+
async setState({ threadId, type, value }) {
|
|
18836
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
18837
|
+
const serialized = JSON.stringify(value ?? null);
|
|
18838
|
+
try {
|
|
18839
|
+
await this.#db.client.none(`INSERT INTO ${this.#table} ("threadId", "type", "value", "createdAt", "createdAtZ", "updatedAt", "updatedAtZ")
|
|
18840
|
+
VALUES ($1, $2, $3::jsonb, $4::timestamp, $5::timestamptz, $4::timestamp, $5::timestamptz)
|
|
18841
|
+
ON CONFLICT ("threadId", "type")
|
|
18842
|
+
DO UPDATE SET "value" = EXCLUDED."value",
|
|
18843
|
+
"updatedAt" = EXCLUDED."updatedAt",
|
|
18844
|
+
"updatedAtZ" = EXCLUDED."updatedAtZ"`, [
|
|
18845
|
+
threadId,
|
|
18846
|
+
type,
|
|
18847
|
+
serialized,
|
|
18848
|
+
now,
|
|
18849
|
+
now
|
|
18850
|
+
]);
|
|
18851
|
+
} catch (error) {
|
|
18852
|
+
throw new _mastra_core_error.MastraError({
|
|
18853
|
+
id: (0, _mastra_core_storage.createStorageErrorId)("PG", "THREAD_STATE_SET", "FAILED"),
|
|
18854
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
18855
|
+
category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
|
|
18856
|
+
details: {
|
|
18857
|
+
threadId,
|
|
18858
|
+
type
|
|
18859
|
+
}
|
|
18860
|
+
}, error);
|
|
18861
|
+
}
|
|
18862
|
+
}
|
|
18863
|
+
async deleteState({ threadId, type }) {
|
|
18864
|
+
try {
|
|
18865
|
+
await this.#db.client.none(`DELETE FROM ${this.#table} WHERE "threadId" = $1 AND "type" = $2`, [threadId, type]);
|
|
18866
|
+
} catch (error) {
|
|
18867
|
+
throw new _mastra_core_error.MastraError({
|
|
18868
|
+
id: (0, _mastra_core_storage.createStorageErrorId)("PG", "THREAD_STATE_DELETE", "FAILED"),
|
|
18869
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
18870
|
+
category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
|
|
18871
|
+
details: {
|
|
18872
|
+
threadId,
|
|
18873
|
+
type
|
|
18874
|
+
}
|
|
18875
|
+
}, error);
|
|
18876
|
+
}
|
|
18877
|
+
}
|
|
18878
|
+
};
|
|
18879
|
+
//#endregion
|
|
18649
18880
|
//#region src/storage/domains/tool-provider-connections/index.ts
|
|
18650
18881
|
function normaliseScope(raw) {
|
|
18651
18882
|
const value = raw == null ? "per-author" : String(raw);
|
|
@@ -20580,7 +20811,8 @@ const ALL_DOMAINS = [
|
|
|
20580
20811
|
BackgroundTasksPG,
|
|
20581
20812
|
FavoritesPG,
|
|
20582
20813
|
ChannelsPG,
|
|
20583
|
-
SchedulesPG
|
|
20814
|
+
SchedulesPG,
|
|
20815
|
+
ThreadStatePG
|
|
20584
20816
|
];
|
|
20585
20817
|
/**
|
|
20586
20818
|
* Exports the Mastra database schema as SQL DDL statements, including tables, indexes, and triggers.
|
|
@@ -20674,7 +20906,8 @@ var PostgresStore = class extends _mastra_core_storage.MastraCompositeStore {
|
|
|
20674
20906
|
experiments: new ExperimentsPG(domainConfig),
|
|
20675
20907
|
backgroundTasks: new BackgroundTasksPG(domainConfig),
|
|
20676
20908
|
channels: new ChannelsPG(domainConfig),
|
|
20677
|
-
schedules: new SchedulesPG(domainConfig)
|
|
20909
|
+
schedules: new SchedulesPG(domainConfig),
|
|
20910
|
+
threadState: new ThreadStatePG(domainConfig)
|
|
20678
20911
|
};
|
|
20679
20912
|
} catch (e) {
|
|
20680
20913
|
throw new _mastra_core_error.MastraError({
|
|
@@ -21005,6 +21238,7 @@ exports.SchedulesPG = SchedulesPG;
|
|
|
21005
21238
|
exports.ScorerDefinitionsPG = ScorerDefinitionsPG;
|
|
21006
21239
|
exports.ScoresPG = ScoresPG;
|
|
21007
21240
|
exports.SkillsPG = SkillsPG;
|
|
21241
|
+
exports.ThreadStatePG = ThreadStatePG;
|
|
21008
21242
|
exports.ToolProviderConnectionsPG = ToolProviderConnectionsPG;
|
|
21009
21243
|
exports.WorkflowDefinitionsPG = WorkflowDefinitionsPG;
|
|
21010
21244
|
exports.WorkflowsPG = WorkflowsPG;
|