@mastra/pg 1.34.0-alpha.0 → 1.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +1 -1
  2. package/dist/docs/SKILL.md +48 -0
  3. package/dist/docs/assets/SOURCE_MAP.json +6 -0
  4. package/dist/docs/references/docs-deployment-workers.md +404 -0
  5. package/dist/docs/references/docs-memory-semantic-recall.md +485 -0
  6. package/dist/docs/references/docs-memory-working-memory.md +450 -0
  7. package/dist/docs/references/docs-storage.md +224 -0
  8. package/dist/docs/references/integrations-databases-clickhouse-managed-postgres.md +82 -0
  9. package/dist/docs/references/integrations-databases-dynamodb.md +286 -0
  10. package/dist/docs/references/integrations-databases-neon.md +222 -0
  11. package/dist/docs/references/integrations-databases-postgresql.md +592 -0
  12. package/dist/docs/references/integrations-vectors-pg.md +451 -0
  13. package/dist/docs/references/reference-memory-memory-class.md +237 -0
  14. package/dist/docs/references/reference-processors-message-history-processor.md +89 -0
  15. package/dist/docs/references/reference-processors-semantic-recall-processor.md +119 -0
  16. package/dist/docs/references/reference-processors-working-memory-processor.md +154 -0
  17. package/dist/docs/references/reference-rag-chunking-and-embedding.md +184 -0
  18. package/dist/docs/references/reference-rag-metadata-filters.md +238 -0
  19. package/dist/docs/references/reference-rag-overview.md +76 -0
  20. package/dist/docs/references/reference-rag-retrieval.md +672 -0
  21. package/dist/docs/references/reference-rag-vector-databases.md +808 -0
  22. package/dist/docs/references/reference-storage-composite.md +433 -0
  23. package/dist/docs/references/reference-storage-retention.md +310 -0
  24. package/dist/docs/references/reference-tools-vector-query-tool.md +500 -0
  25. package/dist/index.cjs +176 -71
  26. package/dist/index.cjs.map +1 -1
  27. package/dist/index.js +176 -71
  28. package/dist/index.js.map +1 -1
  29. package/dist/storage/domains/background-tasks/index.d.ts +1 -0
  30. package/dist/storage/domains/background-tasks/index.d.ts.map +1 -1
  31. package/dist/storage/domains/experiments/index.d.ts +3 -3
  32. package/dist/storage/domains/experiments/index.d.ts.map +1 -1
  33. package/dist/storage/domains/observability/v-next/index.d.ts +1 -1
  34. package/dist/storage/domains/observability/v-next/index.d.ts.map +1 -1
  35. package/dist/storage/domains/observability/v-next/preview-compat.d.ts +3 -0
  36. package/dist/storage/domains/observability/v-next/preview-compat.d.ts.map +1 -1
  37. package/dist/storage/domains/observability/v-next/span-query.d.ts +3 -0
  38. package/dist/storage/domains/observability/v-next/span-query.d.ts.map +1 -1
  39. package/dist/storage/domains/observability/v-next/trace-aggregate.d.ts.map +1 -1
  40. package/dist/storage/domains/observability/v-next/trace-query.d.ts +7 -0
  41. package/dist/storage/domains/observability/v-next/trace-query.d.ts.map +1 -1
  42. package/dist/storage/test-utils.d.ts.map +1 -1
  43. package/dist/vector/index.d.ts.map +1 -1
  44. package/package.json +6 -6
package/dist/index.cjs CHANGED
@@ -1103,42 +1103,43 @@ var PgVector = class extends _mastra_core_vector.MastraVector {
1103
1103
  const scoreExpr = ops.scoreExpr(distanceExpr);
1104
1104
  const filterClause = filterQuery.trim().replace(/^WHERE\s+/i, "");
1105
1105
  const hasFilter = filterClause.length > 0;
1106
- const useIndexedOrder = indexInfo.type === "hnsw" && !hasFilter && minScore <= 0;
1107
1106
  const namespaceParam = filterValues.length + 1;
1108
- const query = useIndexedOrder ? `
1107
+ const queryValues = [...filterValues, namespace];
1108
+ const selectColumns = `vector_id as id, ${scoreExpr} as score, metadata${includeVector ? ", embedding" : ""}`;
1109
+ const whereClause = `WHERE namespace = $${namespaceParam}${filterClause ? ` AND (${filterClause})` : ""}`;
1110
+ const useIndexedOrder = indexInfo.type === "hnsw" && (!hasFilter || this.supportsIterativeScan());
1111
+ let rows;
1112
+ if (useIndexedOrder) {
1113
+ const indexedQuery = `
1109
1114
  WITH vector_scores AS (
1110
- SELECT
1111
- vector_id as id,
1112
- ${scoreExpr} as score,
1113
- metadata
1114
- ${includeVector ? ", embedding" : ""}
1115
+ SELECT ${selectColumns}
1115
1116
  FROM ${tableName}
1116
- WHERE namespace = $${namespaceParam}
1117
+ ${whereClause}
1117
1118
  ORDER BY ${distanceExpr}
1118
1119
  LIMIT $2
1119
1120
  )
1120
- SELECT *
1121
+ SELECT *, score > $1 AS above_min_score
1121
1122
  FROM vector_scores
1122
- WHERE score > $1
1123
- ORDER BY score DESC` : `
1123
+ ORDER BY score DESC`;
1124
+ const candidates = (await client.query(indexedQuery, queryValues)).rows;
1125
+ if (candidates.length >= topK) rows = candidates.filter((row) => row.above_min_score);
1126
+ }
1127
+ if (!rows) {
1128
+ const exactQuery = `
1124
1129
  WITH vector_scores AS (
1125
- SELECT
1126
- vector_id as id,
1127
- ${scoreExpr} as score,
1128
- metadata
1129
- ${includeVector ? ", embedding" : ""}
1130
+ SELECT ${selectColumns}
1130
1131
  FROM ${tableName}
1131
- WHERE namespace = $${namespaceParam}
1132
- ${filterClause ? `AND (${filterClause})` : ""}
1132
+ ${whereClause}
1133
1133
  )
1134
1134
  SELECT *
1135
1135
  FROM vector_scores
1136
1136
  WHERE score > $1
1137
1137
  ORDER BY score DESC
1138
1138
  LIMIT $2`;
1139
- const result = await client.query(query, [...filterValues, namespace]);
1139
+ rows = (await client.query(exactQuery, queryValues)).rows;
1140
+ }
1140
1141
  await client.query("COMMIT");
1141
- return result.rows.map(({ id, score, metadata, embedding }) => ({
1142
+ return rows.map(({ id, score, metadata, embedding }) => ({
1142
1143
  id,
1143
1144
  score,
1144
1145
  metadata,
@@ -1405,7 +1406,7 @@ var PgVector = class extends _mastra_core_vector.MastraVector {
1405
1406
  indexConfig,
1406
1407
  vectorType
1407
1408
  }, client);
1408
- if (metadataIndexes?.length) await this.createMetadataIndexes(tableName, indexName, metadataIndexes);
1409
+ if (metadataIndexes?.length) await this.createMetadataIndexes(client, tableName, indexName, metadataIndexes);
1409
1410
  } catch (error) {
1410
1411
  this.createdIndexes.delete(indexName);
1411
1412
  this.namespaceReadyIndexes.delete(indexName);
@@ -1555,13 +1556,13 @@ var PgVector = class extends _mastra_core_vector.MastraVector {
1555
1556
  await client.query(indexSQL);
1556
1557
  });
1557
1558
  }
1558
- async createMetadataIndexes(tableName, indexName, metadataFields) {
1559
+ async createMetadataIndexes(client, tableName, indexName, metadataFields) {
1559
1560
  const hasher = await this.hasher;
1560
1561
  for (const field of metadataFields) {
1561
1562
  const fieldHash = hasher.h32(field).toString(16);
1562
1563
  const metadataIdxName = `"${indexName.slice(0, 55 - fieldHash.length)}_md_${fieldHash}_idx"`;
1563
1564
  const escapedField = field.replace(/'/g, "''");
1564
- await this.pool.query(`
1565
+ await client.query(`
1565
1566
  CREATE INDEX CONCURRENTLY IF NOT EXISTS ${metadataIdxName}
1566
1567
  ON ${tableName} ((metadata->>'${escapedField}'))
1567
1568
  `);
@@ -5285,6 +5286,7 @@ function rowToTask(row) {
5285
5286
  const suspendedAt = row.suspendedAtZ || row.suspendedAt;
5286
5287
  const completedAt = row.completedAtZ || row.completedAt;
5287
5288
  const leaseExpiresAt = row.leaseExpiresAtZ || row.leaseExpiresAt;
5289
+ const expiresAt = row.expiresAt != null ? row.expiresAtZ || row.expiresAt : void 0;
5288
5290
  return {
5289
5291
  id: row.id,
5290
5292
  status: row.status,
@@ -5306,10 +5308,13 @@ function rowToTask(row) {
5306
5308
  suspendedAt: suspendedAt ? suspendedAt instanceof Date ? suspendedAt : new Date(suspendedAt) : void 0,
5307
5309
  completedAt: completedAt ? completedAt instanceof Date ? completedAt : new Date(completedAt) : void 0,
5308
5310
  ownerId: row.ownerId ?? void 0,
5309
- leaseExpiresAt: leaseExpiresAt ? leaseExpiresAt instanceof Date ? leaseExpiresAt : new Date(leaseExpiresAt) : void 0
5311
+ leaseExpiresAt: leaseExpiresAt ? leaseExpiresAt instanceof Date ? leaseExpiresAt : new Date(leaseExpiresAt) : void 0,
5312
+ expiresAt: expiresAt ? expiresAt instanceof Date ? expiresAt : new Date(expiresAt) : void 0,
5313
+ metadata: parseJson$2(row.metadata)
5310
5314
  };
5311
5315
  }
5312
5316
  var BackgroundTasksPG = class BackgroundTasksPG extends _mastra_core_storage.BackgroundTasksStorage {
5317
+ supportsTaskExpiry = true;
5313
5318
  #db;
5314
5319
  #schema;
5315
5320
  #skipDefaultIndexes;
@@ -5350,7 +5355,9 @@ var BackgroundTasksPG = class BackgroundTasksPG extends _mastra_core_storage.Bac
5350
5355
  "suspend_payload",
5351
5356
  "suspendedAt",
5352
5357
  "ownerId",
5353
- "leaseExpiresAt"
5358
+ "leaseExpiresAt",
5359
+ "expiresAt",
5360
+ "metadata"
5354
5361
  ]
5355
5362
  });
5356
5363
  await this.createDefaultIndexes();
@@ -5484,7 +5491,10 @@ var BackgroundTasksPG = class BackgroundTasksPG extends _mastra_core_storage.Bac
5484
5491
  completedAtZ: task.completedAt?.toISOString() ?? null,
5485
5492
  ownerId: task.ownerId ?? null,
5486
5493
  leaseExpiresAt: task.leaseExpiresAt?.toISOString() ?? null,
5487
- leaseExpiresAtZ: task.leaseExpiresAt?.toISOString() ?? null
5494
+ leaseExpiresAtZ: task.leaseExpiresAt?.toISOString() ?? null,
5495
+ expiresAt: task.expiresAt?.toISOString() ?? null,
5496
+ expiresAtZ: task.expiresAt?.toISOString() ?? null,
5497
+ metadata: serializeJson(task.metadata)
5488
5498
  }
5489
5499
  });
5490
5500
  }
@@ -5540,6 +5550,16 @@ var BackgroundTasksPG = class BackgroundTasksPG extends _mastra_core_storage.Bac
5540
5550
  const val = update.leaseExpiresAt?.toISOString() ?? null;
5541
5551
  params.push(val, val);
5542
5552
  }
5553
+ if ("expiresAt" in update) {
5554
+ setClauses.push(`"expiresAt" = $${paramIdx++}`);
5555
+ setClauses.push(`"expiresAtZ" = $${paramIdx++}`);
5556
+ const val = update.expiresAt?.toISOString() ?? null;
5557
+ params.push(val, val);
5558
+ }
5559
+ if ("metadata" in update) {
5560
+ setClauses.push(`"metadata" = $${paramIdx++}`);
5561
+ params.push(serializeJson(update.metadata));
5562
+ }
5543
5563
  if (setClauses.length === 0) return false;
5544
5564
  const table = getTableName$4(getSchemaName$4(this.#schema));
5545
5565
  params.push(taskId);
@@ -5562,6 +5582,13 @@ var BackgroundTasksPG = class BackgroundTasksPG extends _mastra_core_storage.Bac
5562
5582
  params.push(options.expectedLeaseExpiresAt.toISOString());
5563
5583
  }
5564
5584
  }
5585
+ if (options?.expectedSuspendedAt !== void 0) {
5586
+ if (options.expectedSuspendedAt === null) where += ` AND "suspendedAtZ" IS NULL`;
5587
+ else {
5588
+ where += ` AND "suspendedAtZ" = $${paramIdx++}`;
5589
+ params.push(options.expectedSuspendedAt.toISOString());
5590
+ }
5591
+ }
5565
5592
  return ((await this.#db.client.query(`UPDATE ${table} SET ${setClauses.join(", ")} WHERE ${where}`, params)).rowCount ?? 0) > 0;
5566
5593
  }
5567
5594
  async getTask(taskId) {
@@ -5604,7 +5631,9 @@ var BackgroundTasksPG = class BackgroundTasksPG extends _mastra_core_storage.Bac
5604
5631
  conditions.push(`"tool_call_id" = $${paramIdx++}`);
5605
5632
  params.push(filter.toolCallId);
5606
5633
  }
5607
- const dateCol = filter.dateFilterBy === "startedAt" ? "\"startedAt\"" : filter.dateFilterBy === "suspendedAt" ? "\"suspendedAt\"" : filter.dateFilterBy === "completedAt" ? "\"completedAt\"" : "\"createdAt\"";
5634
+ const dateCol = filter.dateFilterBy === "startedAt" ? "\"startedAt\"" : filter.dateFilterBy === "suspendedAt" ? "\"suspendedAt\"" : filter.dateFilterBy === "completedAt" ? "\"completedAt\"" : filter.dateFilterBy === "expiresAt" ? "\"expiresAtZ\"" : "\"createdAt\"";
5635
+ if (filter.hasExpiresAt !== void 0) conditions.push(filter.hasExpiresAt ? "\"expiresAt\" IS NOT NULL" : "\"expiresAt\" IS NULL");
5636
+ if (filter.dateFilterBy === "expiresAt" && (filter.fromDate || filter.toDate)) conditions.push("\"expiresAt\" IS NOT NULL");
5608
5637
  if (filter.fromDate) {
5609
5638
  conditions.push(`${dateCol} >= $${paramIdx++}`);
5610
5639
  params.push(filter.fromDate.toISOString());
@@ -5645,7 +5674,9 @@ var BackgroundTasksPG = class BackgroundTasksPG extends _mastra_core_storage.Bac
5645
5674
  conditions.push(`"status" IN (${placeholders.join(", ")})`);
5646
5675
  params.push(...statuses);
5647
5676
  }
5648
- const dateCol = filter.dateFilterBy === "startedAt" ? "\"startedAt\"" : filter.dateFilterBy === "suspendedAt" ? "\"suspendedAt\"" : filter.dateFilterBy === "completedAt" ? "\"completedAt\"" : "\"createdAt\"";
5677
+ const dateCol = filter.dateFilterBy === "startedAt" ? "\"startedAt\"" : filter.dateFilterBy === "suspendedAt" ? "\"suspendedAt\"" : filter.dateFilterBy === "completedAt" ? "\"completedAt\"" : filter.dateFilterBy === "expiresAt" ? "\"expiresAtZ\"" : "\"createdAt\"";
5678
+ if (filter.hasExpiresAt !== void 0) conditions.push(filter.hasExpiresAt ? "\"expiresAt\" IS NOT NULL" : "\"expiresAt\" IS NULL");
5679
+ if (filter.dateFilterBy === "expiresAt" && (filter.fromDate || filter.toDate)) conditions.push("\"expiresAt\" IS NOT NULL");
5649
5680
  if (filter.fromDate) {
5650
5681
  conditions.push(`${dateCol} >= $${paramIdx++}`);
5651
5682
  params.push(filter.fromDate.toISOString());
@@ -7261,6 +7292,19 @@ var DatasetsPG = class DatasetsPG extends _mastra_core_storage.DatasetsStorage {
7261
7292
  };
7262
7293
  // src/storage/domains/experiments/index.ts
7263
7294
  const DEFAULT_PRUNE_BATCH_SIZE = 1e3;
7295
+ /**
7296
+ * Column list for reading experiment rows. Each `timestamp` column comes back
7297
+ * twice: as itself, and as its `*Z` mirror derived in the query with
7298
+ * `AT TIME ZONE 'UTC'`. A plain `timestamp` otherwise round-trips through the
7299
+ * session timezone, and reading the stored `*Z` column instead would return the
7300
+ * insert time for rows written before the mirrors were filled in. Deriving it
7301
+ * here makes legacy rows read correctly with no backfill to run at startup.
7302
+ */
7303
+ function selectWithMirrors(schema) {
7304
+ return Object.entries(schema).flatMap(([name, definition]) => definition.type === "timestamp" ? [`"${name}"`, `"${name}" AT TIME ZONE 'UTC' AS "${name}Z"`] : [`"${name}"`]).join(", ");
7305
+ }
7306
+ const EXPERIMENT_COLUMNS = selectWithMirrors(_mastra_core_storage.EXPERIMENTS_SCHEMA);
7307
+ const EXPERIMENT_RESULT_COLUMNS = selectWithMirrors(_mastra_core_storage.EXPERIMENT_RESULTS_SCHEMA);
7264
7308
  var ExperimentsPG = class ExperimentsPG extends _mastra_core_storage.ExperimentsStorage {
7265
7309
  #db;
7266
7310
  #schema;
@@ -7269,9 +7313,9 @@ var ExperimentsPG = class ExperimentsPG extends _mastra_core_storage.Experiments
7269
7313
  static MANAGED_TABLES = [_mastra_core_storage.TABLE_EXPERIMENTS, _mastra_core_storage.TABLE_EXPERIMENT_RESULTS];
7270
7314
  /**
7271
7315
  * Experiments prune as whole units: an aged experiment and its result rows go
7272
- * together, mirroring `deleteExperiment`. Anchored on `completedAt` (not the
7273
- * `completedAtZ` mirror, which carries a `DEFAULT NOW()` this domain never
7274
- * overrides — it holds insert time even for running rows). `completedAt` is
7316
+ * together, mirroring `deleteExperiment`. Anchored on `completedAt` rather
7317
+ * than its `completedAtZ` mirror: both are written together, but
7318
+ * `completedAt` is the column the retention index covers. `completedAt` is
7275
7319
  * written as a UTC ISO string and stays NULL while running, so
7276
7320
  * `completedAt < cutoff` is false for in-flight experiments.
7277
7321
  */
@@ -7591,7 +7635,11 @@ var ExperimentsPG = class ExperimentsPG extends _mastra_core_storage.Experiments
7591
7635
  startedAt: null,
7592
7636
  completedAt: null,
7593
7637
  createdAt: nowIso,
7594
- updatedAt: nowIso
7638
+ updatedAt: nowIso,
7639
+ startedAtZ: null,
7640
+ completedAtZ: null,
7641
+ createdAtZ: nowIso,
7642
+ updatedAtZ: nowIso
7595
7643
  }
7596
7644
  });
7597
7645
  return {
@@ -7680,12 +7728,14 @@ var ExperimentsPG = class ExperimentsPG extends _mastra_core_storage.Experiments
7680
7728
  values.push(input.skippedCount);
7681
7729
  }
7682
7730
  if (input.startedAt !== void 0) {
7683
- setClauses.push(`"startedAt" = $${paramIndex++}`);
7684
- values.push(input.startedAt?.toISOString() ?? null);
7731
+ const iso = input.startedAt?.toISOString() ?? null;
7732
+ setClauses.push(`"startedAt" = $${paramIndex++}`, `"startedAtZ" = $${paramIndex++}`);
7733
+ values.push(iso, iso);
7685
7734
  }
7686
7735
  if (input.completedAt !== void 0) {
7687
- setClauses.push(`"completedAt" = $${paramIndex++}`);
7688
- values.push(input.completedAt?.toISOString() ?? null);
7736
+ const iso = input.completedAt?.toISOString() ?? null;
7737
+ setClauses.push(`"completedAt" = $${paramIndex++}`, `"completedAtZ" = $${paramIndex++}`);
7738
+ values.push(iso, iso);
7689
7739
  }
7690
7740
  values.push(input.id);
7691
7741
  await this.#db.client.none(`UPDATE ${tableName} SET ${setClauses.join(", ")} WHERE "id" = $${paramIndex}`, values);
@@ -7714,7 +7764,7 @@ var ExperimentsPG = class ExperimentsPG extends _mastra_core_storage.Experiments
7714
7764
  });
7715
7765
  const { conditions, params } = tenancyWhere(filters, 2);
7716
7766
  const whereSql = ["\"id\" = $1", ...conditions].join(" AND ");
7717
- const result = await client.oneOrNone(`SELECT * FROM ${tableName} WHERE ${whereSql}`, [id, ...params]);
7767
+ const result = await client.oneOrNone(`SELECT ${EXPERIMENT_COLUMNS} FROM ${tableName} WHERE ${whereSql}`, [id, ...params]);
7718
7768
  return result ? this.transformExperimentRow(result) : null;
7719
7769
  } catch (error) {
7720
7770
  throw new _mastra_core_error.MastraError({
@@ -7801,7 +7851,7 @@ var ExperimentsPG = class ExperimentsPG extends _mastra_core_storage.Experiments
7801
7851
  const { offset, perPage: perPageForResponse } = (0, _mastra_core_storage.calculatePagination)(page, perPageInput, perPage);
7802
7852
  const limitValue = perPageInput === false ? total : perPage;
7803
7853
  return {
7804
- experiments: (await this.#db.readClient.manyOrNone(`SELECT * FROM ${tableName} ${whereClause} ORDER BY "${orderBy.field}" ${orderBy.direction}, "id" ASC LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`, [
7854
+ experiments: (await this.#db.readClient.manyOrNone(`SELECT ${EXPERIMENT_COLUMNS} FROM ${tableName} ${whereClause} ORDER BY "${orderBy.field}" ${orderBy.direction}, "id" ASC LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`, [
7805
7855
  ...queryParams,
7806
7856
  limitValue,
7807
7857
  offset
@@ -7879,9 +7929,11 @@ var ExperimentsPG = class ExperimentsPG extends _mastra_core_storage.Experiments
7879
7929
  return t.one(`INSERT INTO ${resultsTable} (
7880
7930
  "id", "experimentId", "itemId", "itemDatasetVersion", "organizationId", "projectId",
7881
7931
  "input", "output", "groundTruth", "metadata", "error", "startedAt", "completedAt",
7882
- "retryCount", "attempt", "traceId", "status", "tags", "toolMockReport", "createdAt"
7883
- ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20)
7884
- RETURNING *`, [
7932
+ "retryCount", "attempt", "traceId", "status", "tags", "toolMockReport", "createdAt",
7933
+ "startedAtZ", "completedAtZ", "createdAtZ"
7934
+ ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20,
7935
+ $21, $22, $23)
7936
+ RETURNING ${EXPERIMENT_RESULT_COLUMNS}`, [
7885
7937
  id,
7886
7938
  input.experimentId,
7887
7939
  input.itemId,
@@ -7901,6 +7953,9 @@ var ExperimentsPG = class ExperimentsPG extends _mastra_core_storage.Experiments
7901
7953
  input.status ?? null,
7902
7954
  purgeMetadata || input.tags == null ? null : toPgJson(input.tags),
7903
7955
  purgeMetadata || input.toolMockReport == null ? null : toPgJson(input.toolMockReport),
7956
+ nowIso,
7957
+ input.startedAt.toISOString(),
7958
+ input.completedAt.toISOString(),
7904
7959
  nowIso
7905
7960
  ]);
7906
7961
  });
@@ -7927,6 +7982,7 @@ var ExperimentsPG = class ExperimentsPG extends _mastra_core_storage.Experiments
7927
7982
  const row = await this.#db.client.tx(async (t) => {
7928
7983
  const owner = await t.oneOrNone(`SELECT "datasetId" FROM ${experimentsTable} WHERE "id" = $1`, [input.experimentId]);
7929
7984
  const purgeMetadata = await this.#resolvePurgeMetadata(t, owner?.datasetId, input.itemId);
7985
+ const nowIso = (/* @__PURE__ */ new Date()).toISOString();
7930
7986
  const existing = await t.oneOrNone(`SELECT "id" FROM ${tableName} WHERE "experimentId" = $1 AND "itemId" = $2 AND COALESCE("attempt", 0) = $3 FOR UPDATE`, [
7931
7987
  input.experimentId,
7932
7988
  input.itemId,
@@ -7937,9 +7993,11 @@ var ExperimentsPG = class ExperimentsPG extends _mastra_core_storage.Experiments
7937
7993
  return t.one(`INSERT INTO ${tableName} (
7938
7994
  "id", "experimentId", "itemId", "itemDatasetVersion", "organizationId", "projectId",
7939
7995
  "input", "output", "groundTruth", "metadata", "error", "startedAt", "completedAt",
7940
- "retryCount", "attempt", "traceId", "status", "tags", "toolMockReport", "createdAt"
7941
- ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20)
7942
- RETURNING *`, [
7996
+ "retryCount", "attempt", "traceId", "status", "tags", "toolMockReport", "createdAt",
7997
+ "startedAtZ", "completedAtZ", "createdAtZ"
7998
+ ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20,
7999
+ $21, $22, $23)
8000
+ RETURNING ${EXPERIMENT_RESULT_COLUMNS}`, [
7943
8001
  id,
7944
8002
  input.experimentId,
7945
8003
  input.itemId,
@@ -7959,15 +8017,19 @@ var ExperimentsPG = class ExperimentsPG extends _mastra_core_storage.Experiments
7959
8017
  input.status ?? null,
7960
8018
  purgeMetadata || input.tags == null ? null : toPgJson(input.tags),
7961
8019
  purgeMetadata || input.toolMockReport == null ? null : toPgJson(input.toolMockReport),
7962
- (/* @__PURE__ */ new Date()).toISOString()
8020
+ nowIso,
8021
+ input.startedAt.toISOString(),
8022
+ input.completedAt.toISOString(),
8023
+ nowIso
7963
8024
  ]);
7964
8025
  }
7965
8026
  return t.one(`UPDATE ${tableName} SET
7966
8027
  "itemDatasetVersion" = $2, "organizationId" = $3, "projectId" = $4,
7967
8028
  "input" = $5, "output" = $6, "groundTruth" = $7, "metadata" = $8, "error" = $9,
7968
8029
  "startedAt" = $10, "completedAt" = $11, "retryCount" = $12, "attempt" = $13,
7969
- "traceId" = $14, "status" = $15, "tags" = $16, "toolMockReport" = $17
7970
- WHERE "id" = $1 RETURNING *`, [
8030
+ "traceId" = $14, "status" = $15, "tags" = $16, "toolMockReport" = $17,
8031
+ "startedAtZ" = $18, "completedAtZ" = $19
8032
+ WHERE "id" = $1 RETURNING ${EXPERIMENT_RESULT_COLUMNS}`, [
7971
8033
  existing.id,
7972
8034
  input.itemDatasetVersion ?? null,
7973
8035
  input.organizationId ?? null,
@@ -7984,7 +8046,9 @@ var ExperimentsPG = class ExperimentsPG extends _mastra_core_storage.Experiments
7984
8046
  input.traceId ?? null,
7985
8047
  input.status ?? null,
7986
8048
  purgeMetadata || input.tags == null ? null : toPgJson(input.tags),
7987
- purgeMetadata || input.toolMockReport == null ? null : toPgJson(input.toolMockReport)
8049
+ purgeMetadata || input.toolMockReport == null ? null : toPgJson(input.toolMockReport),
8050
+ input.startedAt.toISOString(),
8051
+ input.completedAt.toISOString()
7988
8052
  ]);
7989
8053
  });
7990
8054
  return this.transformExperimentResultRow(row);
@@ -8018,7 +8082,7 @@ var ExperimentsPG = class ExperimentsPG extends _mastra_core_storage.Experiments
8018
8082
  "tags" = CASE WHEN $4 THEN NULL WHEN $5 THEN $6::jsonb ELSE "tags" END,
8019
8083
  "comment" = CASE WHEN $4 THEN NULL WHEN $7 THEN $8 ELSE "comment" END
8020
8084
  WHERE "id" = $1${input.experimentId !== void 0 ? " AND \"experimentId\" = $9" : ""}
8021
- RETURNING *`, [
8085
+ RETURNING ${EXPERIMENT_RESULT_COLUMNS}`, [
8022
8086
  input.id,
8023
8087
  input.status !== void 0,
8024
8088
  input.status ?? null,
@@ -8061,7 +8125,7 @@ var ExperimentsPG = class ExperimentsPG extends _mastra_core_storage.Experiments
8061
8125
  });
8062
8126
  const { conditions, params } = tenancyWhere(filters, 2);
8063
8127
  const whereSql = ["\"id\" = $1", ...conditions].join(" AND ");
8064
- const result = await client.oneOrNone(`SELECT * FROM ${tableName} WHERE ${whereSql}`, [id, ...params]);
8128
+ const result = await client.oneOrNone(`SELECT ${EXPERIMENT_RESULT_COLUMNS} FROM ${tableName} WHERE ${whereSql}`, [id, ...params]);
8065
8129
  return result ? this.transformExperimentResultRow(result) : null;
8066
8130
  } catch (error) {
8067
8131
  throw new _mastra_core_error.MastraError({
@@ -8124,7 +8188,7 @@ var ExperimentsPG = class ExperimentsPG extends _mastra_core_storage.Experiments
8124
8188
  const { offset, perPage: perPageForResponse } = (0, _mastra_core_storage.calculatePagination)(page, perPageInput, perPage);
8125
8189
  const limitValue = perPageInput === false ? total : perPage;
8126
8190
  return {
8127
- results: (await this.#db.readClient.manyOrNone(`SELECT * FROM ${tableName} ${whereClause} ORDER BY "${orderBy.field}" ${orderBy.direction}, "id" ASC LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`, [
8191
+ results: (await this.#db.readClient.manyOrNone(`SELECT ${EXPERIMENT_RESULT_COLUMNS} FROM ${tableName} ${whereClause} ORDER BY "${orderBy.field}" ${orderBy.direction}, "id" ASC LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`, [
8128
8192
  ...queryParams,
8129
8193
  limitValue,
8130
8194
  offset
@@ -19543,6 +19607,13 @@ const timeToFirstChunkSortSupported = _mastra_core_storage.TRACE_QUERY_ORDER_FIE
19543
19607
  /** Same rule for time to first token: returned and sorted only when core knows the field. */
19544
19608
  const timeToFirstTokenSupported = "timeToFirstTokenMs" in _mastra_core_storage.TRACE_QUERY_FIELD_REGISTRY.trace;
19545
19609
  const timeToFirstTokenSortSupported = _mastra_core_storage.TRACE_QUERY_ORDER_FIELDS?.includes("timeToFirstTokenMs") === true;
19610
+ /**
19611
+ * Outcome fields follow the same rule: core before them rejects their predicates and strict rows,
19612
+ * so they are only returned and declared when core knows them.
19613
+ */
19614
+ const spanQueryOutcomeFieldsSupported = "outcome" in (_mastra_core_storage.SPAN_QUERY_FIELD_REGISTRY ?? {});
19615
+ const traceQueryOutcomeFieldsSupported = "outcome" in (_mastra_core_storage.TRACE_QUERY_FIELD_REGISTRY?.trace ?? {});
19616
+ const outcomeFeatures = [...spanQueryOutcomeFieldsSupported ? ["span-query-outcome-fields"] : [], ...traceQueryOutcomeFieldsSupported ? ["trace-query-outcome-fields"] : []];
19546
19617
  const timingFeatures = [
19547
19618
  ...timeToFirstChunkSupported ? ["trace-query-time-to-first-chunk"] : [],
19548
19619
  ...timeToFirstChunkSortSupported ? ["trace-query-time-to-first-chunk-sort"] : [],
@@ -19589,6 +19660,8 @@ const TRACE_FIELDS = {
19589
19660
  spanType: "r.\"spanType\"",
19590
19661
  environment: "r.\"environment\"",
19591
19662
  status: TRACE_STATUS_SQL,
19663
+ outcome: "r.\"outcome\"",
19664
+ stopReason: "r.\"stopReason\"",
19592
19665
  tags: "r.\"tags\""
19593
19666
  };
19594
19667
  const SPAN_FIELDS = {
@@ -19602,6 +19675,8 @@ const SPAN_FIELDS = {
19602
19675
  timeToFirstChunkMs: "s.\"timeToFirstChunkMs\"",
19603
19676
  timeToFirstTokenMs: "s.\"timeToFirstTokenMs\"",
19604
19677
  status: "s.\"status\"",
19678
+ outcome: "s.\"outcome\"",
19679
+ stopReason: "s.\"stopReason\"",
19605
19680
  error: "s.\"error\"",
19606
19681
  entityType: "s.\"entityType\"",
19607
19682
  entityId: "s.\"entityId\"",
@@ -19675,7 +19750,10 @@ const TRACE_SELECT = `
19675
19750
  r."entityName" AS "entityName",
19676
19751
  r."entityType" AS "entityType",
19677
19752
  r."environment" AS "environment",
19678
- ${TRACE_STATUS_SQL} AS "status"`;
19753
+ ${TRACE_STATUS_SQL} AS "status",
19754
+ r."outcome" AS "outcome",
19755
+ r."stopReason" AS "stopReason",
19756
+ r."stopReasonContext" AS "stopReasonContext"`;
19679
19757
  function fieldSql(registry, field) {
19680
19758
  const sql = registry[field];
19681
19759
  if (sql === void 0) throw new Error(`Unsupported trusted trace-query field: ${field}`);
@@ -20032,8 +20110,8 @@ function compilePostgresTraceScope(schema, selection, relationCollections, scope
20032
20110
  s."traceId",
20033
20111
  s."name",
20034
20112
  s."spanType",
20035
- CASE WHEN jsonb_typeof(s."attributes" -> 'model') = 'string' THEN s."attributes" ->> 'model' END AS "model",
20036
- CASE WHEN jsonb_typeof(s."attributes" -> 'provider') = 'string' THEN s."attributes" ->> 'provider' END AS "provider",
20113
+ COALESCE(s."model", CASE WHEN jsonb_typeof(s."attributes" -> 'model') = 'string' THEN s."attributes" ->> 'model' END) AS "model",
20114
+ COALESCE(s."provider", CASE WHEN jsonb_typeof(s."attributes" -> 'provider') = 'string' THEN s."attributes" ->> 'provider' END) AS "provider",
20037
20115
  s."startedAt",
20038
20116
  CASE WHEN s."isPending" THEN NULL ELSE s."endedAt" END AS "endedAt",
20039
20117
  CASE
@@ -20043,6 +20121,8 @@ function compilePostgresTraceScope(schema, selection, relationCollections, scope
20043
20121
  s."timeToFirstChunkMs",
20044
20122
  s."timeToFirstTokenMs",
20045
20123
  CASE WHEN s."error" IS NOT NULL THEN 'error' ELSE 'success' END AS "status",
20124
+ s."outcome",
20125
+ s."stopReason",
20046
20126
  s."error",
20047
20127
  s."entityType",
20048
20128
  s."entityId",
@@ -20420,7 +20500,12 @@ function traceRowToResult(row) {
20420
20500
  entityName: row.entityName == null ? null : String(row.entityName),
20421
20501
  entityType: row.entityType == null ? null : String(row.entityType),
20422
20502
  environment: row.environment == null ? null : String(row.environment),
20423
- status: row.status
20503
+ status: row.status,
20504
+ ...traceQueryOutcomeFieldsSupported && {
20505
+ outcome: row.outcome == null ? null : String(row.outcome),
20506
+ stopReason: row.stopReason == null ? null : String(row.stopReason),
20507
+ stopReasonContext: row.stopReasonContext ?? null
20508
+ }
20424
20509
  };
20425
20510
  }
20426
20511
  async function queryTraces(client, schema, plan, timeoutMs) {
@@ -20573,7 +20658,9 @@ const fields = [
20573
20658
  "environment",
20574
20659
  "executionSource",
20575
20660
  "timeToFirstChunkMs",
20576
- "timeToFirstTokenMs"
20661
+ "timeToFirstTokenMs",
20662
+ "outcome",
20663
+ "stopReason"
20577
20664
  ];
20578
20665
  const identities = identityFields.map((field) => `"${field}"`).join(", ");
20579
20666
  const identityOrder = identityFields.map((field) => `"${field}" COLLATE "C" ASC NULLS LAST`).join(", ");
@@ -20623,8 +20710,6 @@ function compilePostgresSpanSelection(schema, plan) {
20623
20710
  };
20624
20711
  values.push(...predicate.values);
20625
20712
  const table = qualifiedTable(schema, TABLE_SPAN_EVENTS);
20626
- const legacyModelFields = _mastra_core_storage.SPAN_QUERY_FIELD_REGISTRY ? "" : `CASE WHEN jsonb_typeof(attributes -> 'model') = 'string' THEN attributes ->> 'model' END AS model,
20627
- CASE WHEN jsonb_typeof(attributes -> 'provider') = 'string' THEN attributes ->> 'provider' END AS provider,`;
20628
20713
  return {
20629
20714
  ctes: [
20630
20715
  `candidate_ids AS MATERIALIZED (
@@ -20635,7 +20720,7 @@ function compilePostgresSpanSelection(schema, plan) {
20635
20720
  `current_spans AS MATERIALIZED (
20636
20721
  SELECT DISTINCT ON (${identities})
20637
20722
  ${fields.map((field) => `"${field}"`).join(", ")}, "startedAt", "endedAt", "cursorId"::text AS version,
20638
- ${legacyModelFields}
20723
+ "model", "provider",
20639
20724
  (EXTRACT(EPOCH FROM ("endedAt" - "startedAt")) * 1000)::float8 AS "durationMs",
20640
20725
  CASE WHEN error IS NULL THEN 'success' ELSE 'error' END AS status,
20641
20726
  error IS NOT NULL AS "hasError"
@@ -20705,10 +20790,14 @@ async function querySpans(client, schema, plan, timeoutMs) {
20705
20790
  return runWithPostgresTraceQueryTimeout(client, timeoutMs, async (tx) => {
20706
20791
  const query = compilePostgresSpanQuery(schema, plan);
20707
20792
  const rows = await tx.any(query.text, query.values);
20708
- const selected = rows.map(({ version: _version, startedAt, endedAt, timeToFirstChunkMs, timeToFirstTokenMs, ...row }) => ({
20793
+ const selected = rows.map(({ version: _version, startedAt, endedAt, timeToFirstChunkMs, timeToFirstTokenMs, outcome, stopReason, ...row }) => ({
20709
20794
  ...row,
20710
20795
  ...timeToFirstChunkSupported && { timeToFirstChunkMs },
20711
20796
  ...timeToFirstTokenSupported && { timeToFirstTokenMs },
20797
+ ...spanQueryOutcomeFieldsSupported && {
20798
+ outcome,
20799
+ stopReason
20800
+ },
20712
20801
  startedAt: startedAt.toISOString(),
20713
20802
  endedAt: endedAt.toISOString()
20714
20803
  }));
@@ -20726,18 +20815,22 @@ async function querySpans(client, schema, plan, timeoutMs) {
20726
20815
  payloadValues.push(row.version);
20727
20816
  return `(${key} AND "cursorId" = $${payloadValues.length}::bigint)`;
20728
20817
  }).join(" OR ");
20729
- const payloadRows = await tx.any(`SELECT ${identities},
20730
- CASE WHEN jsonb_typeof(attributes -> 'model') = 'string' THEN attributes ->> 'model' END AS model,
20731
- CASE WHEN jsonb_typeof(attributes -> 'provider') = 'string' THEN attributes ->> 'provider' END AS provider,
20818
+ const payloadRows = await tx.any(`SELECT ${identities}, "stopReasonContext",
20819
+ COALESCE("model", CASE WHEN jsonb_typeof(attributes -> 'model') = 'string' THEN attributes ->> 'model' END) AS model,
20820
+ COALESCE("provider", CASE WHEN jsonb_typeof(attributes -> 'provider') = 'string' THEN attributes ->> 'provider' END) AS provider,
20732
20821
  ${PREVIEW_PAYLOADS$1.map(([preview, payload]) => `"${preview}", CASE WHEN ${missingPreviewSql(`"${preview}"`)} THEN "${payload}" END AS "${payload}"`).join(",\n ")}
20733
20822
  FROM ${qualifiedTable(schema, TABLE_SPAN_EVENTS)} WHERE ${payloadWhere}`, payloadValues);
20734
20823
  const payloads = payloadRows.map(toSpanQueryPayload);
20735
20824
  const metadata = new Map(payloadRows.map((row) => [_mastra_core_storage.spanQueryIdentityKey(row), row]));
20736
- const hydrated = selected.map((row) => ({
20737
- ...row,
20738
- model: metadata.get(_mastra_core_storage.spanQueryIdentityKey(row))?.model ?? null,
20739
- provider: metadata.get(_mastra_core_storage.spanQueryIdentityKey(row))?.provider ?? null
20740
- }));
20825
+ const hydrated = selected.map((row) => {
20826
+ const stored = metadata.get(_mastra_core_storage.spanQueryIdentityKey(row));
20827
+ return {
20828
+ ...row,
20829
+ model: stored?.model ?? null,
20830
+ provider: stored?.provider ?? null,
20831
+ ...spanQueryOutcomeFieldsSupported && { stopReasonContext: stored?.stopReasonContext ?? null }
20832
+ };
20833
+ });
20741
20834
  let metrics = [];
20742
20835
  if (!_mastra_core_storage.SPAN_QUERY_FIELD_REGISTRY) {
20743
20836
  await remaining();
@@ -20979,6 +21072,8 @@ const DIMENSION_SQL = {
20979
21072
  entityId: "r.\"entityId\"",
20980
21073
  environment: "r.\"environment\"",
20981
21074
  status: TRACE_STATUS_SQL,
21075
+ outcome: "r.\"outcome\"",
21076
+ stopReason: "r.\"stopReason\"",
20982
21077
  serviceName: "r.\"serviceName\"",
20983
21078
  executionSource: "r.\"executionSource\"",
20984
21079
  threadId: "r.\"threadId\"",
@@ -21001,6 +21096,8 @@ const SPAN_DIMENSION_SQL = Object.fromEntries([
21001
21096
  "rootEntityName",
21002
21097
  "environment",
21003
21098
  "status",
21099
+ "outcome",
21100
+ "stopReason",
21004
21101
  "serviceName",
21005
21102
  "executionSource",
21006
21103
  "threadId",
@@ -21008,7 +21105,9 @@ const SPAN_DIMENSION_SQL = Object.fromEntries([
21008
21105
  "userId",
21009
21106
  "sessionId",
21010
21107
  "organizationId",
21011
- "experimentId"
21108
+ "experimentId",
21109
+ "model",
21110
+ "provider"
21012
21111
  ].map((field) => [field, `r."${field}"`]));
21013
21112
  const PERCENTILES = {
21014
21113
  "duration.p50": .5,
@@ -22061,8 +22160,12 @@ async function dangerouslyClearTracing(client, schema) {
22061
22160
  * application database — observability writes will degrade app performance.
22062
22161
  * Use it through `MastraCompositeStore` with a dedicated Postgres connection.
22063
22162
  */
22064
- const spanQueryFeatures = typeof _mastra_core_storage.planSpanQuery === "function" ? ["span-query", "span-query-context-fields"] : [];
22065
- const spanAggregateFeatures = typeof _mastra_core_storage.planSpanAggregate === "function" ? ["span-aggregate"] : [];
22163
+ const spanQueryFeatures = typeof _mastra_core_storage.planSpanQuery === "function" ? [
22164
+ "span-query",
22165
+ "span-query-context-fields",
22166
+ ..."model" in (_mastra_core_storage.SPAN_QUERY_FIELD_REGISTRY ?? {}) ? ["span-query-model-fields"] : []
22167
+ ] : [];
22168
+ const spanAggregateFeatures = typeof _mastra_core_storage.planSpanAggregate === "function" ? ["span-aggregate", ..."model" in (_mastra_core_storage.SPAN_AGGREGATE_DIMENSION_REGISTRY ?? {}) ? ["span-aggregate-model-dimensions"] : []] : [];
22066
22169
  function wrapError(op, error, details) {
22067
22170
  if (error instanceof _mastra_core_error.MastraError || error instanceof _mastra_core_storage.TraceQueryCursorError || error instanceof _mastra_core_storage.TraceQueryExecutionError || error instanceof _mastra_core_storage.TraceQueryResourceLimitError) throw error;
22068
22171
  throw new _mastra_core_error.MastraError({
@@ -22274,6 +22377,7 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
22274
22377
  "feedback",
22275
22378
  "trace-query-context-ids",
22276
22379
  ...timingFeatures,
22380
+ ...outcomeFeatures,
22277
22381
  "trace-query-root-span-fields",
22278
22382
  "trace-query-root-entity-id"
22279
22383
  ];
@@ -22299,6 +22403,7 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
22299
22403
  "feedback",
22300
22404
  "trace-query-context-ids",
22301
22405
  ...timingFeatures,
22406
+ ...outcomeFeatures,
22302
22407
  "trace-query-root-span-fields",
22303
22408
  "trace-query-root-entity-id"
22304
22409
  ];