@mastra/pg 1.34.0-alpha.0 → 1.34.0-alpha.1

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 (43) 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 +483 -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 +668 -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 +152 -48
  26. package/dist/index.cjs.map +1 -1
  27. package/dist/index.js +152 -48
  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/package.json +3 -3
package/dist/index.js CHANGED
@@ -5262,6 +5262,7 @@ function rowToTask(row) {
5262
5262
  const suspendedAt = row.suspendedAtZ || row.suspendedAt;
5263
5263
  const completedAt = row.completedAtZ || row.completedAt;
5264
5264
  const leaseExpiresAt = row.leaseExpiresAtZ || row.leaseExpiresAt;
5265
+ const expiresAt = row.expiresAt != null ? row.expiresAtZ || row.expiresAt : void 0;
5265
5266
  return {
5266
5267
  id: row.id,
5267
5268
  status: row.status,
@@ -5283,10 +5284,13 @@ function rowToTask(row) {
5283
5284
  suspendedAt: suspendedAt ? suspendedAt instanceof Date ? suspendedAt : new Date(suspendedAt) : void 0,
5284
5285
  completedAt: completedAt ? completedAt instanceof Date ? completedAt : new Date(completedAt) : void 0,
5285
5286
  ownerId: row.ownerId ?? void 0,
5286
- leaseExpiresAt: leaseExpiresAt ? leaseExpiresAt instanceof Date ? leaseExpiresAt : new Date(leaseExpiresAt) : void 0
5287
+ leaseExpiresAt: leaseExpiresAt ? leaseExpiresAt instanceof Date ? leaseExpiresAt : new Date(leaseExpiresAt) : void 0,
5288
+ expiresAt: expiresAt ? expiresAt instanceof Date ? expiresAt : new Date(expiresAt) : void 0,
5289
+ metadata: parseJson$2(row.metadata)
5287
5290
  };
5288
5291
  }
5289
5292
  var BackgroundTasksPG = class BackgroundTasksPG extends BackgroundTasksStorage {
5293
+ supportsTaskExpiry = true;
5290
5294
  #db;
5291
5295
  #schema;
5292
5296
  #skipDefaultIndexes;
@@ -5327,7 +5331,9 @@ var BackgroundTasksPG = class BackgroundTasksPG extends BackgroundTasksStorage {
5327
5331
  "suspend_payload",
5328
5332
  "suspendedAt",
5329
5333
  "ownerId",
5330
- "leaseExpiresAt"
5334
+ "leaseExpiresAt",
5335
+ "expiresAt",
5336
+ "metadata"
5331
5337
  ]
5332
5338
  });
5333
5339
  await this.createDefaultIndexes();
@@ -5461,7 +5467,10 @@ var BackgroundTasksPG = class BackgroundTasksPG extends BackgroundTasksStorage {
5461
5467
  completedAtZ: task.completedAt?.toISOString() ?? null,
5462
5468
  ownerId: task.ownerId ?? null,
5463
5469
  leaseExpiresAt: task.leaseExpiresAt?.toISOString() ?? null,
5464
- leaseExpiresAtZ: task.leaseExpiresAt?.toISOString() ?? null
5470
+ leaseExpiresAtZ: task.leaseExpiresAt?.toISOString() ?? null,
5471
+ expiresAt: task.expiresAt?.toISOString() ?? null,
5472
+ expiresAtZ: task.expiresAt?.toISOString() ?? null,
5473
+ metadata: serializeJson(task.metadata)
5465
5474
  }
5466
5475
  });
5467
5476
  }
@@ -5517,6 +5526,16 @@ var BackgroundTasksPG = class BackgroundTasksPG extends BackgroundTasksStorage {
5517
5526
  const val = update.leaseExpiresAt?.toISOString() ?? null;
5518
5527
  params.push(val, val);
5519
5528
  }
5529
+ if ("expiresAt" in update) {
5530
+ setClauses.push(`"expiresAt" = $${paramIdx++}`);
5531
+ setClauses.push(`"expiresAtZ" = $${paramIdx++}`);
5532
+ const val = update.expiresAt?.toISOString() ?? null;
5533
+ params.push(val, val);
5534
+ }
5535
+ if ("metadata" in update) {
5536
+ setClauses.push(`"metadata" = $${paramIdx++}`);
5537
+ params.push(serializeJson(update.metadata));
5538
+ }
5520
5539
  if (setClauses.length === 0) return false;
5521
5540
  const table = getTableName$4(getSchemaName$4(this.#schema));
5522
5541
  params.push(taskId);
@@ -5539,6 +5558,13 @@ var BackgroundTasksPG = class BackgroundTasksPG extends BackgroundTasksStorage {
5539
5558
  params.push(options.expectedLeaseExpiresAt.toISOString());
5540
5559
  }
5541
5560
  }
5561
+ if (options?.expectedSuspendedAt !== void 0) {
5562
+ if (options.expectedSuspendedAt === null) where += ` AND "suspendedAtZ" IS NULL`;
5563
+ else {
5564
+ where += ` AND "suspendedAtZ" = $${paramIdx++}`;
5565
+ params.push(options.expectedSuspendedAt.toISOString());
5566
+ }
5567
+ }
5542
5568
  return ((await this.#db.client.query(`UPDATE ${table} SET ${setClauses.join(", ")} WHERE ${where}`, params)).rowCount ?? 0) > 0;
5543
5569
  }
5544
5570
  async getTask(taskId) {
@@ -5581,7 +5607,9 @@ var BackgroundTasksPG = class BackgroundTasksPG extends BackgroundTasksStorage {
5581
5607
  conditions.push(`"tool_call_id" = $${paramIdx++}`);
5582
5608
  params.push(filter.toolCallId);
5583
5609
  }
5584
- const dateCol = filter.dateFilterBy === "startedAt" ? "\"startedAt\"" : filter.dateFilterBy === "suspendedAt" ? "\"suspendedAt\"" : filter.dateFilterBy === "completedAt" ? "\"completedAt\"" : "\"createdAt\"";
5610
+ const dateCol = filter.dateFilterBy === "startedAt" ? "\"startedAt\"" : filter.dateFilterBy === "suspendedAt" ? "\"suspendedAt\"" : filter.dateFilterBy === "completedAt" ? "\"completedAt\"" : filter.dateFilterBy === "expiresAt" ? "\"expiresAtZ\"" : "\"createdAt\"";
5611
+ if (filter.hasExpiresAt !== void 0) conditions.push(filter.hasExpiresAt ? "\"expiresAt\" IS NOT NULL" : "\"expiresAt\" IS NULL");
5612
+ if (filter.dateFilterBy === "expiresAt" && (filter.fromDate || filter.toDate)) conditions.push("\"expiresAt\" IS NOT NULL");
5585
5613
  if (filter.fromDate) {
5586
5614
  conditions.push(`${dateCol} >= $${paramIdx++}`);
5587
5615
  params.push(filter.fromDate.toISOString());
@@ -5622,7 +5650,9 @@ var BackgroundTasksPG = class BackgroundTasksPG extends BackgroundTasksStorage {
5622
5650
  conditions.push(`"status" IN (${placeholders.join(", ")})`);
5623
5651
  params.push(...statuses);
5624
5652
  }
5625
- const dateCol = filter.dateFilterBy === "startedAt" ? "\"startedAt\"" : filter.dateFilterBy === "suspendedAt" ? "\"suspendedAt\"" : filter.dateFilterBy === "completedAt" ? "\"completedAt\"" : "\"createdAt\"";
5653
+ const dateCol = filter.dateFilterBy === "startedAt" ? "\"startedAt\"" : filter.dateFilterBy === "suspendedAt" ? "\"suspendedAt\"" : filter.dateFilterBy === "completedAt" ? "\"completedAt\"" : filter.dateFilterBy === "expiresAt" ? "\"expiresAtZ\"" : "\"createdAt\"";
5654
+ if (filter.hasExpiresAt !== void 0) conditions.push(filter.hasExpiresAt ? "\"expiresAt\" IS NOT NULL" : "\"expiresAt\" IS NULL");
5655
+ if (filter.dateFilterBy === "expiresAt" && (filter.fromDate || filter.toDate)) conditions.push("\"expiresAt\" IS NOT NULL");
5626
5656
  if (filter.fromDate) {
5627
5657
  conditions.push(`${dateCol} >= $${paramIdx++}`);
5628
5658
  params.push(filter.fromDate.toISOString());
@@ -7238,6 +7268,19 @@ var DatasetsPG = class DatasetsPG extends DatasetsStorage {
7238
7268
  };
7239
7269
  // src/storage/domains/experiments/index.ts
7240
7270
  const DEFAULT_PRUNE_BATCH_SIZE = 1e3;
7271
+ /**
7272
+ * Column list for reading experiment rows. Each `timestamp` column comes back
7273
+ * twice: as itself, and as its `*Z` mirror derived in the query with
7274
+ * `AT TIME ZONE 'UTC'`. A plain `timestamp` otherwise round-trips through the
7275
+ * session timezone, and reading the stored `*Z` column instead would return the
7276
+ * insert time for rows written before the mirrors were filled in. Deriving it
7277
+ * here makes legacy rows read correctly with no backfill to run at startup.
7278
+ */
7279
+ function selectWithMirrors(schema) {
7280
+ return Object.entries(schema).flatMap(([name, definition]) => definition.type === "timestamp" ? [`"${name}"`, `"${name}" AT TIME ZONE 'UTC' AS "${name}Z"`] : [`"${name}"`]).join(", ");
7281
+ }
7282
+ const EXPERIMENT_COLUMNS = selectWithMirrors(EXPERIMENTS_SCHEMA);
7283
+ const EXPERIMENT_RESULT_COLUMNS = selectWithMirrors(EXPERIMENT_RESULTS_SCHEMA);
7241
7284
  var ExperimentsPG = class ExperimentsPG extends ExperimentsStorage {
7242
7285
  #db;
7243
7286
  #schema;
@@ -7246,9 +7289,9 @@ var ExperimentsPG = class ExperimentsPG extends ExperimentsStorage {
7246
7289
  static MANAGED_TABLES = [TABLE_EXPERIMENTS, TABLE_EXPERIMENT_RESULTS];
7247
7290
  /**
7248
7291
  * Experiments prune as whole units: an aged experiment and its result rows go
7249
- * together, mirroring `deleteExperiment`. Anchored on `completedAt` (not the
7250
- * `completedAtZ` mirror, which carries a `DEFAULT NOW()` this domain never
7251
- * overrides — it holds insert time even for running rows). `completedAt` is
7292
+ * together, mirroring `deleteExperiment`. Anchored on `completedAt` rather
7293
+ * than its `completedAtZ` mirror: both are written together, but
7294
+ * `completedAt` is the column the retention index covers. `completedAt` is
7252
7295
  * written as a UTC ISO string and stays NULL while running, so
7253
7296
  * `completedAt < cutoff` is false for in-flight experiments.
7254
7297
  */
@@ -7568,7 +7611,11 @@ var ExperimentsPG = class ExperimentsPG extends ExperimentsStorage {
7568
7611
  startedAt: null,
7569
7612
  completedAt: null,
7570
7613
  createdAt: nowIso,
7571
- updatedAt: nowIso
7614
+ updatedAt: nowIso,
7615
+ startedAtZ: null,
7616
+ completedAtZ: null,
7617
+ createdAtZ: nowIso,
7618
+ updatedAtZ: nowIso
7572
7619
  }
7573
7620
  });
7574
7621
  return {
@@ -7657,12 +7704,14 @@ var ExperimentsPG = class ExperimentsPG extends ExperimentsStorage {
7657
7704
  values.push(input.skippedCount);
7658
7705
  }
7659
7706
  if (input.startedAt !== void 0) {
7660
- setClauses.push(`"startedAt" = $${paramIndex++}`);
7661
- values.push(input.startedAt?.toISOString() ?? null);
7707
+ const iso = input.startedAt?.toISOString() ?? null;
7708
+ setClauses.push(`"startedAt" = $${paramIndex++}`, `"startedAtZ" = $${paramIndex++}`);
7709
+ values.push(iso, iso);
7662
7710
  }
7663
7711
  if (input.completedAt !== void 0) {
7664
- setClauses.push(`"completedAt" = $${paramIndex++}`);
7665
- values.push(input.completedAt?.toISOString() ?? null);
7712
+ const iso = input.completedAt?.toISOString() ?? null;
7713
+ setClauses.push(`"completedAt" = $${paramIndex++}`, `"completedAtZ" = $${paramIndex++}`);
7714
+ values.push(iso, iso);
7666
7715
  }
7667
7716
  values.push(input.id);
7668
7717
  await this.#db.client.none(`UPDATE ${tableName} SET ${setClauses.join(", ")} WHERE "id" = $${paramIndex}`, values);
@@ -7691,7 +7740,7 @@ var ExperimentsPG = class ExperimentsPG extends ExperimentsStorage {
7691
7740
  });
7692
7741
  const { conditions, params } = tenancyWhere(filters, 2);
7693
7742
  const whereSql = ["\"id\" = $1", ...conditions].join(" AND ");
7694
- const result = await client.oneOrNone(`SELECT * FROM ${tableName} WHERE ${whereSql}`, [id, ...params]);
7743
+ const result = await client.oneOrNone(`SELECT ${EXPERIMENT_COLUMNS} FROM ${tableName} WHERE ${whereSql}`, [id, ...params]);
7695
7744
  return result ? this.transformExperimentRow(result) : null;
7696
7745
  } catch (error) {
7697
7746
  throw new MastraError({
@@ -7778,7 +7827,7 @@ var ExperimentsPG = class ExperimentsPG extends ExperimentsStorage {
7778
7827
  const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
7779
7828
  const limitValue = perPageInput === false ? total : perPage;
7780
7829
  return {
7781
- experiments: (await this.#db.readClient.manyOrNone(`SELECT * FROM ${tableName} ${whereClause} ORDER BY "${orderBy.field}" ${orderBy.direction}, "id" ASC LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`, [
7830
+ 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}`, [
7782
7831
  ...queryParams,
7783
7832
  limitValue,
7784
7833
  offset
@@ -7856,9 +7905,11 @@ var ExperimentsPG = class ExperimentsPG extends ExperimentsStorage {
7856
7905
  return t.one(`INSERT INTO ${resultsTable} (
7857
7906
  "id", "experimentId", "itemId", "itemDatasetVersion", "organizationId", "projectId",
7858
7907
  "input", "output", "groundTruth", "metadata", "error", "startedAt", "completedAt",
7859
- "retryCount", "attempt", "traceId", "status", "tags", "toolMockReport", "createdAt"
7860
- ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20)
7861
- RETURNING *`, [
7908
+ "retryCount", "attempt", "traceId", "status", "tags", "toolMockReport", "createdAt",
7909
+ "startedAtZ", "completedAtZ", "createdAtZ"
7910
+ ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20,
7911
+ $21, $22, $23)
7912
+ RETURNING ${EXPERIMENT_RESULT_COLUMNS}`, [
7862
7913
  id,
7863
7914
  input.experimentId,
7864
7915
  input.itemId,
@@ -7878,6 +7929,9 @@ var ExperimentsPG = class ExperimentsPG extends ExperimentsStorage {
7878
7929
  input.status ?? null,
7879
7930
  purgeMetadata || input.tags == null ? null : toPgJson(input.tags),
7880
7931
  purgeMetadata || input.toolMockReport == null ? null : toPgJson(input.toolMockReport),
7932
+ nowIso,
7933
+ input.startedAt.toISOString(),
7934
+ input.completedAt.toISOString(),
7881
7935
  nowIso
7882
7936
  ]);
7883
7937
  });
@@ -7904,6 +7958,7 @@ var ExperimentsPG = class ExperimentsPG extends ExperimentsStorage {
7904
7958
  const row = await this.#db.client.tx(async (t) => {
7905
7959
  const owner = await t.oneOrNone(`SELECT "datasetId" FROM ${experimentsTable} WHERE "id" = $1`, [input.experimentId]);
7906
7960
  const purgeMetadata = await this.#resolvePurgeMetadata(t, owner?.datasetId, input.itemId);
7961
+ const nowIso = (/* @__PURE__ */ new Date()).toISOString();
7907
7962
  const existing = await t.oneOrNone(`SELECT "id" FROM ${tableName} WHERE "experimentId" = $1 AND "itemId" = $2 AND COALESCE("attempt", 0) = $3 FOR UPDATE`, [
7908
7963
  input.experimentId,
7909
7964
  input.itemId,
@@ -7914,9 +7969,11 @@ var ExperimentsPG = class ExperimentsPG extends ExperimentsStorage {
7914
7969
  return t.one(`INSERT INTO ${tableName} (
7915
7970
  "id", "experimentId", "itemId", "itemDatasetVersion", "organizationId", "projectId",
7916
7971
  "input", "output", "groundTruth", "metadata", "error", "startedAt", "completedAt",
7917
- "retryCount", "attempt", "traceId", "status", "tags", "toolMockReport", "createdAt"
7918
- ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20)
7919
- RETURNING *`, [
7972
+ "retryCount", "attempt", "traceId", "status", "tags", "toolMockReport", "createdAt",
7973
+ "startedAtZ", "completedAtZ", "createdAtZ"
7974
+ ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20,
7975
+ $21, $22, $23)
7976
+ RETURNING ${EXPERIMENT_RESULT_COLUMNS}`, [
7920
7977
  id,
7921
7978
  input.experimentId,
7922
7979
  input.itemId,
@@ -7936,15 +7993,19 @@ var ExperimentsPG = class ExperimentsPG extends ExperimentsStorage {
7936
7993
  input.status ?? null,
7937
7994
  purgeMetadata || input.tags == null ? null : toPgJson(input.tags),
7938
7995
  purgeMetadata || input.toolMockReport == null ? null : toPgJson(input.toolMockReport),
7939
- (/* @__PURE__ */ new Date()).toISOString()
7996
+ nowIso,
7997
+ input.startedAt.toISOString(),
7998
+ input.completedAt.toISOString(),
7999
+ nowIso
7940
8000
  ]);
7941
8001
  }
7942
8002
  return t.one(`UPDATE ${tableName} SET
7943
8003
  "itemDatasetVersion" = $2, "organizationId" = $3, "projectId" = $4,
7944
8004
  "input" = $5, "output" = $6, "groundTruth" = $7, "metadata" = $8, "error" = $9,
7945
8005
  "startedAt" = $10, "completedAt" = $11, "retryCount" = $12, "attempt" = $13,
7946
- "traceId" = $14, "status" = $15, "tags" = $16, "toolMockReport" = $17
7947
- WHERE "id" = $1 RETURNING *`, [
8006
+ "traceId" = $14, "status" = $15, "tags" = $16, "toolMockReport" = $17,
8007
+ "startedAtZ" = $18, "completedAtZ" = $19
8008
+ WHERE "id" = $1 RETURNING ${EXPERIMENT_RESULT_COLUMNS}`, [
7948
8009
  existing.id,
7949
8010
  input.itemDatasetVersion ?? null,
7950
8011
  input.organizationId ?? null,
@@ -7961,7 +8022,9 @@ var ExperimentsPG = class ExperimentsPG extends ExperimentsStorage {
7961
8022
  input.traceId ?? null,
7962
8023
  input.status ?? null,
7963
8024
  purgeMetadata || input.tags == null ? null : toPgJson(input.tags),
7964
- purgeMetadata || input.toolMockReport == null ? null : toPgJson(input.toolMockReport)
8025
+ purgeMetadata || input.toolMockReport == null ? null : toPgJson(input.toolMockReport),
8026
+ input.startedAt.toISOString(),
8027
+ input.completedAt.toISOString()
7965
8028
  ]);
7966
8029
  });
7967
8030
  return this.transformExperimentResultRow(row);
@@ -7995,7 +8058,7 @@ var ExperimentsPG = class ExperimentsPG extends ExperimentsStorage {
7995
8058
  "tags" = CASE WHEN $4 THEN NULL WHEN $5 THEN $6::jsonb ELSE "tags" END,
7996
8059
  "comment" = CASE WHEN $4 THEN NULL WHEN $7 THEN $8 ELSE "comment" END
7997
8060
  WHERE "id" = $1${input.experimentId !== void 0 ? " AND \"experimentId\" = $9" : ""}
7998
- RETURNING *`, [
8061
+ RETURNING ${EXPERIMENT_RESULT_COLUMNS}`, [
7999
8062
  input.id,
8000
8063
  input.status !== void 0,
8001
8064
  input.status ?? null,
@@ -8038,7 +8101,7 @@ var ExperimentsPG = class ExperimentsPG extends ExperimentsStorage {
8038
8101
  });
8039
8102
  const { conditions, params } = tenancyWhere(filters, 2);
8040
8103
  const whereSql = ["\"id\" = $1", ...conditions].join(" AND ");
8041
- const result = await client.oneOrNone(`SELECT * FROM ${tableName} WHERE ${whereSql}`, [id, ...params]);
8104
+ const result = await client.oneOrNone(`SELECT ${EXPERIMENT_RESULT_COLUMNS} FROM ${tableName} WHERE ${whereSql}`, [id, ...params]);
8042
8105
  return result ? this.transformExperimentResultRow(result) : null;
8043
8106
  } catch (error) {
8044
8107
  throw new MastraError({
@@ -8101,7 +8164,7 @@ var ExperimentsPG = class ExperimentsPG extends ExperimentsStorage {
8101
8164
  const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
8102
8165
  const limitValue = perPageInput === false ? total : perPage;
8103
8166
  return {
8104
- results: (await this.#db.readClient.manyOrNone(`SELECT * FROM ${tableName} ${whereClause} ORDER BY "${orderBy.field}" ${orderBy.direction}, "id" ASC LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`, [
8167
+ 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}`, [
8105
8168
  ...queryParams,
8106
8169
  limitValue,
8107
8170
  offset
@@ -19520,6 +19583,13 @@ const timeToFirstChunkSortSupported = coreStorage.TRACE_QUERY_ORDER_FIELDS?.incl
19520
19583
  /** Same rule for time to first token: returned and sorted only when core knows the field. */
19521
19584
  const timeToFirstTokenSupported = "timeToFirstTokenMs" in coreStorage.TRACE_QUERY_FIELD_REGISTRY.trace;
19522
19585
  const timeToFirstTokenSortSupported = coreStorage.TRACE_QUERY_ORDER_FIELDS?.includes("timeToFirstTokenMs") === true;
19586
+ /**
19587
+ * Outcome fields follow the same rule: core before them rejects their predicates and strict rows,
19588
+ * so they are only returned and declared when core knows them.
19589
+ */
19590
+ const spanQueryOutcomeFieldsSupported = "outcome" in (coreStorage.SPAN_QUERY_FIELD_REGISTRY ?? {});
19591
+ const traceQueryOutcomeFieldsSupported = "outcome" in (coreStorage.TRACE_QUERY_FIELD_REGISTRY?.trace ?? {});
19592
+ const outcomeFeatures = [...spanQueryOutcomeFieldsSupported ? ["span-query-outcome-fields"] : [], ...traceQueryOutcomeFieldsSupported ? ["trace-query-outcome-fields"] : []];
19523
19593
  const timingFeatures = [
19524
19594
  ...timeToFirstChunkSupported ? ["trace-query-time-to-first-chunk"] : [],
19525
19595
  ...timeToFirstChunkSortSupported ? ["trace-query-time-to-first-chunk-sort"] : [],
@@ -19566,6 +19636,8 @@ const TRACE_FIELDS = {
19566
19636
  spanType: "r.\"spanType\"",
19567
19637
  environment: "r.\"environment\"",
19568
19638
  status: TRACE_STATUS_SQL,
19639
+ outcome: "r.\"outcome\"",
19640
+ stopReason: "r.\"stopReason\"",
19569
19641
  tags: "r.\"tags\""
19570
19642
  };
19571
19643
  const SPAN_FIELDS = {
@@ -19579,6 +19651,8 @@ const SPAN_FIELDS = {
19579
19651
  timeToFirstChunkMs: "s.\"timeToFirstChunkMs\"",
19580
19652
  timeToFirstTokenMs: "s.\"timeToFirstTokenMs\"",
19581
19653
  status: "s.\"status\"",
19654
+ outcome: "s.\"outcome\"",
19655
+ stopReason: "s.\"stopReason\"",
19582
19656
  error: "s.\"error\"",
19583
19657
  entityType: "s.\"entityType\"",
19584
19658
  entityId: "s.\"entityId\"",
@@ -19652,7 +19726,10 @@ const TRACE_SELECT = `
19652
19726
  r."entityName" AS "entityName",
19653
19727
  r."entityType" AS "entityType",
19654
19728
  r."environment" AS "environment",
19655
- ${TRACE_STATUS_SQL} AS "status"`;
19729
+ ${TRACE_STATUS_SQL} AS "status",
19730
+ r."outcome" AS "outcome",
19731
+ r."stopReason" AS "stopReason",
19732
+ r."stopReasonContext" AS "stopReasonContext"`;
19656
19733
  function fieldSql(registry, field) {
19657
19734
  const sql = registry[field];
19658
19735
  if (sql === void 0) throw new Error(`Unsupported trusted trace-query field: ${field}`);
@@ -20009,8 +20086,8 @@ function compilePostgresTraceScope(schema, selection, relationCollections, scope
20009
20086
  s."traceId",
20010
20087
  s."name",
20011
20088
  s."spanType",
20012
- CASE WHEN jsonb_typeof(s."attributes" -> 'model') = 'string' THEN s."attributes" ->> 'model' END AS "model",
20013
- CASE WHEN jsonb_typeof(s."attributes" -> 'provider') = 'string' THEN s."attributes" ->> 'provider' END AS "provider",
20089
+ COALESCE(s."model", CASE WHEN jsonb_typeof(s."attributes" -> 'model') = 'string' THEN s."attributes" ->> 'model' END) AS "model",
20090
+ COALESCE(s."provider", CASE WHEN jsonb_typeof(s."attributes" -> 'provider') = 'string' THEN s."attributes" ->> 'provider' END) AS "provider",
20014
20091
  s."startedAt",
20015
20092
  CASE WHEN s."isPending" THEN NULL ELSE s."endedAt" END AS "endedAt",
20016
20093
  CASE
@@ -20020,6 +20097,8 @@ function compilePostgresTraceScope(schema, selection, relationCollections, scope
20020
20097
  s."timeToFirstChunkMs",
20021
20098
  s."timeToFirstTokenMs",
20022
20099
  CASE WHEN s."error" IS NOT NULL THEN 'error' ELSE 'success' END AS "status",
20100
+ s."outcome",
20101
+ s."stopReason",
20023
20102
  s."error",
20024
20103
  s."entityType",
20025
20104
  s."entityId",
@@ -20397,7 +20476,12 @@ function traceRowToResult(row) {
20397
20476
  entityName: row.entityName == null ? null : String(row.entityName),
20398
20477
  entityType: row.entityType == null ? null : String(row.entityType),
20399
20478
  environment: row.environment == null ? null : String(row.environment),
20400
- status: row.status
20479
+ status: row.status,
20480
+ ...traceQueryOutcomeFieldsSupported && {
20481
+ outcome: row.outcome == null ? null : String(row.outcome),
20482
+ stopReason: row.stopReason == null ? null : String(row.stopReason),
20483
+ stopReasonContext: row.stopReasonContext ?? null
20484
+ }
20401
20485
  };
20402
20486
  }
20403
20487
  async function queryTraces(client, schema, plan, timeoutMs) {
@@ -20550,7 +20634,9 @@ const fields = [
20550
20634
  "environment",
20551
20635
  "executionSource",
20552
20636
  "timeToFirstChunkMs",
20553
- "timeToFirstTokenMs"
20637
+ "timeToFirstTokenMs",
20638
+ "outcome",
20639
+ "stopReason"
20554
20640
  ];
20555
20641
  const identities = identityFields.map((field) => `"${field}"`).join(", ");
20556
20642
  const identityOrder = identityFields.map((field) => `"${field}" COLLATE "C" ASC NULLS LAST`).join(", ");
@@ -20600,8 +20686,6 @@ function compilePostgresSpanSelection(schema, plan) {
20600
20686
  };
20601
20687
  values.push(...predicate.values);
20602
20688
  const table = qualifiedTable(schema, TABLE_SPAN_EVENTS);
20603
- const legacyModelFields = coreStorage.SPAN_QUERY_FIELD_REGISTRY ? "" : `CASE WHEN jsonb_typeof(attributes -> 'model') = 'string' THEN attributes ->> 'model' END AS model,
20604
- CASE WHEN jsonb_typeof(attributes -> 'provider') = 'string' THEN attributes ->> 'provider' END AS provider,`;
20605
20689
  return {
20606
20690
  ctes: [
20607
20691
  `candidate_ids AS MATERIALIZED (
@@ -20612,7 +20696,7 @@ function compilePostgresSpanSelection(schema, plan) {
20612
20696
  `current_spans AS MATERIALIZED (
20613
20697
  SELECT DISTINCT ON (${identities})
20614
20698
  ${fields.map((field) => `"${field}"`).join(", ")}, "startedAt", "endedAt", "cursorId"::text AS version,
20615
- ${legacyModelFields}
20699
+ "model", "provider",
20616
20700
  (EXTRACT(EPOCH FROM ("endedAt" - "startedAt")) * 1000)::float8 AS "durationMs",
20617
20701
  CASE WHEN error IS NULL THEN 'success' ELSE 'error' END AS status,
20618
20702
  error IS NOT NULL AS "hasError"
@@ -20682,10 +20766,14 @@ async function querySpans(client, schema, plan, timeoutMs) {
20682
20766
  return runWithPostgresTraceQueryTimeout(client, timeoutMs, async (tx) => {
20683
20767
  const query = compilePostgresSpanQuery(schema, plan);
20684
20768
  const rows = await tx.any(query.text, query.values);
20685
- const selected = rows.map(({ version: _version, startedAt, endedAt, timeToFirstChunkMs, timeToFirstTokenMs, ...row }) => ({
20769
+ const selected = rows.map(({ version: _version, startedAt, endedAt, timeToFirstChunkMs, timeToFirstTokenMs, outcome, stopReason, ...row }) => ({
20686
20770
  ...row,
20687
20771
  ...timeToFirstChunkSupported && { timeToFirstChunkMs },
20688
20772
  ...timeToFirstTokenSupported && { timeToFirstTokenMs },
20773
+ ...spanQueryOutcomeFieldsSupported && {
20774
+ outcome,
20775
+ stopReason
20776
+ },
20689
20777
  startedAt: startedAt.toISOString(),
20690
20778
  endedAt: endedAt.toISOString()
20691
20779
  }));
@@ -20703,18 +20791,22 @@ async function querySpans(client, schema, plan, timeoutMs) {
20703
20791
  payloadValues.push(row.version);
20704
20792
  return `(${key} AND "cursorId" = $${payloadValues.length}::bigint)`;
20705
20793
  }).join(" OR ");
20706
- const payloadRows = await tx.any(`SELECT ${identities},
20707
- CASE WHEN jsonb_typeof(attributes -> 'model') = 'string' THEN attributes ->> 'model' END AS model,
20708
- CASE WHEN jsonb_typeof(attributes -> 'provider') = 'string' THEN attributes ->> 'provider' END AS provider,
20794
+ const payloadRows = await tx.any(`SELECT ${identities}, "stopReasonContext",
20795
+ COALESCE("model", CASE WHEN jsonb_typeof(attributes -> 'model') = 'string' THEN attributes ->> 'model' END) AS model,
20796
+ COALESCE("provider", CASE WHEN jsonb_typeof(attributes -> 'provider') = 'string' THEN attributes ->> 'provider' END) AS provider,
20709
20797
  ${PREVIEW_PAYLOADS$1.map(([preview, payload]) => `"${preview}", CASE WHEN ${missingPreviewSql(`"${preview}"`)} THEN "${payload}" END AS "${payload}"`).join(",\n ")}
20710
20798
  FROM ${qualifiedTable(schema, TABLE_SPAN_EVENTS)} WHERE ${payloadWhere}`, payloadValues);
20711
20799
  const payloads = payloadRows.map(toSpanQueryPayload);
20712
20800
  const metadata = new Map(payloadRows.map((row) => [coreStorage.spanQueryIdentityKey(row), row]));
20713
- const hydrated = selected.map((row) => ({
20714
- ...row,
20715
- model: metadata.get(coreStorage.spanQueryIdentityKey(row))?.model ?? null,
20716
- provider: metadata.get(coreStorage.spanQueryIdentityKey(row))?.provider ?? null
20717
- }));
20801
+ const hydrated = selected.map((row) => {
20802
+ const stored = metadata.get(coreStorage.spanQueryIdentityKey(row));
20803
+ return {
20804
+ ...row,
20805
+ model: stored?.model ?? null,
20806
+ provider: stored?.provider ?? null,
20807
+ ...spanQueryOutcomeFieldsSupported && { stopReasonContext: stored?.stopReasonContext ?? null }
20808
+ };
20809
+ });
20718
20810
  let metrics = [];
20719
20811
  if (!coreStorage.SPAN_QUERY_FIELD_REGISTRY) {
20720
20812
  await remaining();
@@ -20956,6 +21048,8 @@ const DIMENSION_SQL = {
20956
21048
  entityId: "r.\"entityId\"",
20957
21049
  environment: "r.\"environment\"",
20958
21050
  status: TRACE_STATUS_SQL,
21051
+ outcome: "r.\"outcome\"",
21052
+ stopReason: "r.\"stopReason\"",
20959
21053
  serviceName: "r.\"serviceName\"",
20960
21054
  executionSource: "r.\"executionSource\"",
20961
21055
  threadId: "r.\"threadId\"",
@@ -20978,6 +21072,8 @@ const SPAN_DIMENSION_SQL = Object.fromEntries([
20978
21072
  "rootEntityName",
20979
21073
  "environment",
20980
21074
  "status",
21075
+ "outcome",
21076
+ "stopReason",
20981
21077
  "serviceName",
20982
21078
  "executionSource",
20983
21079
  "threadId",
@@ -20985,7 +21081,9 @@ const SPAN_DIMENSION_SQL = Object.fromEntries([
20985
21081
  "userId",
20986
21082
  "sessionId",
20987
21083
  "organizationId",
20988
- "experimentId"
21084
+ "experimentId",
21085
+ "model",
21086
+ "provider"
20989
21087
  ].map((field) => [field, `r."${field}"`]));
20990
21088
  const PERCENTILES = {
20991
21089
  "duration.p50": .5,
@@ -22038,8 +22136,12 @@ async function dangerouslyClearTracing(client, schema) {
22038
22136
  * application database — observability writes will degrade app performance.
22039
22137
  * Use it through `MastraCompositeStore` with a dedicated Postgres connection.
22040
22138
  */
22041
- const spanQueryFeatures = typeof coreStorage.planSpanQuery === "function" ? ["span-query", "span-query-context-fields"] : [];
22042
- const spanAggregateFeatures = typeof coreStorage.planSpanAggregate === "function" ? ["span-aggregate"] : [];
22139
+ const spanQueryFeatures = typeof coreStorage.planSpanQuery === "function" ? [
22140
+ "span-query",
22141
+ "span-query-context-fields",
22142
+ ..."model" in (coreStorage.SPAN_QUERY_FIELD_REGISTRY ?? {}) ? ["span-query-model-fields"] : []
22143
+ ] : [];
22144
+ const spanAggregateFeatures = typeof coreStorage.planSpanAggregate === "function" ? ["span-aggregate", ..."model" in (coreStorage.SPAN_AGGREGATE_DIMENSION_REGISTRY ?? {}) ? ["span-aggregate-model-dimensions"] : []] : [];
22043
22145
  function wrapError(op, error, details) {
22044
22146
  if (error instanceof MastraError || error instanceof coreStorage.TraceQueryCursorError || error instanceof coreStorage.TraceQueryExecutionError || error instanceof coreStorage.TraceQueryResourceLimitError) throw error;
22045
22147
  throw new MastraError({
@@ -22251,6 +22353,7 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
22251
22353
  "feedback",
22252
22354
  "trace-query-context-ids",
22253
22355
  ...timingFeatures,
22356
+ ...outcomeFeatures,
22254
22357
  "trace-query-root-span-fields",
22255
22358
  "trace-query-root-entity-id"
22256
22359
  ];
@@ -22276,6 +22379,7 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
22276
22379
  "feedback",
22277
22380
  "trace-query-context-ids",
22278
22381
  ...timingFeatures,
22382
+ ...outcomeFeatures,
22279
22383
  "trace-query-root-span-fields",
22280
22384
  "trace-query-root-entity-id"
22281
22385
  ];