@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 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
@@ -3,7 +3,7 @@ name: mastra-pg
3
3
  description: Documentation for @mastra/pg. Use when working with @mastra/pg APIs, configuration, or implementation.
4
4
  metadata:
5
5
  package: "@mastra/pg"
6
- version: "1.20.0-alpha.0"
6
+ version: "1.20.0-alpha.2"
7
7
  ---
8
8
 
9
9
  ## When to use
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.20.0-alpha.0",
2
+ "version": "1.20.0-alpha.2",
3
3
  "package": "@mastra/pg",
4
4
  "exports": {},
5
5
  "modules": {}
@@ -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 supports all domains above; PostgreSQL and MongoDB support all except `threadState` and `harness`, which they don't implement.
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
- async _getIncludedMessages({ include }) {
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 targetRows = await this.#db.client.manyOrNone(`SELECT id, thread_id, "createdAt" FROM ${tableName} WHERE id IN (${idPlaceholders})`, targetIds);
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 paramIdx = 1;
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({ include });
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
- const countResult = await this.#db.client.one(`SELECT COUNT(*) FROM ${tableName} ${whereClause}`, queryParams);
9498
- total = parseInt(countResult.count, 10);
9499
- const limitValue = perPageInput === false ? total : perPage;
9500
- const dataQuery = `${selectStatement} FROM ${tableName} ${whereClause} ${orderByStatement} LIMIT $${paramIndex++} OFFSET $${paramIndex++}`;
9501
- messages = [...await this.#db.client.manyOrNone(dataQuery, [
9502
- ...queryParams,
9503
- limitValue,
9504
- offset
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 this._getIncludedMessages({ include });
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({ include });
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
- const countResult = await this.#db.client.one(`SELECT COUNT(*) FROM ${tableName} ${whereClause}`, queryParams);
9633
- total = parseInt(countResult.count, 10);
9634
- const limitValue = perPageInput === false ? total : perPage;
9635
- const dataQuery = `${selectStatement} FROM ${tableName} ${whereClause} ${orderByStatement} LIMIT $${paramIndex++} OFFSET $${paramIndex++}`;
9636
- messages = [...await this.#db.client.manyOrNone(dataQuery, [
9637
- ...queryParams,
9638
- limitValue,
9639
- offset
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 this._getIncludedMessages({ include });
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;