@mastra/pg 1.31.0-alpha.0 → 1.31.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.
@@ -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.31.0-alpha.0"
6
+ version: "1.31.0-alpha.2"
7
7
  ---
8
8
 
9
9
  ## When to use
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.31.0-alpha.0",
2
+ "version": "1.31.0-alpha.2",
3
3
  "package": "@mastra/pg",
4
4
  "exports": {},
5
5
  "modules": {}
@@ -411,11 +411,11 @@ PostgreSQL supports both IVFFlat and HNSW indexes. By default, Mastra creates an
411
411
 
412
412
  ```typescript
413
413
  import { Memory } from '@mastra/memory'
414
- import { PgStore, PgVector } from '@mastra/pg'
414
+ import { PostgresStore, PgVector } from '@mastra/pg'
415
415
 
416
416
  const agent = new Agent({
417
417
  memory: new Memory({
418
- storage: new PgStore({
418
+ storage: new PostgresStore({
419
419
  id: 'agent-storage',
420
420
  connectionString: process.env.DATABASE_URL,
421
421
  }),
@@ -272,6 +272,22 @@ Schema-based working memory uses **merge semantics**, meaning the agent only nee
272
272
  - **Set a field to `null` to delete it:** This explicitly removes the field from memory
273
273
  - **Arrays are replaced entirely:** When an array field is provided, it replaces the existing array (arrays aren't merged element-by-element)
274
274
 
275
+ ### Concurrent updates
276
+
277
+ With `scope: 'resource'` and a storage adapter that supports atomic merges (currently `@mastra/pg`), each merge runs inside a row-locked database transaction. Concurrent updates to different fields from separate processes are all kept. Other adapters merge in the application, which only serializes writers inside a single process.
278
+
279
+ To merge a partial update from your own code, call `memory.mergeWorkingMemory()`:
280
+
281
+ ```typescript
282
+ await memory.mergeWorkingMemory({
283
+ threadId: 'thread-1',
284
+ resourceId: 'user-123',
285
+ workingMemory: { preferences: { tone: 'formal' } },
286
+ })
287
+ ```
288
+
289
+ It throws if working memory isn't schema-based and resource-scoped, or if the storage adapter doesn't support atomic merges. Check `await memory.supportsAtomicWorkingMemoryMerge()` first if you need to fall back.
290
+
275
291
  ## Choosing between template and schema
276
292
 
277
293
  - Use a **template** (Markdown) if you want the agent to maintain memory as a free-form text block, such as a user profile or scratchpad. Templates use **replace semantics**: the agent must provide the complete memory content on each update.
@@ -101,14 +101,14 @@ export const agent = new Agent({
101
101
  import { Memory } from '@mastra/memory'
102
102
  import { Agent } from '@mastra/core/agent'
103
103
  import { ModelRouterEmbeddingModel } from '@mastra/core/llm'
104
- import { PgStore, PgVector } from '@mastra/pg'
104
+ import { PostgresStore, PgVector } from '@mastra/pg'
105
105
 
106
106
  export const agent = new Agent({
107
107
  name: 'pg-agent',
108
108
  instructions: 'You are an agent with optimized PostgreSQL memory.',
109
109
  model: 'openai/gpt-5.6-sol',
110
110
  memory: new Memory({
111
- storage: new PgStore({
111
+ storage: new PostgresStore({
112
112
  id: 'pg-agent-storage',
113
113
  connectionString: process.env.DATABASE_URL,
114
114
  }),
@@ -40,9 +40,10 @@ const processor = new MessageHistory({
40
40
  ```typescript
41
41
  import { Agent } from '@mastra/core/agent'
42
42
  import { MessageHistory } from '@mastra/core/processors'
43
- import { PostgresStorage } from '@mastra/pg'
43
+ import { PostgresStore } from '@mastra/pg'
44
44
 
45
- const storage = new PostgresStorage({
45
+ const storage = new PostgresStore({
46
+ id: 'pg-storage',
46
47
  connectionString: process.env.DATABASE_URL,
47
48
  })
48
49
 
@@ -59,11 +59,10 @@ const processor = new SemanticRecall({
59
59
  ```typescript
60
60
  import { Agent } from '@mastra/core/agent'
61
61
  import { SemanticRecall, MessageHistory } from '@mastra/core/processors'
62
- import { PostgresStorage } from '@mastra/pg'
63
- import { PgVector } from '@mastra/pg'
62
+ import { PostgresStore, PgVector } from '@mastra/pg'
64
63
  import { openai } from '@ai-sdk/openai'
65
64
 
66
- const storage = new PostgresStorage({
65
+ const storage = new PostgresStore({
67
66
  id: 'pg-storage',
68
67
  connectionString: process.env.DATABASE_URL,
69
68
  })
@@ -62,9 +62,10 @@ const processor = new WorkingMemory({
62
62
  ```typescript
63
63
  import { Agent } from '@mastra/core/agent'
64
64
  import { WorkingMemory, MessageHistory } from '@mastra/core/processors'
65
- import { PostgresStorage } from '@mastra/pg'
65
+ import { PostgresStore } from '@mastra/pg'
66
66
 
67
- const storage = new PostgresStorage({
67
+ const storage = new PostgresStore({
68
+ id: 'pg-storage',
68
69
  connectionString: process.env.DATABASE_URL,
69
70
  })
70
71
 
@@ -40,7 +40,7 @@ const chunks = await doc.chunk({
40
40
  overlap: 50,
41
41
  separators: ['\n'],
42
42
  extract: {
43
- metadata: true, // Optionally extract metadata
43
+ title: true, // Optionally extract a title for each chunk
44
44
  },
45
45
  })
46
46
  ```
@@ -231,7 +231,7 @@ await store.createIndex({
231
231
  - [libSQL](https://mastra.ai/reference/vectors/libsql)
232
232
  - [MongoDB](https://mastra.ai/reference/vectors/mongodb)
233
233
  - [OracleDB](https://mastra.ai/reference/vectors/oracledb)
234
- - [PgStore](https://mastra.ai/reference/vectors/pg)
234
+ - [PgVector](https://mastra.ai/reference/vectors/pg)
235
235
  - [Pinecone](https://mastra.ai/reference/vectors/pinecone)
236
236
  - [Qdrant](https://mastra.ai/reference/vectors/qdrant)
237
237
  - [Upstash](https://mastra.ai/reference/vectors/upstash)
@@ -50,23 +50,25 @@ console.log(results)
50
50
 
51
51
  The `topK` parameter specifies the maximum number of most similar results to return from the vector search.
52
52
 
53
- Results include both the text content and a similarity score:
53
+ Each result includes an `id`, a similarity `score`, and the stored `metadata`. When you store chunk text in metadata, it's available as `metadata.text`:
54
54
 
55
55
  ```ts
56
56
  [
57
57
  {
58
- text: 'Climate change poses significant challenges...',
58
+ id: 'chunk-1',
59
59
  score: 0.89,
60
- metadata: { source: 'article1.txt' },
60
+ metadata: { text: 'Climate change poses significant challenges...', source: 'article1.txt' },
61
61
  },
62
62
  {
63
- text: 'Rising temperatures affect crop yields...',
63
+ id: 'chunk-2',
64
64
  score: 0.82,
65
- metadata: { source: 'article1.txt' },
65
+ metadata: { text: 'Rising temperatures affect crop yields...', source: 'article1.txt' },
66
66
  },
67
67
  ]
68
68
  ```
69
69
 
70
+ Results can also include `vector` when you set `includeVector: true`, and `document` when the store returns the original document (Chroma only).
71
+
70
72
  ## Advanced retrieval options
71
73
 
72
74
  ### Metadata Filtering
@@ -230,7 +230,7 @@ await store.upsert({
230
230
  **libSQL**:
231
231
 
232
232
  ```ts
233
- import { LibSQLVector } from '@mastra/core/vector/libsql'
233
+ import { LibSQLVector } from '@mastra/libsql'
234
234
 
235
235
  const store = new LibSQLVector({
236
236
  id: 'libsql-vector',
package/dist/index.cjs CHANGED
@@ -2499,7 +2499,8 @@ async function loadSchemaSnapshot(client, schemaName) {
2499
2499
  const schema = schemaName || "public";
2500
2500
  const [tableRows, columnRows, indexRows] = await Promise.all([
2501
2501
  client.manyOrNone(`SELECT tablename FROM pg_catalog.pg_tables WHERE schemaname = $1`, [schema]),
2502
- client.manyOrNone(`SELECT c.relname AS table_name, a.attname AS column_name, format_type(a.atttypid, a.atttypmod) AS data_type
2502
+ client.manyOrNone(`SELECT c.relname AS table_name, a.attname AS column_name, format_type(a.atttypid, a.atttypmod) AS data_type,
2503
+ a.attnotnull AS not_null
2503
2504
  FROM pg_catalog.pg_class c
2504
2505
  JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
2505
2506
  JOIN pg_catalog.pg_attribute a ON a.attrelid = c.oid
@@ -2507,7 +2508,7 @@ async function loadSchemaSnapshot(client, schemaName) {
2507
2508
  AND c.relkind IN ('r', 'p')
2508
2509
  AND a.attnum > 0
2509
2510
  AND NOT a.attisdropped`, [schema]),
2510
- client.manyOrNone(`SELECT c.relname AS indexname, i.indisreplident AS is_replica_identity, i.indisprimary AS is_primary
2511
+ client.manyOrNone(`SELECT c.relname AS indexname, i.indisreplident AS is_replica_identity, i.indisprimary AS is_primary, i.indisvalid AS is_valid
2511
2512
  FROM pg_catalog.pg_index i
2512
2513
  JOIN pg_catalog.pg_class c ON c.oid = i.indexrelid
2513
2514
  JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
@@ -2515,6 +2516,7 @@ async function loadSchemaSnapshot(client, schemaName) {
2515
2516
  ]);
2516
2517
  const columns = /* @__PURE__ */ new Map();
2517
2518
  const columnTypes = /* @__PURE__ */ new Map();
2519
+ const notNullColumns = /* @__PURE__ */ new Map();
2518
2520
  for (const row of columnRows) {
2519
2521
  let set = columns.get(row.table_name);
2520
2522
  if (!set) {
@@ -2528,12 +2530,22 @@ async function loadSchemaSnapshot(client, schemaName) {
2528
2530
  columnTypes.set(row.table_name, types);
2529
2531
  }
2530
2532
  types.set(row.column_name, row.data_type);
2533
+ if (row.not_null) {
2534
+ let notNull = notNullColumns.get(row.table_name);
2535
+ if (!notNull) {
2536
+ notNull = /* @__PURE__ */ new Set();
2537
+ notNullColumns.set(row.table_name, notNull);
2538
+ }
2539
+ notNull.add(row.column_name);
2540
+ }
2531
2541
  }
2532
2542
  const indexes = /* @__PURE__ */ new Set();
2533
2543
  const replicaIdentityIndexes = /* @__PURE__ */ new Set();
2534
2544
  const primaryKeyIndexes = /* @__PURE__ */ new Set();
2545
+ const invalidIndexes = /* @__PURE__ */ new Set();
2535
2546
  for (const row of indexRows) {
2536
- indexes.add(row.indexname);
2547
+ if (row.is_valid) indexes.add(row.indexname);
2548
+ else invalidIndexes.add(row.indexname);
2537
2549
  if (row.is_replica_identity) replicaIdentityIndexes.add(row.indexname);
2538
2550
  if (row.is_primary) primaryKeyIndexes.add(row.indexname.toLowerCase());
2539
2551
  }
@@ -2542,9 +2554,11 @@ async function loadSchemaSnapshot(client, schemaName) {
2542
2554
  tables: new Set(tableRows.map((r) => r.tablename)),
2543
2555
  columns,
2544
2556
  columnTypes,
2557
+ notNullColumns,
2545
2558
  indexes,
2546
2559
  replicaIdentityIndexes,
2547
- primaryKeyIndexes
2560
+ primaryKeyIndexes,
2561
+ invalidIndexes
2548
2562
  };
2549
2563
  }
2550
2564
  //#endregion
@@ -3619,6 +3633,33 @@ MIGRATION REQUIRED: Duplicate spans detected in ${duplicateInfo.tableName}\n====
3619
3633
  this.columnTypeCache.delete(tableName);
3620
3634
  }
3621
3635
  }
3636
+ /**
3637
+ * Drops NOT NULL from `columns` on `tableName` where it is still set. Answered
3638
+ * from the init snapshot when one is installed, so a converged schema issues
3639
+ * no query and never takes the ACCESS EXCLUSIVE lock.
3640
+ */
3641
+ async dropNotNull({ tableName, columns }) {
3642
+ const parsedColumns = columns.map((c) => (0, _mastra_core_utils.parseSqlIdentifier)(c, "column name"));
3643
+ const snapshot = this.schemaSnapshot;
3644
+ let toAlter;
3645
+ if (snapshot) {
3646
+ const notNull = snapshot.notNullColumns.get(tableName);
3647
+ toAlter = parsedColumns.filter((c) => notNull?.has(c));
3648
+ } else toAlter = (await this.client.any(`SELECT column_name FROM information_schema.columns
3649
+ WHERE table_schema = $1 AND table_name = $2 AND column_name = ANY($3) AND is_nullable = 'NO'`, [
3650
+ this.schemaName || "public",
3651
+ tableName,
3652
+ parsedColumns
3653
+ ])).map((r) => r.column_name);
3654
+ if (toAlter.length === 0) return;
3655
+ const fullTableName = getTableName$6({
3656
+ indexName: tableName,
3657
+ schemaName: getSchemaName$6(this.schemaName)
3658
+ });
3659
+ await this.client.none(`ALTER TABLE ${fullTableName} ${toAlter.map((c) => `ALTER COLUMN "${c}" DROP NOT NULL`).join(", ")}`);
3660
+ const notNull = snapshot?.notNullColumns.get(tableName);
3661
+ for (const c of toAlter) notNull?.delete(c);
3662
+ }
3622
3663
  async load({ tableName, keys }) {
3623
3664
  try {
3624
3665
  const keyEntries = Object.entries(keys).map(([key, value]) => [(0, _mastra_core_utils.parseSqlIdentifier)(key, "column name"), value]);
@@ -3692,11 +3733,8 @@ MIGRATION REQUIRED: Duplicate spans detected in ${duplicateInfo.tableName}\n====
3692
3733
  schemaName: getSchemaName$6(this.schemaName)
3693
3734
  });
3694
3735
  const snapshot = this.schemaSnapshot;
3695
- if (snapshot) {
3696
- if (snapshot.indexes.has(name)) return;
3697
- } else if (await this.client.oneOrNone(`SELECT 1 FROM pg_indexes
3698
- WHERE indexname = $1
3699
- AND schemaname = $2`, [name, schemaName])) return;
3736
+ if (snapshot?.indexes.has(name)) return;
3737
+ if (!await this.prepareIndexBuild(name, schemaName)) return;
3700
3738
  const uniqueStr = unique ? "UNIQUE " : "";
3701
3739
  const concurrentStr = concurrent ? "CONCURRENTLY " : "";
3702
3740
  const methodStr = method !== "btree" ? `USING ${method} ` : "";
@@ -3715,16 +3753,9 @@ MIGRATION REQUIRED: Duplicate spans detected in ${duplicateInfo.tableName}\n====
3715
3753
  let withStr = "";
3716
3754
  if (storage && Object.keys(storage).length > 0) withStr = ` WITH (${Object.entries(storage).map(([key, value]) => `${key} = ${value}`).join(", ")})`;
3717
3755
  const sql = `CREATE ${uniqueStr}INDEX ${concurrentStr}${`"${(0, _mastra_core_utils.parseSqlIdentifier)(name, "index name")}"`} ON ${fullTableName} ${methodStr}(${columnsStr})${withStr}${tablespaceStr}${whereStr}`;
3718
- await this.client.none(sql);
3756
+ await this.timedIndexBuild(name, sql);
3719
3757
  snapshot?.indexes.add(name);
3720
3758
  } catch (error) {
3721
- if (error instanceof Error && error.message.includes("CONCURRENTLY")) {
3722
- const retryOptions = {
3723
- ...options,
3724
- concurrent: false
3725
- };
3726
- return this.createIndex(retryOptions);
3727
- }
3728
3759
  throw new _mastra_core_error.MastraError({
3729
3760
  id: (0, _mastra_core_storage.createStorageErrorId)("PG", "INDEX_CREATE", "FAILED"),
3730
3761
  domain: _mastra_core_error.ErrorDomain.STORAGE,
@@ -3748,9 +3779,43 @@ MIGRATION REQUIRED: Duplicate spans detected in ${duplicateInfo.tableName}\n====
3748
3779
  async createIndexFromStatement(indexName, sql) {
3749
3780
  const snapshot = this.schemaSnapshot;
3750
3781
  if (snapshot?.indexes.has(indexName)) return;
3751
- await this.client.none(sql);
3782
+ if (!await this.prepareIndexBuild(indexName, this.schemaName || "public")) return;
3783
+ await this.timedIndexBuild(indexName, sql);
3752
3784
  snapshot?.indexes.add(indexName);
3753
3785
  }
3786
+ /**
3787
+ * Decides whether `indexName` still needs building. Returns false when a valid
3788
+ * index already exists, or when another session is currently building it.
3789
+ * An invalid index left by an interrupted `CREATE INDEX CONCURRENTLY` is
3790
+ * dropped (concurrently) so the caller rebuilds it — `IF NOT EXISTS` would
3791
+ * otherwise keep the broken index forever.
3792
+ */
3793
+ async prepareIndexBuild(indexName, schemaName) {
3794
+ const snapshot = this.schemaSnapshot;
3795
+ if (snapshot && !snapshot.invalidIndexes.has(indexName)) return true;
3796
+ const existing = await this.client.oneOrNone(`SELECT i.indisvalid AS is_valid,
3797
+ EXISTS (SELECT 1 FROM pg_stat_progress_create_index p WHERE p.index_relid = c.oid) AS building
3798
+ FROM pg_catalog.pg_index i
3799
+ JOIN pg_catalog.pg_class c ON c.oid = i.indexrelid
3800
+ JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
3801
+ WHERE c.relname = $1 AND n.nspname = $2`, [indexName, schemaName]);
3802
+ if (!existing) return true;
3803
+ if (existing.is_valid) return false;
3804
+ if (existing.building) {
3805
+ this.logger.warn(`Index ${indexName} is being built by another session; skipping`);
3806
+ return false;
3807
+ }
3808
+ this.logger.warn(`Dropping invalid index ${indexName} left by an interrupted build; rebuilding it`);
3809
+ const quotedIndexName = `"${(0, _mastra_core_utils.parseSqlIdentifier)(indexName, "index name")}"`;
3810
+ await this.client.none(`DROP INDEX CONCURRENTLY IF EXISTS ${getSchemaName$6(this.schemaName)}.${quotedIndexName}`);
3811
+ return true;
3812
+ }
3813
+ async timedIndexBuild(indexName, sql) {
3814
+ const startedAt = Date.now();
3815
+ this.logger.info(`Creating index ${indexName}`);
3816
+ await this.client.none(sql);
3817
+ this.logger.info(`Created index ${indexName} in ${Date.now() - startedAt}ms`);
3818
+ }
3754
3819
  async dropIndex(indexName) {
3755
3820
  try {
3756
3821
  const schemaName = this.schemaName || "public";
@@ -7085,6 +7150,10 @@ var ExperimentsPG = class ExperimentsPG extends _mastra_core_storage.Experiments
7085
7150
  "scorerIds"
7086
7151
  ]
7087
7152
  });
7153
+ await this.#db.dropNotNull({
7154
+ tableName: _mastra_core_storage.TABLE_EXPERIMENTS,
7155
+ columns: ["targetType", "targetId"]
7156
+ });
7088
7157
  await this.#db.alterTable({
7089
7158
  tableName: _mastra_core_storage.TABLE_EXPERIMENT_RESULTS,
7090
7159
  schema: _mastra_core_storage.EXPERIMENT_RESULTS_SCHEMA,
@@ -10495,6 +10564,7 @@ function dedupeMessagesForSave(messages) {
10495
10564
  var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
10496
10565
  supportsPartialThreadUpdate = true;
10497
10566
  supportsObservationalMemory = true;
10567
+ supportsAtomicWorkingMemoryMerge = true;
10498
10568
  supportsObservationalMemoryHistorySearch = true;
10499
10569
  /**
10500
10570
  * Retention-eligible tables. `threads`, `messages`, and `resources` all anchor
@@ -11841,6 +11911,57 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
11841
11911
  metadata: metadataJson ? JSON.parse(metadataJson) : resource.metadata
11842
11912
  };
11843
11913
  }
11914
+ async mergeResourceWorkingMemory({ resourceId, merge }) {
11915
+ const tableName = getTableName$3({
11916
+ indexName: _mastra_core_storage.TABLE_RESOURCES,
11917
+ schemaName: getSchemaName$3(this.#schema)
11918
+ });
11919
+ const callbackError = {};
11920
+ try {
11921
+ return await this.#db.client.tx(async (t) => {
11922
+ const now = (/* @__PURE__ */ new Date()).toISOString();
11923
+ await t.none(`INSERT INTO ${tableName} (id, metadata, "createdAt", "createdAtZ", "updatedAt", "updatedAtZ")
11924
+ VALUES ($1, $2, $3, $4, $5, $6) ON CONFLICT (id) DO NOTHING`, [
11925
+ resourceId,
11926
+ toPgJson({}),
11927
+ now,
11928
+ now,
11929
+ now,
11930
+ now
11931
+ ]);
11932
+ await t.one(`SELECT id FROM ${tableName} WHERE id = $1 FOR UPDATE`, [resourceId]);
11933
+ const existing = await this.#getResourceById(t, resourceId);
11934
+ let workingMemory;
11935
+ try {
11936
+ workingMemory = merge(existing.workingMemory ?? void 0);
11937
+ } catch (error) {
11938
+ callbackError.error = error;
11939
+ throw error;
11940
+ }
11941
+ const updatedAt = /* @__PURE__ */ new Date();
11942
+ const updatedAtStr = updatedAt.toISOString();
11943
+ await t.none(`UPDATE ${tableName} SET "workingMemory" = $1, "updatedAt" = $2, "updatedAtZ" = $3 WHERE id = $4`, [
11944
+ workingMemory,
11945
+ updatedAtStr,
11946
+ updatedAtStr,
11947
+ resourceId
11948
+ ]);
11949
+ return {
11950
+ ...existing,
11951
+ workingMemory,
11952
+ updatedAt
11953
+ };
11954
+ });
11955
+ } catch (error) {
11956
+ if (error === callbackError.error || error instanceof _mastra_core_error.MastraError) throw error;
11957
+ throw new _mastra_core_error.MastraError({
11958
+ id: (0, _mastra_core_storage.createStorageErrorId)("PG", "MERGE_RESOURCE_WORKING_MEMORY", "FAILED"),
11959
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
11960
+ category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
11961
+ details: { resourceId }
11962
+ }, error);
11963
+ }
11964
+ }
11844
11965
  async updateResource({ resourceId, workingMemory, metadata }) {
11845
11966
  const existingResource = await this.#getResourceById(this.#db.client, resourceId);
11846
11967
  if (!existingResource) {
@@ -23208,12 +23329,17 @@ function workflowSnapshotStatusIndexName(schemaName) {
23208
23329
  /**
23209
23330
  * Expression index on `(workflow_name, snapshot->>'status', "createdAt" DESC)` so
23210
23331
  * listWorkflowRuns() status filters can use an index instead of scanning every snapshot.
23332
+ *
23333
+ * `concurrently` is set by init, which may build the index on a large existing table while
23334
+ * other processes keep writing to it. The exported DDL stays plain because migration
23335
+ * tooling often wraps statements in a transaction, where CONCURRENTLY is rejected.
23211
23336
  */
23212
- function workflowSnapshotStatusIndexSQL(indexName, schemaName) {
23213
- return `CREATE INDEX IF NOT EXISTS "${indexName}" ON ${getTableName({
23337
+ function workflowSnapshotStatusIndexSQL(indexName, schemaName, concurrently = false) {
23338
+ const tableName = getTableName({
23214
23339
  indexName: _mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT,
23215
23340
  schemaName: getSchemaName(schemaName)
23216
- })} (workflow_name, (snapshot ->> 'status'), "createdAt" DESC)`;
23341
+ });
23342
+ return `CREATE INDEX ${concurrently ? "CONCURRENTLY " : ""}IF NOT EXISTS "${indexName}" ON ${tableName} (workflow_name, (snapshot ->> 'status'), "createdAt" DESC)`;
23217
23343
  }
23218
23344
  /** Base name (before any schema prefix) of the expression index backing the threadId filter. */
23219
23345
  const WORKFLOW_SNAPSHOT_THREAD_ID_INDEX = "mastra_workflow_snapshot_threadid_idx";
@@ -23250,12 +23376,14 @@ const WORKFLOW_SNAPSHOT_THREAD_ID_EXPR = `COALESCE(jsonb_path_query_first(snapsh
23250
23376
  /**
23251
23377
  * Expression index on the snapshot-embedded thread id so listWorkflowRuns() threadId filters
23252
23378
  * (Agent.listSuspendedRuns) can use an index instead of detoasting every snapshot.
23379
+ * See {@link workflowSnapshotStatusIndexSQL} for `concurrently`.
23253
23380
  */
23254
- function workflowSnapshotThreadIdIndexSQL(indexName, schemaName) {
23255
- return `CREATE INDEX IF NOT EXISTS "${indexName}" ON ${getTableName({
23381
+ function workflowSnapshotThreadIdIndexSQL(indexName, schemaName, concurrently = false) {
23382
+ const tableName = getTableName({
23256
23383
  indexName: _mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT,
23257
23384
  schemaName: getSchemaName(schemaName)
23258
- })} ((${WORKFLOW_SNAPSHOT_THREAD_ID_EXPR}))`;
23385
+ });
23386
+ return `CREATE INDEX ${concurrently ? "CONCURRENTLY " : ""}IF NOT EXISTS "${indexName}" ON ${tableName} ((${WORKFLOW_SNAPSHOT_THREAD_ID_EXPR}))`;
23259
23387
  }
23260
23388
  var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorage {
23261
23389
  #db;
@@ -23352,13 +23480,13 @@ var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorag
23352
23480
  if (await this.#db.getColumnType(_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT, "snapshot") !== "jsonb") return;
23353
23481
  const indexName = workflowSnapshotStatusIndexName(this.#schema);
23354
23482
  try {
23355
- await this.#db.createIndexFromStatement(indexName, workflowSnapshotStatusIndexSQL(indexName, this.#schema));
23483
+ await this.#db.createIndexFromStatement(indexName, workflowSnapshotStatusIndexSQL(indexName, this.#schema, true));
23356
23484
  } catch (error) {
23357
23485
  this.logger?.warn?.(`Failed to create index ${indexName}:`, error);
23358
23486
  }
23359
23487
  const threadIdIndexName = workflowSnapshotThreadIdIndexName(this.#schema);
23360
23488
  try {
23361
- await this.#db.createIndexFromStatement(threadIdIndexName, workflowSnapshotThreadIdIndexSQL(threadIdIndexName, this.#schema));
23489
+ await this.#db.createIndexFromStatement(threadIdIndexName, workflowSnapshotThreadIdIndexSQL(threadIdIndexName, this.#schema, true));
23362
23490
  } catch (error) {
23363
23491
  this.logger?.warn?.(`Failed to create index ${threadIdIndexName}:`, error);
23364
23492
  }
@@ -23429,7 +23557,7 @@ var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorag
23429
23557
  async dangerouslyClearAll() {
23430
23558
  await this.#db.clearTable({ tableName: _mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT });
23431
23559
  }
23432
- async updateWorkflowResults({ workflowName, runId, stepId, result, requestContext }) {
23560
+ async updateWorkflowResults({ workflowName, runId, stepId, result, requestContext, state }) {
23433
23561
  try {
23434
23562
  return await this.#db.client.tx(async (t) => {
23435
23563
  const tableName = getTableName({
@@ -23460,7 +23588,8 @@ var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorag
23460
23588
  snapshot,
23461
23589
  stepId,
23462
23590
  result,
23463
- requestContext
23591
+ requestContext,
23592
+ state
23464
23593
  });
23465
23594
  const now = /* @__PURE__ */ new Date();
23466
23595
  const sanitizedSnapshot = toPgJson(snapshot);