@mastra/pg 1.33.0-alpha.1 → 1.33.0-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/dist/_types/@internal_core/dist/knowledge-compat/index.d.ts +0 -2
  2. package/dist/_types/@internal_core/dist/memory/index.d.ts +0 -2
  3. package/dist/docs/SKILL.md +1 -1
  4. package/dist/docs/assets/SOURCE_MAP.json +1 -1
  5. package/dist/docs/references/docs-memory-semantic-recall.md +5 -1
  6. package/dist/docs/references/integrations-databases-dynamodb.md +4 -4
  7. package/dist/docs/references/integrations-databases-postgresql.md +4 -4
  8. package/dist/docs/references/reference-memory-memory-class.md +2 -2
  9. package/dist/docs/references/reference-rag-chunking-and-embedding.md +1 -1
  10. package/dist/docs/references/reference-rag-retrieval.md +11 -0
  11. package/dist/docs/references/reference-tools-vector-query-tool.md +1 -0
  12. package/dist/docs/references/reference-vectors-pg.md +1 -1
  13. package/dist/index.cjs +238 -227
  14. package/dist/index.cjs.map +1 -1
  15. package/dist/index.js +237 -225
  16. package/dist/index.js.map +1 -1
  17. package/dist/shared/pool-error-guard.d.ts +23 -0
  18. package/dist/shared/pool-error-guard.d.ts.map +1 -0
  19. package/dist/shared/test-fixtures/terminate-backend.d.ts +2 -0
  20. package/dist/shared/test-fixtures/terminate-backend.d.ts.map +1 -0
  21. package/dist/storage/db/index.d.ts +14 -0
  22. package/dist/storage/db/index.d.ts.map +1 -1
  23. package/dist/storage/domains/agents/index.d.ts.map +1 -1
  24. package/dist/storage/domains/memory/index.d.ts.map +1 -1
  25. package/dist/storage/domains/observability/index.d.ts +2 -1
  26. package/dist/storage/domains/observability/index.d.ts.map +1 -1
  27. package/dist/storage/domains/observability/v-next/ddl.d.ts +2 -0
  28. package/dist/storage/domains/observability/v-next/ddl.d.ts.map +1 -1
  29. package/dist/storage/domains/observability/v-next/index.d.ts.map +1 -1
  30. package/dist/storage/domains/observability/v-next/thread-query.d.ts +0 -2
  31. package/dist/storage/domains/observability/v-next/thread-query.d.ts.map +1 -1
  32. package/dist/storage/domains/workflows/index.d.ts +9 -3
  33. package/dist/storage/domains/workflows/index.d.ts.map +1 -1
  34. package/dist/storage/factory-storage.d.ts +1 -22
  35. package/dist/storage/factory-storage.d.ts.map +1 -1
  36. package/dist/storage/index.d.ts.map +1 -1
  37. package/dist/vector/index.d.ts.map +1 -1
  38. package/package.json +9 -9
package/dist/index.js CHANGED
@@ -16,7 +16,7 @@ import { MessageList } from "@mastra/core/agent";
16
16
  import { saveScorePayloadSchema } from "@mastra/core/evals";
17
17
  import { skillSnapshotFieldValuesEqual } from "@mastra/core/storage/domains/skills";
18
18
  import { isDeepStrictEqual } from "util";
19
- //#region src/shared/config.ts
19
+ // src/shared/config.ts
20
20
  /**
21
21
  * Type guard for pre-configured pg.Pool config
22
22
  */
@@ -63,8 +63,7 @@ const validateConfig = (name, config) => {
63
63
  ]) if (!config[key] || typeof config[key] !== "string" || config[key].trim() === "") throw new Error(`${name}: ${key} must be provided and cannot be empty. Passing an empty string may cause fallback to local Postgres defaults.`);
64
64
  } else throw new Error(`${name}: invalid config. Provide either {pool}, {connectionString}, {host,port,database,user,password}, or a pg ClientConfig (e.g., Cloud SQL connector with \`stream\`).`);
65
65
  };
66
- //#endregion
67
- //#region src/shared/pool-config.ts
66
+ // src/shared/pool-config.ts
68
67
  /**
69
68
  * Builds the `pg.Pool` options for a connection-string based config.
70
69
  *
@@ -93,8 +92,40 @@ function buildConnectionStringPoolConfig(config, defaults) {
93
92
  idleTimeoutMillis: config.idleTimeoutMillis ?? defaults.idleTimeoutMillis
94
93
  };
95
94
  }
96
- //#endregion
97
- //#region src/shared/schema-name.ts
95
+ // src/shared/pool-error-guard.ts
96
+ /**
97
+ * A pool-level `error` listener only covers *idle* clients: pg hands ownership
98
+ * of a client to the borrower for the duration of a checkout and takes its own
99
+ * listener off. So a backend restart or network blip that lands on a client
100
+ * mid-transaction reaches an emitter with no listener, and Node escalates that
101
+ * to an uncaughtException that kills the process — the pool handler logs the
102
+ * idle siblings and the borrowed one still brings everything down.
103
+ *
104
+ * Attach a listener once per physical connection instead. `connect` fires when
105
+ * the pool establishes a client, so the client stays covered for its whole life
106
+ * and there is nothing to remove on release.
107
+ *
108
+ * That listener outlives the checkout, so it also sees the failures the pool is
109
+ * already reporting through its own idle listener. Follow `acquire`/`release`
110
+ * to tell the two apart and stay quiet while the client is idle — otherwise one
111
+ * dropped connection is announced twice, the second time as the wrong thing.
112
+ *
113
+ * The pool still discards the failed connection; this only keeps the failure
114
+ * reportable instead of fatal.
115
+ */
116
+ function guardCheckedOutClientErrors(pool, warn = console.warn) {
117
+ const borrowed = /* @__PURE__ */ new WeakSet();
118
+ pool.on("connect", (client) => {
119
+ borrowed.add(client);
120
+ client.on("error", (err) => {
121
+ if (!borrowed.has(client)) return;
122
+ warn(`PostgreSQL: client error while checked out (the pool discards this connection): ${err instanceof Error ? err.message : String(err)}`);
123
+ });
124
+ });
125
+ pool.on("acquire", (client) => borrowed.add(client));
126
+ pool.on("release", (_err, client) => borrowed.delete(client));
127
+ }
128
+ // src/shared/schema-name.ts
98
129
  const POSTGRES_IDENTIFIER_MAX_BYTES = 63;
99
130
  const UNSAFE_SCHEMA_NAME_CHARS = /["'\\$\u0000-\u001f\u007f]/;
100
131
  /**
@@ -121,8 +152,7 @@ function schemaNamePrefix(name) {
121
152
  const sanitized = parseSchemaName(name).replace(/[^A-Za-z0-9_]/g, "_");
122
153
  return /^[0-9]/.test(sanitized) ? `_${sanitized}` : sanitized;
123
154
  }
124
- //#endregion
125
- //#region src/vector/filter.ts
155
+ // src/vector/filter.ts
126
156
  /**
127
157
  * Translates MongoDB-style filters to PG compatible filters.
128
158
  *
@@ -178,8 +208,7 @@ var PGFilterTranslator = class extends BaseFilterTranslator {
178
208
  return { $regex: flags ? `(?${flags})${pattern}` : pattern };
179
209
  }
180
210
  };
181
- //#endregion
182
- //#region src/vector/sql-builder.ts
211
+ // src/vector/sql-builder.ts
183
212
  const getTextExtractExpr = (key) => {
184
213
  const jsonPathKey = parseJsonPathKey(key);
185
214
  if (!key.includes(".")) return `metadata->>'${jsonPathKey}'`;
@@ -557,8 +586,7 @@ function buildFilterQuery(filter, minScore, topK) {
557
586
  values
558
587
  };
559
588
  }
560
- //#endregion
561
- //#region src/vector/index.ts
589
+ // src/vector/index.ts
562
590
  const DEFAULT_NAMESPACE = "default";
563
591
  const MAX_UPSERT_ROWS_PER_STATEMENT = Math.floor(65535 / 4);
564
592
  var PgVector = class extends MastraVector {
@@ -625,6 +653,7 @@ var PgVector = class extends MastraVector {
625
653
  };
626
654
  else throw new Error("PgVector: invalid configuration provided");
627
655
  this.pool = new pg$1.Pool(poolConfig);
656
+ guardCheckedOutClientErrors(this.pool, (message) => this.logger?.warn?.(message));
628
657
  this.pool.on("error", (err) => {
629
658
  this.logger?.warn?.("PgVector: idle pool client error (pool discards the client and reconnects on next checkout)", { err: err instanceof Error ? err.message : err });
630
659
  });
@@ -2023,8 +2052,7 @@ var PgVector = class extends MastraVector {
2023
2052
  }
2024
2053
  }
2025
2054
  };
2026
- //#endregion
2027
- //#region src/storage/client.ts
2055
+ // src/storage/client.ts
2028
2056
  /**
2029
2057
  * Truncate a query string for error messages.
2030
2058
  */
@@ -2406,8 +2434,7 @@ function buildConstraintName({ baseName, schemaName, maxLength = 63, hashWhenTru
2406
2434
  }
2407
2435
  return truncateIdentifier(fullName, maxLength);
2408
2436
  }
2409
- //#endregion
2410
- //#region src/storage/db/pg-errors.ts
2437
+ // src/storage/db/pg-errors.ts
2411
2438
  function asPgError(error) {
2412
2439
  return error ?? {};
2413
2440
  }
@@ -2435,8 +2462,7 @@ function isDuplicateSchemaError(error) {
2435
2462
  if (code === "23505" && constraint === "pg_namespace_nspname_index") return true;
2436
2463
  return /schema .* already exists/i.test(message);
2437
2464
  }
2438
- //#endregion
2439
- //#region src/storage/db/sanitize-json.ts
2465
+ // src/storage/db/sanitize-json.ts
2440
2466
  const repair = (value) => value.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, "�").replaceAll("\0", "");
2441
2467
  function repairJson(value) {
2442
2468
  if (typeof value === "string") return repair(value);
@@ -2462,8 +2488,7 @@ function toPgJson(value) {
2462
2488
  if (json === void 0 || !/\\u(?:0000|[dD][89a-fA-F][0-9a-fA-F]{2})/.test(json)) return json;
2463
2489
  return JSON.stringify(repairJson(JSON.parse(json)));
2464
2490
  }
2465
- //#endregion
2466
- //#region src/storage/db/schema-snapshot.ts
2491
+ // src/storage/db/schema-snapshot.ts
2467
2492
  /**
2468
2493
  * Returns the snapshot currently installed on `client`, but only when it was
2469
2494
  * taken from the schema the caller operates on. A store configured for another
@@ -2545,8 +2570,7 @@ async function loadSchemaSnapshot(client, schemaName) {
2545
2570
  invalidIndexes
2546
2571
  };
2547
2572
  }
2548
- //#endregion
2549
- //#region src/storage/db/index.ts
2573
+ // src/storage/db/index.ts
2550
2574
  const POSTGRES_MAX_BIND_PARAMETERS$1 = 65535;
2551
2575
  /**
2552
2576
  * Resolves PgDomainConfig to a database client and schema.
@@ -2589,6 +2613,7 @@ function resolvePgConfig(config) {
2589
2613
  pool.on("error", (err) => {
2590
2614
  console.warn(`resolvePgConfig: idle pool client error (pool discards the client and reconnects on next checkout): ${err instanceof Error ? err.message : String(err)}`);
2591
2615
  });
2616
+ guardCheckedOutClientErrors(pool);
2592
2617
  const client = new PoolAdapter(pool);
2593
2618
  return {
2594
2619
  client,
@@ -3971,6 +3996,42 @@ MIGRATION REQUIRED: Duplicate spans detected in ${duplicateInfo.tableName}\n====
3971
3996
  }, error);
3972
3997
  }
3973
3998
  }
3999
+ /**
4000
+ * Runs one UPDATE per record in a transaction, changing a row only while every
4001
+ * column in `nullColumns` is still NULL. Never inserts. Returns, in input order,
4002
+ * whether each update changed a row.
4003
+ */
4004
+ async batchUpdateWhereNull({ tableName, updates, nullColumns }) {
4005
+ const nullConditions = nullColumns.map((column) => ` AND "${parseSqlIdentifier(column, "column name")}" IS NULL`).join("");
4006
+ try {
4007
+ return await this.client.tx(async (tx) => {
4008
+ const applied = [];
4009
+ for (const update of updates) {
4010
+ const query = await this.buildUpdateQuery({
4011
+ tableName,
4012
+ ...update
4013
+ });
4014
+ if (!query) {
4015
+ applied.push(false);
4016
+ continue;
4017
+ }
4018
+ const rows = await tx.any(`${query.sql}${nullConditions} RETURNING 1`, query.values);
4019
+ applied.push(rows.length > 0);
4020
+ }
4021
+ return applied;
4022
+ });
4023
+ } catch (error) {
4024
+ throw new MastraError({
4025
+ id: createStorageErrorId("PG", "BATCH_UPDATE_WHERE_NULL", "FAILED"),
4026
+ domain: ErrorDomain.STORAGE,
4027
+ category: ErrorCategory.THIRD_PARTY,
4028
+ details: {
4029
+ tableName,
4030
+ numberOfRecords: updates.length
4031
+ }
4032
+ }, error);
4033
+ }
4034
+ }
3974
4035
  async batchDelete({ tableName, keys }) {
3975
4036
  try {
3976
4037
  if (keys.length === 0) return;
@@ -4091,9 +4152,14 @@ MIGRATION REQUIRED: Duplicate spans detected in ${duplicateInfo.tableName}\n====
4091
4152
  columns: [column]
4092
4153
  });
4093
4154
  }
4094
- async executeUpdate(client, { tableName, keys, data }) {
4155
+ async executeUpdate(client, args) {
4156
+ const query = await this.buildUpdateQuery(args);
4157
+ if (!query) return;
4158
+ await client.none(query.sql, query.values);
4159
+ }
4160
+ async buildUpdateQuery({ tableName, keys, data }) {
4095
4161
  const filteredData = await this.filterRecordToKnownColumns(tableName, data);
4096
- if (Object.keys(filteredData).length === 0) return;
4162
+ if (Object.keys(filteredData).length === 0) return null;
4097
4163
  const setColumns = [];
4098
4164
  const setValues = [];
4099
4165
  let paramIndex = 1;
@@ -4109,16 +4175,16 @@ MIGRATION REQUIRED: Duplicate spans detected in ${duplicateInfo.tableName}\n====
4109
4175
  whereConditions.push(`"${parsedKey}" = $${paramIndex++}`);
4110
4176
  whereValues.push(this.prepareValue(value, key, tableName));
4111
4177
  });
4112
- const sql = `UPDATE ${getTableName$6({
4113
- indexName: tableName,
4114
- schemaName: getSchemaName$6(this.schemaName)
4115
- })} SET ${setColumns.join(", ")} WHERE ${whereConditions.join(" AND ")}`;
4116
- const values = [...setValues, ...whereValues];
4117
- await client.none(sql, values);
4178
+ return {
4179
+ sql: `UPDATE ${getTableName$6({
4180
+ indexName: tableName,
4181
+ schemaName: getSchemaName$6(this.schemaName)
4182
+ })} SET ${setColumns.join(", ")} WHERE ${whereConditions.join(" AND ")}`,
4183
+ values: [...setValues, ...whereValues]
4184
+ };
4118
4185
  }
4119
4186
  };
4120
- //#endregion
4121
- //#region src/storage/domains/utils.ts
4187
+ // src/storage/domains/utils.ts
4122
4188
  function getSchemaName$5(schema) {
4123
4189
  return schema ? `"${parseSchemaName(schema)}"` : void 0;
4124
4190
  }
@@ -4199,8 +4265,7 @@ function tenancyWhere(filters, startIndex) {
4199
4265
  nextIndex: idx
4200
4266
  };
4201
4267
  }
4202
- //#endregion
4203
- //#region src/storage/domains/agents/index.ts
4268
+ // src/storage/domains/agents/index.ts
4204
4269
  var AgentsPG = class AgentsPG extends AgentsStorage {
4205
4270
  #db;
4206
4271
  #schema;
@@ -5047,8 +5112,7 @@ var AgentsPG = class AgentsPG extends AgentsStorage {
5047
5112
  };
5048
5113
  }
5049
5114
  };
5050
- //#endregion
5051
- //#region src/storage/retention.ts
5115
+ // src/storage/retention.ts
5052
5116
  function cutoffFor(policy, anchorType, now = Date.now()) {
5053
5117
  const cutoffMs = retentionCutoffMs(policy, now);
5054
5118
  return anchorType === "epoch-ms" ? cutoffMs : new Date(cutoffMs);
@@ -5075,8 +5139,7 @@ function resolveTargets({ policies, descriptor, order }) {
5075
5139
  order
5076
5140
  });
5077
5141
  }
5078
- //#endregion
5079
- //#region src/storage/domains/background-tasks/index.ts
5142
+ // src/storage/domains/background-tasks/index.ts
5080
5143
  function getSchemaName$4(schema) {
5081
5144
  return schema ? `"${schema}"` : "\"public\"";
5082
5145
  }
@@ -5490,8 +5553,7 @@ var BackgroundTasksPG = class BackgroundTasksPG extends BackgroundTasksStorage {
5490
5553
  return Number(result?.count ?? 0);
5491
5554
  }
5492
5555
  };
5493
- //#endregion
5494
- //#region src/storage/domains/blobs/index.ts
5556
+ // src/storage/domains/blobs/index.ts
5495
5557
  var BlobsPG = class extends BlobStore {
5496
5558
  #db;
5497
5559
  #schema;
@@ -5593,8 +5655,7 @@ var BlobsPG = class extends BlobStore {
5593
5655
  };
5594
5656
  }
5595
5657
  };
5596
- //#endregion
5597
- //#region src/storage/domains/channels/index.ts
5658
+ // src/storage/domains/channels/index.ts
5598
5659
  var ChannelsPG = class ChannelsPG extends ChannelsStorage {
5599
5660
  #db;
5600
5661
  #schema;
@@ -5808,8 +5869,7 @@ var ChannelsPG = class ChannelsPG extends ChannelsStorage {
5808
5869
  };
5809
5870
  }
5810
5871
  };
5811
- //#endregion
5812
- //#region src/storage/domains/datasets/index.ts
5872
+ // src/storage/domains/datasets/index.ts
5813
5873
  /** Serialize a value for a jsonb column. Returns null for null/undefined. */
5814
5874
  function jsonbArg(value) {
5815
5875
  return value === void 0 || value === null ? null : toPgJson(value);
@@ -7080,8 +7140,7 @@ var DatasetsPG = class DatasetsPG extends DatasetsStorage {
7080
7140
  await this.#db.clearTable({ tableName: TABLE_DATASETS });
7081
7141
  }
7082
7142
  };
7083
- //#endregion
7084
- //#region src/storage/domains/experiments/index.ts
7143
+ // src/storage/domains/experiments/index.ts
7085
7144
  const DEFAULT_PRUNE_BATCH_SIZE = 1e3;
7086
7145
  var ExperimentsPG = class ExperimentsPG extends ExperimentsStorage {
7087
7146
  #db;
@@ -8027,8 +8086,7 @@ var ExperimentsPG = class ExperimentsPG extends ExperimentsStorage {
8027
8086
  await this.#db.clearTable({ tableName: TABLE_EXPERIMENTS });
8028
8087
  }
8029
8088
  };
8030
- //#endregion
8031
- //#region src/storage/domains/favorites/index.ts
8089
+ // src/storage/domains/favorites/index.ts
8032
8090
  /**
8033
8091
  * Maps a favorite entity type to its parent entity table.
8034
8092
  */
@@ -8300,8 +8358,7 @@ var FavoritesPG = class FavoritesPG extends FavoritesStorage {
8300
8358
  }
8301
8359
  }
8302
8360
  };
8303
- //#endregion
8304
- //#region ../../packages/_internal-core/dist/knowledge-compat/index.js
8361
+ // ../../packages/_internal-core/dist/knowledge-compat/index.js
8305
8362
  const KNOWLEDGE_V2_CORE_FEATURE = "knowledge-v2";
8306
8363
  /** Hard cap on scope nodes returned by one `listScopeNodes` read. */
8307
8364
  const MAX_KNOWLEDGE_SCOPE_NODES = 1e3;
@@ -8935,8 +8992,7 @@ function createKnowledgeV2CoreLoader(features, loadStorageModule) {
8935
8992
  return knowledgeV2Core;
8936
8993
  };
8937
8994
  }
8938
- //#endregion
8939
- //#region src/storage/domains/knowledge/index.ts
8995
+ // src/storage/domains/knowledge/index.ts
8940
8996
  const loadKnowledgeV2Core = createKnowledgeV2CoreLoader(coreFeatures, () => import("@mastra/core/storage"));
8941
8997
  async function assertKnowledgeDescriptionWithinBoundCompat(description) {
8942
8998
  const { assertKnowledgeDescriptionWithinBound } = await loadKnowledgeV2Core();
@@ -10460,8 +10516,7 @@ var KnowledgePG = class extends KnowledgeStorage {
10460
10516
  });
10461
10517
  }
10462
10518
  };
10463
- //#endregion
10464
- //#region src/storage/domains/mcp-clients/index.ts
10519
+ // src/storage/domains/mcp-clients/index.ts
10465
10520
  const SNAPSHOT_FIELDS$5 = [
10466
10521
  "name",
10467
10522
  "description",
@@ -11022,8 +11077,7 @@ var MCPClientsPG = class MCPClientsPG extends MCPClientsStorage {
11022
11077
  };
11023
11078
  }
11024
11079
  };
11025
- //#endregion
11026
- //#region src/storage/domains/mcp-servers/index.ts
11080
+ // src/storage/domains/mcp-servers/index.ts
11027
11081
  const SNAPSHOT_FIELDS$4 = [
11028
11082
  "name",
11029
11083
  "version",
@@ -11605,8 +11659,7 @@ var MCPServersPG = class MCPServersPG extends MCPServersStorage {
11605
11659
  };
11606
11660
  }
11607
11661
  };
11608
- //#endregion
11609
- //#region ../../packages/_internal-core/dist/memory/index.js
11662
+ // ../../packages/_internal-core/dist/memory/index.js
11610
11663
  /**
11611
11664
  * Shared rules for observational memory lifecycle writes. Every storage adapter applies
11612
11665
  * these inside its own atomic section (lock, transaction, or conditional update), so the
@@ -11691,8 +11744,7 @@ function isAppendOnlySince(storedObservations, snapshotObservations) {
11691
11744
  const snapshot = snapshotObservations ?? "";
11692
11745
  return stored === snapshot || stored.startsWith(snapshot);
11693
11746
  }
11694
- //#endregion
11695
- //#region src/storage/db/run-fencing.ts
11747
+ // src/storage/db/run-fencing.ts
11696
11748
  /**
11697
11749
  * Epoch milliseconds on the database clock. `clock_timestamp()` rather than
11698
11750
  * `now()`: a statement can wait on a row lock, and lease math must use the
@@ -11724,8 +11776,7 @@ function withRunFence(client, claimsTable, fence, operation, write) {
11724
11776
  return write(t);
11725
11777
  });
11726
11778
  }
11727
- //#endregion
11728
- //#region src/storage/domains/memory/index.ts
11779
+ // src/storage/domains/memory/index.ts
11729
11780
  /**
11730
11781
  * Local constant for the observational memory table name.
11731
11782
  * Defined locally to avoid a static import that crashes on older @mastra/core
@@ -12006,7 +12057,7 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
12006
12057
  indexName: OM_TABLE,
12007
12058
  schemaName: quotedSchemaName
12008
12059
  });
12009
- const idxPrefix = schemaPrefix ? `${schemaPrefix}` : "";
12060
+ const idxPrefix = schemaPrefix ? schemaPrefix : "";
12010
12061
  statements.push(`CREATE INDEX IF NOT EXISTS "${idxPrefix}idx_om_lookup_key" ON ${fullOmTableName} ("lookupKey");`);
12011
12062
  }
12012
12063
  for (const idx of MemoryPG.getDefaultIndexDefs(schemaPrefix)) statements.push(generateIndexSQL(idx, schemaName));
@@ -14557,8 +14608,7 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
14557
14608
  }
14558
14609
  }
14559
14610
  };
14560
- //#endregion
14561
- //#region src/storage/domains/notifications/index.ts
14611
+ // src/storage/domains/notifications/index.ts
14562
14612
  const COMPOSITE_PRIMARY_KEY$1 = TABLE_CONFIGS[TABLE_NOTIFICATIONS].compositePrimaryKey;
14563
14613
  const statusTimestamp = (status, now) => {
14564
14614
  if (status === "delivered") return { deliveredAt: now };
@@ -15061,8 +15111,7 @@ var NotificationsPG = class NotificationsPG extends NotificationsStorage {
15061
15111
  return row ? rowToNotification(row) : void 0;
15062
15112
  }
15063
15113
  };
15064
- //#endregion
15065
- //#region src/storage/domains/observability/index.ts
15114
+ // src/storage/domains/observability/index.ts
15066
15115
  var ObservabilityPG = class ObservabilityPG extends ObservabilityStorage {
15067
15116
  /**
15068
15117
  * Retention-eligible tables. The observability domain has a single physical
@@ -15682,30 +15731,33 @@ var ObservabilityPG = class ObservabilityPG extends ObservabilityStorage {
15682
15731
  }, error);
15683
15732
  }
15684
15733
  }
15734
+ #toSpanUpdates(records) {
15735
+ return records.map((record) => {
15736
+ const data = { ...record.updates };
15737
+ if (data.endedAt instanceof Date) {
15738
+ const endedAt = data.endedAt.toISOString();
15739
+ data.endedAt = endedAt;
15740
+ data.endedAtZ = endedAt;
15741
+ }
15742
+ if (data.startedAt instanceof Date) {
15743
+ const startedAt = data.startedAt.toISOString();
15744
+ data.startedAt = startedAt;
15745
+ data.startedAtZ = startedAt;
15746
+ }
15747
+ return {
15748
+ keys: {
15749
+ spanId: record.spanId,
15750
+ traceId: record.traceId
15751
+ },
15752
+ data
15753
+ };
15754
+ });
15755
+ }
15685
15756
  async batchUpdateSpans(args) {
15686
15757
  try {
15687
15758
  return this.#db.batchUpdate({
15688
15759
  tableName: TABLE_SPANS,
15689
- updates: args.records.map((record) => {
15690
- const data = { ...record.updates };
15691
- if (data.endedAt instanceof Date) {
15692
- const endedAt = data.endedAt.toISOString();
15693
- data.endedAt = endedAt;
15694
- data.endedAtZ = endedAt;
15695
- }
15696
- if (data.startedAt instanceof Date) {
15697
- const startedAt = data.startedAt.toISOString();
15698
- data.startedAt = startedAt;
15699
- data.startedAtZ = startedAt;
15700
- }
15701
- return {
15702
- keys: {
15703
- spanId: record.spanId,
15704
- traceId: record.traceId
15705
- },
15706
- data
15707
- };
15708
- })
15760
+ updates: this.#toSpanUpdates(args.records)
15709
15761
  });
15710
15762
  } catch (error) {
15711
15763
  throw new MastraError({
@@ -15715,6 +15767,25 @@ var ObservabilityPG = class ObservabilityPG extends ObservabilityStorage {
15715
15767
  }, error);
15716
15768
  }
15717
15769
  }
15770
+ async batchUpdateUnendedSpans(args) {
15771
+ try {
15772
+ const applied = await this.#db.batchUpdateWhereNull({
15773
+ tableName: TABLE_SPANS,
15774
+ updates: this.#toSpanUpdates(args.records),
15775
+ nullColumns: ["endedAt"]
15776
+ });
15777
+ return { updated: args.records.filter((_, index) => applied[index]).map(({ traceId, spanId }) => ({
15778
+ traceId,
15779
+ spanId
15780
+ })) };
15781
+ } catch (error) {
15782
+ throw new MastraError({
15783
+ id: createStorageErrorId("PG", "BATCH_UPDATE_UNENDED_SPANS", "FAILED"),
15784
+ domain: ErrorDomain.STORAGE,
15785
+ category: ErrorCategory.USER
15786
+ }, error);
15787
+ }
15788
+ }
15718
15789
  async batchDeleteTraces(args) {
15719
15790
  this.assertUnscopedBatchDeleteTraces(args);
15720
15791
  try {
@@ -15733,8 +15804,7 @@ var ObservabilityPG = class ObservabilityPG extends ObservabilityStorage {
15733
15804
  }
15734
15805
  }
15735
15806
  };
15736
- //#endregion
15737
- //#region src/storage/domains/observability/v-next/signal-schema.ts
15807
+ // src/storage/domains/observability/v-next/signal-schema.ts
15738
15808
  const CURSOR_ID_COLUMN = {
15739
15809
  name: "cursorId",
15740
15810
  type: "bigserial"
@@ -16385,8 +16455,7 @@ const TEXT_ARRAY_COLUMNS = new Set(columnNamesByType(ALL_EVENT_COLUMNS, "text[]"
16385
16455
  const METRIC_TYPED_COLUMNS = typedColumnNames(METRIC_EVENT_COLUMNS);
16386
16456
  const SCORE_TYPED_COLUMNS = typedColumnNames(SCORE_EVENT_COLUMNS);
16387
16457
  const FEEDBACK_TYPED_COLUMNS = typedColumnNames(FEEDBACK_EVENT_COLUMNS);
16388
- //#endregion
16389
- //#region src/storage/domains/observability/v-next/ddl.ts
16458
+ // src/storage/domains/observability/v-next/ddl.ts
16390
16459
  /**
16391
16460
  * Raw DDL for Postgres v-next observability tables.
16392
16461
  *
@@ -16783,8 +16852,7 @@ function additiveColumns(schema) {
16783
16852
  function allIndexDDL(schema) {
16784
16853
  return tableIndexes().map((spec) => indexDDL(schema, spec));
16785
16854
  }
16786
- //#endregion
16787
- //#region src/storage/domains/observability/v-next/discovery.ts
16855
+ // src/storage/domains/observability/v-next/discovery.ts
16788
16856
  const DEFAULT_TTL_SECONDS = 300;
16789
16857
  const DEFAULT_LOOKBACK_SECONDS = 2592e3;
16790
16858
  /** All signal tables that contain a column. Used by cross-signal discovery. */
@@ -17019,8 +17087,7 @@ async function getMetricLabelValues(client, schema, args, config) {
17019
17087
  if (args.limit) filtered = filtered.slice(0, args.limit);
17020
17088
  return { values: filtered };
17021
17089
  }
17022
- //#endregion
17023
- //#region src/storage/domains/observability/v-next/filters.ts
17090
+ // src/storage/domains/observability/v-next/filters.ts
17024
17091
  function newFilterAccumulator(startIndex = 1) {
17025
17092
  return {
17026
17093
  conditions: [],
@@ -17095,8 +17162,7 @@ function applySingleOrArrayFilter(acc, column, value, prefix = "") {
17095
17162
  function whereOrEmpty(acc) {
17096
17163
  return acc.conditions.length ? `WHERE ${acc.conditions.join(" AND ")}` : "";
17097
17164
  }
17098
- //#endregion
17099
- //#region src/storage/domains/observability/v-next/preview-compat.ts
17165
+ // src/storage/domains/observability/v-next/preview-compat.ts
17100
17166
  const generator = coreStorage.buildStoredPreview;
17101
17167
  const canGeneratePreviews = typeof generator === "function";
17102
17168
  /** SQL detection only; preview formatting stays in TypeScript. Empty strings are intentional. */
@@ -17153,8 +17219,7 @@ function buildSpanQueryResponse(...args) {
17153
17219
  }
17154
17220
  return response;
17155
17221
  }
17156
- //#endregion
17157
- //#region src/storage/domains/observability/v-next/review-status.ts
17222
+ // src/storage/domains/observability/v-next/review-status.ts
17158
17223
  const FEEDBACK_REVIEW_STATUSES = ["needs-review", "reviewed"];
17159
17224
  function isFeedbackReviewStatus(value) {
17160
17225
  return FEEDBACK_REVIEW_STATUSES.some((status) => status === value);
@@ -17178,8 +17243,7 @@ function parseUpdateFeedbackReviewStatusArgs(args) {
17178
17243
  reviewStatus: args.reviewStatus
17179
17244
  };
17180
17245
  }
17181
- //#endregion
17182
- //#region src/storage/domains/observability/v-next/sql.ts
17246
+ // src/storage/domains/observability/v-next/sql.ts
17183
17247
  /**
17184
17248
  * SQL helpers for the v-next Postgres observability domain.
17185
17249
  *
@@ -17309,8 +17373,7 @@ const SCORE_SELECT_COLUMNS = `
17309
17373
  ${buildSelectColumns(SCORE_EVENT_COLUMNS)}`;
17310
17374
  const FEEDBACK_SELECT_COLUMNS = `
17311
17375
  ${buildSelectColumns(FEEDBACK_EVENT_COLUMNS)}`;
17312
- //#endregion
17313
- //#region src/storage/domains/observability/v-next/helpers.ts
17376
+ // src/storage/domains/observability/v-next/helpers.ts
17314
17377
  const PROMOTED_KEYS = /* @__PURE__ */ new Set([
17315
17378
  "experimentId",
17316
17379
  "entityType",
@@ -17747,8 +17810,7 @@ function rowToFeedbackRecord(row) {
17747
17810
  scope: parsedJson(row.scope) ?? void 0
17748
17811
  };
17749
17812
  }
17750
- //#endregion
17751
- //#region src/storage/domains/observability/v-next/polling.ts
17813
+ // src/storage/domains/observability/v-next/polling.ts
17752
17814
  /**
17753
17815
  * Delta-polling helpers for the v-next Postgres observability domain.
17754
17816
  *
@@ -17817,8 +17879,7 @@ function decodeDeltaCursor(cursor) {
17817
17879
  async function readSafeXactHorizon(client) {
17818
17880
  return validatePgInteger((await client.one(`SELECT pg_snapshot_xmin(pg_current_snapshot())::text AS "xactId"`)).xactId);
17819
17881
  }
17820
- //#endregion
17821
- //#region src/storage/domains/observability/v-next/listing.ts
17882
+ // src/storage/domains/observability/v-next/listing.ts
17822
17883
  function responseItems(key, items) {
17823
17884
  return { [key]: items };
17824
17885
  }
@@ -17905,8 +17966,7 @@ async function listSignalDelta(config) {
17905
17966
  deltaCursor
17906
17967
  };
17907
17968
  }
17908
- //#endregion
17909
- //#region src/storage/domains/observability/v-next/olap.ts
17969
+ // src/storage/domains/observability/v-next/olap.ts
17910
17970
  /**
17911
17971
  * Shared OLAP helpers for the v-next Postgres observability domain.
17912
17972
  *
@@ -18110,8 +18170,7 @@ const COMPLEX_GROUP_BY_EXCLUDED = /* @__PURE__ */ new Set([
18110
18170
  "tags",
18111
18171
  "requestContext"
18112
18172
  ]);
18113
- //#endregion
18114
- //#region src/storage/domains/observability/v-next/feedback.ts
18173
+ // src/storage/domains/observability/v-next/feedback.ts
18115
18174
  /**
18116
18175
  * Feedback operations for the v-next Postgres observability domain.
18117
18176
  *
@@ -18349,8 +18408,7 @@ async function getFeedbackPercentiles(client, schema, args) {
18349
18408
  `;
18350
18409
  return { series: percentileSeriesFromRows(await client.manyOrNone(sql, acc.params), args.percentiles) };
18351
18410
  }
18352
- //#endregion
18353
- //#region src/storage/domains/observability/v-next/logs.ts
18411
+ // src/storage/domains/observability/v-next/logs.ts
18354
18412
  /**
18355
18413
  * Log operations for the v-next Postgres observability domain.
18356
18414
  */
@@ -18402,8 +18460,7 @@ async function listLogsDelta(client, table, filters, after, limit) {
18402
18460
  mapRow: rowToLogRecord
18403
18461
  });
18404
18462
  }
18405
- //#endregion
18406
- //#region src/storage/domains/observability/v-next/metrics.ts
18463
+ // src/storage/domains/observability/v-next/metrics.ts
18407
18464
  /**
18408
18465
  * Metric operations for the v-next Postgres observability domain.
18409
18466
  *
@@ -18640,8 +18697,7 @@ async function getMetricPercentiles(client, schema, args) {
18640
18697
  `;
18641
18698
  return { series: percentileSeriesFromRows(await client.manyOrNone(sql, acc.params), args.percentiles) };
18642
18699
  }
18643
- //#endregion
18644
- //#region src/storage/domains/observability/v-next/partitioning.ts
18700
+ // src/storage/domains/observability/v-next/partitioning.ts
18645
18701
  const DEFAULT_FUTURE_DAYS = 14;
18646
18702
  async function detectTimescale(client) {
18647
18703
  const row = await client.oneOrNone(`SELECT EXISTS (SELECT 1 FROM pg_extension WHERE extname = 'timescaledb') AS "exists"`);
@@ -18800,8 +18856,7 @@ async function setupPartitioning(client, schema, options = {}) {
18800
18856
  return "native";
18801
18857
  }
18802
18858
  }
18803
- //#endregion
18804
- //#region src/storage/domains/observability/v-next/retention.ts
18859
+ // src/storage/domains/observability/v-next/retention.ts
18805
18860
  /**
18806
18861
  * Age-based retention for the v-next Postgres observability domain.
18807
18862
  *
@@ -18921,8 +18976,7 @@ async function pruneTimescaleTable({ client, schema, table, cutoff, options }) {
18921
18976
  done: true
18922
18977
  };
18923
18978
  }
18924
- //#endregion
18925
- //#region src/storage/domains/observability/v-next/scores.ts
18979
+ // src/storage/domains/observability/v-next/scores.ts
18926
18980
  /**
18927
18981
  * Score operations for the v-next Postgres observability domain.
18928
18982
  *
@@ -19201,8 +19255,7 @@ async function getScorePercentiles(client, schema, args) {
19201
19255
  `;
19202
19256
  return { series: percentileSeriesFromRows(await client.manyOrNone(sql, acc.params), args.percentiles) };
19203
19257
  }
19204
- //#endregion
19205
- //#region src/storage/domains/observability/v-next/trace-query.ts
19258
+ // src/storage/domains/observability/v-next/trace-query.ts
19206
19259
  const TRACE_STATUS_SQL = `CASE WHEN r."error" IS NOT NULL THEN 'error' ELSE 'success' END`;
19207
19260
  /** Core versions without the field reject it in strict query results, so it is only returned when core knows it. */
19208
19261
  const timeToFirstChunkSupported = "timeToFirstChunkMs" in coreStorage.TRACE_QUERY_FIELD_REGISTRY.trace;
@@ -20194,8 +20247,7 @@ function compileSpanQueryPredicate(predicate, offset) {
20194
20247
  function compileThreadQueryPredicate(predicate, offset) {
20195
20248
  return compileScalarPredicate(predicate, Object.fromEntries(Object.keys(coreStorage.THREAD_QUERY_FIELD_REGISTRY).map((field) => [field, `s."${field}"`])), offset);
20196
20249
  }
20197
- //#endregion
20198
- //#region src/storage/domains/observability/v-next/span-query.ts
20250
+ // src/storage/domains/observability/v-next/span-query.ts
20199
20251
  const identityFields = [
20200
20252
  "organizationId",
20201
20253
  "resourceId",
@@ -20408,8 +20460,7 @@ async function querySpans(client, schema, plan, timeoutMs) {
20408
20460
  return buildSpanQueryResponse(plan, hydrated, payloads, metrics);
20409
20461
  }, { repeatableRead: true });
20410
20462
  }
20411
- //#endregion
20412
- //#region src/storage/domains/observability/v-next/thread-query.ts
20463
+ // src/storage/domains/observability/v-next/thread-query.ts
20413
20464
  const contextFields = [
20414
20465
  "resourceId",
20415
20466
  "userId",
@@ -20573,7 +20624,7 @@ async function queryThreadRows(client, schema, plan, timeoutMs, strategy = "auto
20573
20624
  return `"${field}" ${field === "organizationId" || field === "resourceId" ? "IS NOT DISTINCT FROM" : "="} $${values.length}`;
20574
20625
  }).join(" AND ")})`;
20575
20626
  }));
20576
- const payloads = await run(`SELECT "organizationId", "traceId", "cursorId", "spanType", ${contextFields.map((field) => `"${field}"`).join(", ")},
20627
+ const payloads = await run(`SELECT "organizationId", "traceId", "cursorId", ${contextFields.map((field) => `"${field}"`).join(", ")},
20577
20628
  CASE WHEN "spanType" = 'agent_run' THEN "timeToFirstChunkMs" END AS "timeToFirstChunkMs",
20578
20629
  CASE WHEN error IS NULL THEN 'success' ELSE 'error' END AS status,
20579
20630
  ${[
@@ -20588,30 +20639,10 @@ async function queryThreadRows(client, schema, plan, timeoutMs, strategy = "auto
20588
20639
  String(version)
20589
20640
  ]);
20590
20641
  const byRoot = new Map(payloads.map((row) => [key(row.organizationId, row.traceId, row.cursorId), row]));
20591
- const agents = page.filter((row) => byRoot.get(key(row.organizationId, row.firstTraceId, row.firstCursorId))?.spanType === "agent_run");
20592
- const models = /* @__PURE__ */ new Map();
20593
- if (agents.length) {
20594
- const modelValues = [];
20595
- const rows = await run(`SELECT DISTINCT ON ("organizationId", "traceId") "organizationId", "traceId",
20596
- CASE WHEN jsonb_typeof(attributes -> 'model') = 'string' THEN attributes ->> 'model' END AS model,
20597
- CASE WHEN jsonb_typeof(attributes -> 'provider') = 'string' THEN attributes ->> 'provider' END AS provider
20598
- FROM ${table} WHERE "spanType" = 'model_generation' AND NOT "isPending" AND (${agents.map((row) => `(${Object.entries({
20599
- organizationId: row.organizationId,
20600
- resourceId: row.resourceId,
20601
- traceId: row.firstTraceId,
20602
- parentSpanId: row.firstSpanId
20603
- }).map(([field, value]) => {
20604
- modelValues.push(value);
20605
- return `"${field}" ${field === "organizationId" || field === "resourceId" ? "IS NOT DISTINCT FROM" : "="} $${modelValues.length}`;
20606
- }).join(" AND ")})`).join(" OR ")})
20607
- ORDER BY "organizationId", "traceId", "startedAt", "spanId" COLLATE "C", "cursorId" DESC`, modelValues);
20608
- for (const row of rows) models.set(JSON.stringify([row.organizationId, row.traceId]), row);
20609
- }
20610
20642
  const threads = page.flatMap((row) => {
20611
20643
  const first = byRoot.get(key(row.organizationId, row.firstTraceId, row.firstCursorId));
20612
20644
  const last = byRoot.get(key(row.organizationId, row.lastTraceId, row.lastCursorId));
20613
20645
  if (!first || !last) return [];
20614
- const model = models.get(JSON.stringify([row.organizationId, row.firstTraceId]));
20615
20646
  return [coreStorage.threadQueryRowSchema.parse({
20616
20647
  organizationId: row.organizationId,
20617
20648
  threadId: row.threadId,
@@ -20623,8 +20654,6 @@ async function queryThreadRows(client, schema, plan, timeoutMs, strategy = "auto
20623
20654
  endedAt: row.endedAt.toISOString(),
20624
20655
  traceCount: Number(row.traceCount),
20625
20656
  timeToFirstChunkMs: first.timeToFirstChunkMs,
20626
- model: model?.model ?? null,
20627
- provider: model?.provider ?? null,
20628
20657
  inputPreview: readPreview(first.inputPreview, first.input),
20629
20658
  outputPreview: readPreview(last.outputPreview, last.output),
20630
20659
  errorPreview: readPreview(last.errorPreview, last.error)
@@ -20640,8 +20669,7 @@ async function queryThreadRows(client, schema, plan, timeoutMs, strategy = "auto
20640
20669
  }) : null }
20641
20670
  };
20642
20671
  }
20643
- //#endregion
20644
- //#region src/storage/domains/observability/v-next/trace-aggregate.ts
20672
+ // src/storage/domains/observability/v-next/trace-aggregate.ts
20645
20673
  /** Trace-root column expressions (alias `r`) for each groupable dimension; same values `where` sees. */
20646
20674
  const DIMENSION_SQL = {
20647
20675
  entityType: "r.\"entityType\"",
@@ -20967,8 +20995,7 @@ async function runAggregate(client, plan, compiled, timeoutMs) {
20967
20995
  truncated
20968
20996
  };
20969
20997
  }
20970
- //#endregion
20971
- //#region src/storage/domains/observability/v-next/traces.ts
20998
+ // src/storage/domains/observability/v-next/traces.ts
20972
20999
  /**
20973
21000
  * Trace / branch list reads for the v-next Postgres observability domain.
20974
21001
  *
@@ -21582,8 +21609,7 @@ async function listBranchesDelta(client, span, filters, after, limit) {
21582
21609
  async function readBranchesStreamHeadCursor(client, span, filters) {
21583
21610
  return encodeDeltaCursor(await readSafeXactHorizon(client), 0);
21584
21611
  }
21585
- //#endregion
21586
- //#region src/storage/domains/observability/v-next/tracing.ts
21612
+ // src/storage/domains/observability/v-next/tracing.ts
21587
21613
  async function writeSpanRows(client, schema, rows) {
21588
21614
  const deduped = dedupeSpanRows(rows);
21589
21615
  const insert = buildInsert(schema, TABLE_SPAN_EVENTS, deduped, spanConflictClause(Object.keys(deduped[0] ?? {})));
@@ -21712,8 +21738,7 @@ async function dangerouslyClearTracing(client, schema) {
21712
21738
  const span = qualifiedTable(schema, TABLE_SPAN_EVENTS);
21713
21739
  await client.none(`TRUNCATE TABLE ${span} RESTART IDENTITY`);
21714
21740
  }
21715
- //#endregion
21716
- //#region src/storage/domains/observability/v-next/index.ts
21741
+ // src/storage/domains/observability/v-next/index.ts
21717
21742
  /**
21718
21743
  * Postgres v-next observability storage domain.
21719
21744
  *
@@ -21801,7 +21826,7 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
21801
21826
  else if (await detectPartman(this.#client)) mode = "partman";
21802
21827
  else mode = "native";
21803
21828
  const ddlMode = mode === "timescale" ? "timescale" : "partitioned";
21804
- try {
21829
+ if (!await this.#client.oneOrNone("SELECT 1 FROM information_schema.schemata WHERE schema_name = $1", [this.#schema])) try {
21805
21830
  await this.#client.none(schemaDDL(this.#schema));
21806
21831
  } catch (error) {
21807
21832
  if (!isDuplicateSchemaError(error)) throw error;
@@ -22152,8 +22177,7 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
22152
22177
  }
22153
22178
  }
22154
22179
  };
22155
- //#endregion
22156
- //#region src/storage/domains/prompt-blocks/index.ts
22180
+ // src/storage/domains/prompt-blocks/index.ts
22157
22181
  const SNAPSHOT_FIELDS$3 = [
22158
22182
  "name",
22159
22183
  "description",
@@ -22730,8 +22754,7 @@ var PromptBlocksPG = class PromptBlocksPG extends PromptBlocksStorage {
22730
22754
  };
22731
22755
  }
22732
22756
  };
22733
- //#endregion
22734
- //#region src/storage/domains/schedules/index.ts
22757
+ // src/storage/domains/schedules/index.ts
22735
22758
  function getSchemaName$2(schema) {
22736
22759
  return schema ? `"${schema}"` : "\"public\"";
22737
22760
  }
@@ -23099,8 +23122,7 @@ var SchedulesPG = class SchedulesPG extends SchedulesStorage {
23099
23122
  ${limitClause}`, params)).map(rowToTrigger);
23100
23123
  }
23101
23124
  };
23102
- //#endregion
23103
- //#region src/storage/domains/scorer-definitions/index.ts
23125
+ // src/storage/domains/scorer-definitions/index.ts
23104
23126
  const SNAPSHOT_FIELDS$2 = [
23105
23127
  "name",
23106
23128
  "description",
@@ -23700,8 +23722,7 @@ var ScorerDefinitionsPG = class ScorerDefinitionsPG extends ScorerDefinitionsSto
23700
23722
  };
23701
23723
  }
23702
23724
  };
23703
- //#endregion
23704
- //#region src/storage/domains/scores/index.ts
23725
+ // src/storage/domains/scores/index.ts
23705
23726
  /**
23706
23727
  * Appends multi-tenant scope conditions to a query. Mutates `conditions`/`queryParams`.
23707
23728
  * Returns the next positional parameter index.
@@ -24163,8 +24184,7 @@ var ScoresPG = class ScoresPG extends ScoresStorage {
24163
24184
  }
24164
24185
  }
24165
24186
  };
24166
- //#endregion
24167
- //#region src/storage/domains/skills/index.ts
24187
+ // src/storage/domains/skills/index.ts
24168
24188
  const SNAPSHOT_FIELDS$1 = [
24169
24189
  "name",
24170
24190
  "description",
@@ -24860,8 +24880,7 @@ var SkillsPG = class SkillsPG extends SkillsStorage {
24860
24880
  };
24861
24881
  }
24862
24882
  };
24863
- //#endregion
24864
- //#region src/storage/domains/thread-state/index.ts
24883
+ // src/storage/domains/thread-state/index.ts
24865
24884
  const COMPOSITE_PRIMARY_KEY = ["threadId", "type"];
24866
24885
  /**
24867
24886
  * PostgreSQL implementation of {@link ThreadStateStorage}.
@@ -25026,8 +25045,7 @@ var ThreadStatePG = class ThreadStatePG extends ThreadStateStorage {
25026
25045
  }
25027
25046
  }
25028
25047
  };
25029
- //#endregion
25030
- //#region src/storage/domains/tool-provider-connections/index.ts
25048
+ // src/storage/domains/tool-provider-connections/index.ts
25031
25049
  function normaliseScope(raw) {
25032
25050
  const value = raw == null ? "per-author" : String(raw);
25033
25051
  if (value === "shared") return "shared";
@@ -25282,8 +25300,7 @@ var ToolProviderConnectionsPG = class ToolProviderConnectionsPG extends ToolProv
25282
25300
  }
25283
25301
  }
25284
25302
  };
25285
- //#endregion
25286
- //#region src/storage/domains/workflow-definitions/index.ts
25303
+ // src/storage/domains/workflow-definitions/index.ts
25287
25304
  function rowToDefinition(row) {
25288
25305
  const inputSchema = parseJsonResilient(row.inputSchema);
25289
25306
  const outputSchema = parseJsonResilient(row.outputSchema);
@@ -25483,8 +25500,7 @@ var WorkflowDefinitionsPG = class WorkflowDefinitionsPG extends WorkflowDefiniti
25483
25500
  await this.#db.client.none(`DELETE FROM ${tableName} WHERE "id" = $1`, [id]);
25484
25501
  }
25485
25502
  };
25486
- //#endregion
25487
- //#region src/storage/domains/workflows/index.ts
25503
+ // src/storage/domains/workflows/index.ts
25488
25504
  function getSchemaName(schema) {
25489
25505
  return schema ? `"${schema}"` : "\"public\"";
25490
25506
  }
@@ -25621,6 +25637,13 @@ var WorkflowsPG = class WorkflowsPG extends WorkflowsStorage {
25621
25637
  supportsConcurrentUpdates() {
25622
25638
  return true;
25623
25639
  }
25640
+ supportsGuardedSnapshotWrites() {
25641
+ return true;
25642
+ }
25643
+ /** SQL for a snapshot's status. Legacy json/text columns can hold escapes that fail a jsonb cast (#11563). */
25644
+ async #statusExpression(column) {
25645
+ return await this.#db.getColumnType(TABLE_WORKFLOW_SNAPSHOT, "snapshot") === "jsonb" ? `${column} ->> 'status'` : `regexp_replace(${column}::text, '\\\\u(0000|[Dd][89A-Fa-f][0-9A-Fa-f]{2})', '', 'g')::jsonb ->> 'status'`;
25646
+ }
25624
25647
  parseWorkflowRun(row) {
25625
25648
  let parsedSnapshot = row.snapshot;
25626
25649
  if (typeof parsedSnapshot === "string") try {
@@ -26016,20 +26039,22 @@ var WorkflowsPG = class WorkflowsPG extends WorkflowsStorage {
26016
26039
  }, error);
26017
26040
  }
26018
26041
  }
26019
- async persistWorkflowSnapshot({ workflowName, runId, resourceId, snapshot, createdAt, updatedAt, fence }) {
26042
+ async persistWorkflowSnapshot({ workflowName, runId, resourceId, snapshot, createdAt, updatedAt, fence, expectedStatus }) {
26020
26043
  try {
26021
26044
  const now = /* @__PURE__ */ new Date();
26022
26045
  const createdAtValue = createdAt ? createdAt : now;
26023
26046
  const updatedAtValue = updatedAt ? updatedAt : now;
26024
26047
  const sanitizedSnapshot = toPgJson(snapshot);
26025
- await withRunFence(this.#db.client, this.#runOwnersTable(), resolveRunFence(this, fence, runId), "persistWorkflowSnapshot", (q) => q.none(`INSERT INTO ${getTableName({
26048
+ const guard = expectedStatus === void 0 ? "" : ` WHERE ${await this.#statusExpression("t.snapshot")} = ANY($9::text[])`;
26049
+ return await withRunFence(this.#db.client, this.#runOwnersTable(), resolveRunFence(this, fence, runId), "persistWorkflowSnapshot", (q) => q.oneOrNone(`INSERT INTO ${getTableName({
26026
26050
  indexName: TABLE_WORKFLOW_SNAPSHOT,
26027
26051
  schemaName: getSchemaName(this.#schema)
26028
26052
  })} AS t
26029
26053
  (workflow_name, run_id, "resourceId", snapshot, "createdAt", "updatedAt", "createdAtZ", "updatedAtZ")
26030
26054
  VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
26031
26055
  ON CONFLICT (workflow_name, run_id) DO UPDATE
26032
- SET "resourceId" = COALESCE($3, t."resourceId"), snapshot = $4, "updatedAt" = $6, "updatedAtZ" = $8`, [
26056
+ SET "resourceId" = COALESCE($3, t."resourceId"), snapshot = $4, "updatedAt" = $6, "updatedAtZ" = $8${guard}
26057
+ RETURNING 1 AS written`, [
26033
26058
  workflowName,
26034
26059
  runId,
26035
26060
  resourceId,
@@ -26037,8 +26062,9 @@ var WorkflowsPG = class WorkflowsPG extends WorkflowsStorage {
26037
26062
  createdAtValue,
26038
26063
  updatedAtValue,
26039
26064
  createdAtValue,
26040
- updatedAtValue
26041
- ]));
26065
+ updatedAtValue,
26066
+ ...expectedStatus === void 0 ? [] : [Array.isArray(expectedStatus) ? expectedStatus : [expectedStatus]]
26067
+ ])) !== null;
26042
26068
  } catch (error) {
26043
26069
  if (isRunFenceConflictError(error)) throw error;
26044
26070
  throw new MastraError({
@@ -26048,6 +26074,25 @@ var WorkflowsPG = class WorkflowsPG extends WorkflowsStorage {
26048
26074
  }, error);
26049
26075
  }
26050
26076
  }
26077
+ async loadWorkflowRunStatus({ workflowName, runId }) {
26078
+ try {
26079
+ const statusExpr = await this.#statusExpression("snapshot");
26080
+ return (await this.#db.client.oneOrNone(`SELECT ${statusExpr} AS status FROM ${getTableName({
26081
+ indexName: TABLE_WORKFLOW_SNAPSHOT,
26082
+ schemaName: getSchemaName(this.#schema)
26083
+ })} WHERE "workflow_name" = $1 AND "run_id" = $2 ORDER BY "createdAt" DESC LIMIT 1`, [workflowName, runId]))?.status ?? null;
26084
+ } catch (error) {
26085
+ throw new MastraError({
26086
+ id: createStorageErrorId("PG", "LOAD_WORKFLOW_RUN_STATUS", "FAILED"),
26087
+ domain: ErrorDomain.STORAGE,
26088
+ category: ErrorCategory.THIRD_PARTY,
26089
+ details: {
26090
+ workflowName,
26091
+ runId
26092
+ }
26093
+ }, error);
26094
+ }
26095
+ }
26051
26096
  async loadWorkflowSnapshot({ workflowName, runId }) {
26052
26097
  try {
26053
26098
  const result = await this.#db.load({
@@ -26213,8 +26258,7 @@ var WorkflowsPG = class WorkflowsPG extends WorkflowsStorage {
26213
26258
  }
26214
26259
  }
26215
26260
  };
26216
- //#endregion
26217
- //#region src/storage/domains/workspaces/index.ts
26261
+ // src/storage/domains/workspaces/index.ts
26218
26262
  const SNAPSHOT_FIELDS = [
26219
26263
  "name",
26220
26264
  "description",
@@ -26835,8 +26879,7 @@ var WorkspacesPG = class WorkspacesPG extends WorkspacesStorage {
26835
26879
  };
26836
26880
  }
26837
26881
  };
26838
- //#endregion
26839
- //#region src/storage/factory-storage.ts
26882
+ // src/storage/factory-storage.ts
26840
26883
  const IDENTIFIER_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
26841
26884
  const PG_UNIQUE_VIOLATION = "23505";
26842
26885
  const COLUMN_DDL = {
@@ -27128,38 +27171,6 @@ var PgFactoryStorageOps = class {
27128
27171
  * Supports serializable app-table transactions for cross-replica invariant
27129
27172
  * enforcement and `authDatabase()` exposing the shared pool.
27130
27173
  */
27131
- /**
27132
- * A pool-level `error` listener only covers *idle* clients: pg hands ownership
27133
- * of a client to the borrower for the duration of a checkout and takes its own
27134
- * listener off. So a backend restart or network blip that lands on a client
27135
- * mid-transaction reaches an emitter with no listener, and Node escalates that
27136
- * to an uncaughtException that kills the process — the pool handler logs the
27137
- * idle siblings and the borrowed one still brings everything down.
27138
- *
27139
- * Attach a listener once per physical connection instead. `connect` fires when
27140
- * the pool establishes a client, so the client stays covered for its whole life
27141
- * and there is nothing to remove on release.
27142
- *
27143
- * That listener outlives the checkout, so it also sees the failures the pool is
27144
- * already reporting through its own idle listener. Follow `acquire`/`release`
27145
- * to tell the two apart and stay quiet while the client is idle — otherwise one
27146
- * dropped connection is announced twice, the second time as the wrong thing.
27147
- *
27148
- * The pool still discards the failed connection; this only keeps the failure
27149
- * reportable instead of fatal.
27150
- */
27151
- function guardCheckedOutClientErrors(pool, warn = console.warn) {
27152
- const borrowed = /* @__PURE__ */ new WeakSet();
27153
- pool.on("connect", (client) => {
27154
- borrowed.add(client);
27155
- client.on("error", (err) => {
27156
- if (!borrowed.has(client)) return;
27157
- warn(`PgFactoryStorage: client error while checked out (the pool discards this connection): ${err instanceof Error ? err.message : String(err)}`);
27158
- });
27159
- });
27160
- pool.on("acquire", (client) => borrowed.add(client));
27161
- pool.on("release", (_err, client) => borrowed.delete(client));
27162
- }
27163
27174
  var PgFactoryStorage = class extends FactoryStorage {
27164
27175
  ops;
27165
27176
  #pool;
@@ -27285,8 +27296,7 @@ var PgFactoryStorage = class extends FactoryStorage {
27285
27296
  }
27286
27297
  }
27287
27298
  };
27288
- //#endregion
27289
- //#region src/storage/index.ts
27299
+ // src/storage/index.ts
27290
27300
  /** Default maximum number of connections in the pool */
27291
27301
  const DEFAULT_MAX_CONNECTIONS = 20;
27292
27302
  /** Default idle timeout in milliseconds */
@@ -27458,6 +27468,7 @@ var PostgresStore = class extends MastraCompositeStore {
27458
27468
  pool.on("error", (err) => {
27459
27469
  this.logger?.warn?.("PostgresStore: idle pool client error (pool discards the client and reconnects on next checkout)", { err: err instanceof Error ? err.message : err });
27460
27470
  });
27471
+ guardCheckedOutClientErrors(pool, (message) => this.logger?.warn?.(message));
27461
27472
  return pool;
27462
27473
  }
27463
27474
  #buildPool(config) {
@@ -27597,9 +27608,12 @@ var PostgresStoreVNext = class extends PostgresStore {
27597
27608
  const observabilityClient = built.client;
27598
27609
  this.#observabilityPool = built.pool;
27599
27610
  this.#ownsObservabilityPool = built.owned;
27600
- if (built.owned) built.pool.on("error", (err) => {
27601
- this.logger?.warn?.("PostgresStoreVNext: idle observability pool client error (pool discards the client and reconnects on next checkout)", { err: err instanceof Error ? err.message : err });
27602
- });
27611
+ if (built.owned) {
27612
+ guardCheckedOutClientErrors(built.pool, (message) => this.logger?.warn?.(message));
27613
+ built.pool.on("error", (err) => {
27614
+ this.logger?.warn?.("PostgresStoreVNext: idle observability pool client error (pool discards the client and reconnects on next checkout)", { err: err instanceof Error ? err.message : err });
27615
+ });
27616
+ }
27603
27617
  const observability = new ObservabilityStoragePostgresVNext({
27604
27618
  client: observabilityClient,
27605
27619
  schemaName: obsConfig.schemaName ?? config.schemaName,
@@ -27653,8 +27667,7 @@ var PostgresStoreVNext = class extends PostgresStore {
27653
27667
  }
27654
27668
  }
27655
27669
  };
27656
- //#endregion
27657
- //#region src/vector/prompt.ts
27670
+ // src/vector/prompt.ts
27658
27671
  /**
27659
27672
  * Vector store specific prompt that details supported operators and examples.
27660
27673
  * This prompt helps users construct valid filters for PG Vector.
@@ -27756,7 +27769,6 @@ Example Complex Query:
27756
27769
  ]}
27757
27770
  ]
27758
27771
  }`;
27759
- //#endregion
27760
27772
  export { AgentsPG, BackgroundTasksPG, BlobsPG, ChannelsPG, DatasetsPG, ExperimentsPG, FavoritesPG, KnowledgePG, MCPClientsPG, MCPServersPG, MemoryPG, NotificationsPG, ObservabilityPG, ObservabilityStoragePostgresVNext, PGVECTOR_PROMPT, PgFactoryStorage, PgVector, PoolAdapter, PostgresStore, PostgresStoreVNext, PromptBlocksPG, SchedulesPG, ScorerDefinitionsPG, ScoresPG, SkillsPG, ThreadStatePG, ToolProviderConnectionsPG, WorkflowDefinitionsPG, WorkflowsPG, WorkspacesPG, exportSchemas };
27761
27773
 
27762
27774
  //# sourceMappingURL=index.js.map