@mastra/pg 1.26.0-alpha.3 → 1.26.0-alpha.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,7 +3,7 @@ name: mastra-pg
3
3
  description: Documentation for @mastra/pg. Use when working with @mastra/pg APIs, configuration, or implementation.
4
4
  metadata:
5
5
  package: "@mastra/pg"
6
- version: "1.26.0-alpha.3"
6
+ version: "1.26.0-alpha.5"
7
7
  ---
8
8
 
9
9
  ## When to use
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.26.0-alpha.3",
2
+ "version": "1.26.0-alpha.5",
3
3
  "package": "@mastra/pg",
4
4
  "exports": {},
5
5
  "modules": {}
@@ -51,7 +51,7 @@ export const agent = new Agent({
51
51
 
52
52
  **options.observationalMemory** (`boolean | ObservationalMemoryOptions`): Enable Observational Memory for long-context agentic memory. Set to true for defaults, or pass a config object to customize token budgets, models, and scope. See Observational Memory reference for configuration details.
53
53
 
54
- **options.generateTitle** (`boolean | { model: DynamicArgument<MastraLanguageModel>; instructions?: DynamicArgument<string> }`): Controls automatic thread title generation from the conversation transcript. Can be a boolean or an object with custom model and instructions.
54
+ **options.generateTitle** (`boolean | { model?: DynamicArgument<MastraModelConfig>; instructions?: DynamicArgument<string>; minMessages?: number; emitEvent?: boolean }`): Controls automatic thread title generation from the conversation transcript. Accepts a boolean or an object with a custom model (any MastraModelConfig: a model instance, a "provider/model" ID, or an OpenAI-compatible config; defaults to the agent's own model), custom instructions, a minimum message count, and emitEvent. With emitEvent: true the run's stream waits for the title and emits it as a transient data-thread-title chunk before finish (durable and evented agents persist the title but don't emit the chunk yet).
55
55
 
56
56
  ## Returns
57
57
 
package/dist/index.cjs CHANGED
@@ -2360,6 +2360,8 @@ var RoutingDbClient = class {
2360
2360
  return this.active.tx(callback);
2361
2361
  }
2362
2362
  };
2363
+ /** Bytes reserved for the `_xxxxxxxx` collision suffix when hashWhenTruncated applies. */
2364
+ const TRUNCATION_HASH_SUFFIX_LENGTH = 9;
2363
2365
  function truncateIdentifier(value, maxLength = 63) {
2364
2366
  if (maxLength <= 0) return "";
2365
2367
  if (Buffer.byteLength(value, "utf-8") <= maxLength) return value;
@@ -2380,9 +2382,25 @@ function truncateIdentifier(value, maxLength = 63) {
2380
2382
  * in system catalogs (pg_constraint.conname, pg_indexes.indexname, etc.).
2381
2383
  * Without this normalisation, runtime lookups that compare a mixed-case name
2382
2384
  * against the catalog would silently fail.
2385
+ *
2386
+ * With `hashWhenTruncated`, a name that exceeds the limit is truncated further
2387
+ * to make room for `_` + 8 hex chars of the full name's sha256. Plain
2388
+ * truncation cuts the tail, so two names sharing a long `<schema>_<prefix>`
2389
+ * collapse to the same identifier and `CREATE INDEX IF NOT EXISTS` (which
2390
+ * matches by name only) silently skips the second one. The suffix is
2391
+ * deterministic, so creation, warm-init snapshot checks, and DDL export all
2392
+ * agree on the same name. Opt-in because renaming already-released constraint
2393
+ * names would orphan the existing objects in deployed catalogs.
2383
2394
  */
2384
- function buildConstraintName({ baseName, schemaName, maxLength = 63 }) {
2385
- return truncateIdentifier(`${schemaName ? `${schemaName}_` : ""}${baseName}`.toLowerCase(), maxLength);
2395
+ function buildConstraintName({ baseName, schemaName, maxLength = 63, hashWhenTruncated = false }) {
2396
+ const fullName = `${schemaName ? `${schemaName}_` : ""}${baseName}`.toLowerCase();
2397
+ if (hashWhenTruncated && Buffer.byteLength(fullName, "utf-8") > maxLength) {
2398
+ const suffixLength = Math.min(TRUNCATION_HASH_SUFFIX_LENGTH, maxLength);
2399
+ if (suffixLength < 2) return truncateIdentifier(fullName, maxLength);
2400
+ const hash = (0, crypto$1.createHash)("sha256").update(fullName).digest("hex").slice(0, suffixLength - 1);
2401
+ return `${truncateIdentifier(fullName, maxLength - suffixLength)}_${hash}`;
2402
+ }
2403
+ return truncateIdentifier(fullName, maxLength);
2386
2404
  }
2387
2405
  //#endregion
2388
2406
  //#region src/storage/db/pg-errors.ts
@@ -6118,6 +6136,14 @@ var DatasetsPG = class DatasetsPG extends _mastra_core_storage.DatasetsStorage {
6118
6136
  }
6119
6137
  async listDatasets(args) {
6120
6138
  try {
6139
+ const orderBy = (0, _mastra_core_storage.resolveListOrderBy)(args.orderBy, [
6140
+ "createdAt",
6141
+ "updatedAt",
6142
+ "name"
6143
+ ], {
6144
+ field: "createdAt",
6145
+ direction: "DESC"
6146
+ });
6121
6147
  const { page, perPage: perPageInput } = args.pagination;
6122
6148
  const tableName = getTableName$5({
6123
6149
  indexName: _mastra_core_storage.TABLE_DATASETS,
@@ -6173,7 +6199,7 @@ var DatasetsPG = class DatasetsPG extends _mastra_core_storage.DatasetsStorage {
6173
6199
  const { offset, perPage: perPageForResponse } = (0, _mastra_core_storage.calculatePagination)(page, perPageInput, perPage);
6174
6200
  const limitValue = perPageInput === false ? total : perPage;
6175
6201
  return {
6176
- datasets: (await this.#db.readClient.manyOrNone(`SELECT * FROM ${tableName} ${whereClause} ORDER BY "createdAt" DESC, "id" ASC LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`, [
6202
+ datasets: (await this.#db.readClient.manyOrNone(`SELECT * FROM ${tableName} ${whereClause} ORDER BY "${orderBy.field}" ${orderBy.direction}, "id" ASC LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`, [
6177
6203
  ...queryParams,
6178
6204
  limitValue,
6179
6205
  offset
@@ -6740,6 +6766,10 @@ var DatasetsPG = class DatasetsPG extends _mastra_core_storage.DatasetsStorage {
6740
6766
  */
6741
6767
  async #listItems(client, args) {
6742
6768
  try {
6769
+ const orderBy = (0, _mastra_core_storage.resolveListOrderBy)(args.orderBy, ["createdAt", "updatedAt"], {
6770
+ field: "createdAt",
6771
+ direction: "DESC"
6772
+ });
6743
6773
  const { page, perPage: perPageInput } = args.pagination;
6744
6774
  const tableName = getTableName$5({
6745
6775
  indexName: _mastra_core_storage.TABLE_DATASET_ITEMS,
@@ -6790,7 +6820,7 @@ var DatasetsPG = class DatasetsPG extends _mastra_core_storage.DatasetsStorage {
6790
6820
  const { offset, perPage: perPageForResponse } = (0, _mastra_core_storage.calculatePagination)(page, perPageInput, perPage);
6791
6821
  const limitValue = perPageInput === false ? total : perPage;
6792
6822
  return {
6793
- items: (await client.manyOrNone(`SELECT * FROM ${tableName} ${whereClause} ORDER BY "createdAt" DESC, "id" ASC LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`, [
6823
+ items: (await client.manyOrNone(`SELECT * FROM ${tableName} ${whereClause} ORDER BY "${orderBy.field}" ${orderBy.direction}, "id" ASC LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`, [
6794
6824
  ...queryParams,
6795
6825
  limitValue,
6796
6826
  offset
@@ -7350,6 +7380,10 @@ var ExperimentsPG = class ExperimentsPG extends _mastra_core_storage.Experiments
7350
7380
  }
7351
7381
  async listExperiments(args) {
7352
7382
  try {
7383
+ const orderBy = (0, _mastra_core_storage.resolveListOrderBy)(args.orderBy, ["createdAt", "status"], {
7384
+ field: "createdAt",
7385
+ direction: "DESC"
7386
+ });
7353
7387
  const { page, perPage: perPageInput } = args.pagination;
7354
7388
  const tableName = getTableName$5({
7355
7389
  indexName: _mastra_core_storage.TABLE_EXPERIMENTS,
@@ -7421,7 +7455,7 @@ var ExperimentsPG = class ExperimentsPG extends _mastra_core_storage.Experiments
7421
7455
  const { offset, perPage: perPageForResponse } = (0, _mastra_core_storage.calculatePagination)(page, perPageInput, perPage);
7422
7456
  const limitValue = perPageInput === false ? total : perPage;
7423
7457
  return {
7424
- experiments: (await this.#db.readClient.manyOrNone(`SELECT * FROM ${tableName} ${whereClause} ORDER BY "createdAt" DESC LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`, [
7458
+ experiments: (await this.#db.readClient.manyOrNone(`SELECT * FROM ${tableName} ${whereClause} ORDER BY "${orderBy.field}" ${orderBy.direction}, "id" ASC LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`, [
7425
7459
  ...queryParams,
7426
7460
  limitValue,
7427
7461
  offset
@@ -7693,6 +7727,10 @@ var ExperimentsPG = class ExperimentsPG extends _mastra_core_storage.Experiments
7693
7727
  }
7694
7728
  async listExperimentResults(args) {
7695
7729
  try {
7730
+ const orderBy = (0, _mastra_core_storage.resolveListOrderBy)(args.orderBy, ["startedAt", "createdAt"], {
7731
+ field: "startedAt",
7732
+ direction: "ASC"
7733
+ });
7696
7734
  const { page, perPage: perPageInput } = args.pagination;
7697
7735
  const tableName = getTableName$5({
7698
7736
  indexName: _mastra_core_storage.TABLE_EXPERIMENT_RESULTS,
@@ -7740,7 +7778,7 @@ var ExperimentsPG = class ExperimentsPG extends _mastra_core_storage.Experiments
7740
7778
  const { offset, perPage: perPageForResponse } = (0, _mastra_core_storage.calculatePagination)(page, perPageInput, perPage);
7741
7779
  const limitValue = perPageInput === false ? total : perPage;
7742
7780
  return {
7743
- results: (await this.#db.readClient.manyOrNone(`SELECT * FROM ${tableName} ${whereClause} ORDER BY "startedAt" ASC LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`, [
7781
+ results: (await this.#db.readClient.manyOrNone(`SELECT * FROM ${tableName} ${whereClause} ORDER BY "${orderBy.field}" ${orderBy.direction}, "id" ASC LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`, [
7744
7782
  ...queryParams,
7745
7783
  limitValue,
7746
7784
  offset
@@ -17279,7 +17317,7 @@ const TRACE_SELECT = `
17279
17317
  r."name" AS "name",
17280
17318
  r."entityId" AS "entityId",
17281
17319
  r."parentSpanId" AS "parentSpanId",
17282
- r."metadata" AS "metadata",
17320
+ r."metadataRaw" AS "metadata",
17283
17321
  r."input" AS "input",
17284
17322
  r."threadId" AS "threadId",
17285
17323
  r."resourceId" AS "resourceId",
@@ -17497,22 +17535,27 @@ function compileThreadPredicate(predicate, parameterOffset) {
17497
17535
  values: compiled.values
17498
17536
  };
17499
17537
  }
17500
- function compilePostgresTraceScope(schema, selection, relationCollections) {
17538
+ function compilePostgresTraceScope(schema, selection, relationCollections, deltaWindow) {
17501
17539
  const spanTable = qualifiedTable(schema, TABLE_SPAN_EVENTS);
17502
17540
  const scoreTable = qualifiedTable(schema, TABLE_SCORE_EVENTS);
17503
17541
  const feedbackTable = qualifiedTable(schema, TABLE_FEEDBACK_EVENTS);
17504
17542
  const values = [selection.timeRange.from, selection.timeRange.to];
17505
- const ctes = [`root_scope AS MATERIALIZED (
17506
- SELECT *
17507
- FROM ${spanTable} r
17508
- WHERE ${[
17543
+ const rootConditions = [
17509
17544
  `r."parentSpanId" IS NULL`,
17510
17545
  latestRootPredicate$1(spanTable),
17511
17546
  `NOT r."isPending"`,
17512
17547
  `r."endedAt" IS NOT NULL`,
17513
17548
  `r."startedAt" >= $1`,
17514
17549
  `r."startedAt" < $2`
17515
- ].join("\n AND ")}
17550
+ ];
17551
+ if (deltaWindow) {
17552
+ values.push(deltaWindow.xactId, deltaWindow.cursorId, deltaWindow.safeHorizon);
17553
+ rootConditions.push(`(r."xactId", r."cursorId") > ($3::xid8, $4::bigint)`, `r."xactId" < $5::xid8`);
17554
+ }
17555
+ const ctes = [`root_scope AS MATERIALIZED (
17556
+ SELECT *
17557
+ FROM ${spanTable} r
17558
+ WHERE ${rootConditions.join("\n AND ")}
17516
17559
  )`];
17517
17560
  if (relationCollections.has("spans")) ctes.push(`current_spans AS MATERIALIZED (
17518
17561
  SELECT
@@ -17581,8 +17624,18 @@ function compilePostgresTraceScope(schema, selection, relationCollections) {
17581
17624
  values
17582
17625
  };
17583
17626
  }
17584
- function compilePostgresTraceQuery(schema, plan, mode = "data") {
17585
- const { ctes, values } = compilePostgresTraceScope(schema, plan, collectRelationCollections(plan.where));
17627
+ function compilePostgresTraceQuery(schema, plan, mode = "data", safeHorizon) {
17628
+ const relationCollections = collectRelationCollections(plan.where);
17629
+ let deltaWindow;
17630
+ if (plan.paginationMode === "delta") {
17631
+ const watermark = _mastra_core_storage.getTraceQueryDeltaWatermark(plan, "pg");
17632
+ if (watermark === void 0 || safeHorizon === void 0) throw new Error("Delta query requires a cursor and safe horizon");
17633
+ deltaWindow = {
17634
+ ...decodeTraceDeltaWatermark(watermark),
17635
+ safeHorizon
17636
+ };
17637
+ }
17638
+ const { ctes, values } = compilePostgresTraceScope(schema, plan, relationCollections, deltaWindow);
17586
17639
  let predicateSql = "TRUE";
17587
17640
  if (plan.where) {
17588
17641
  const predicate = compilePredicate(plan.where, values.length + 1);
@@ -17590,7 +17643,7 @@ function compilePostgresTraceQuery(schema, plan, mode = "data") {
17590
17643
  values.push(...predicate.values);
17591
17644
  }
17592
17645
  ctes.push(`candidates AS (
17593
- SELECT ${TRACE_SELECT}
17646
+ SELECT ${TRACE_SELECT}${plan.paginationMode === "delta" ? ", r.\"xactId\", r.\"cursorId\"" : ""}
17594
17647
  FROM root_scope r
17595
17648
  WHERE ${predicateSql}
17596
17649
  )`);
@@ -17601,6 +17654,16 @@ SELECT COUNT(*)::text AS count
17601
17654
  FROM candidates`,
17602
17655
  values
17603
17656
  };
17657
+ if (plan.paginationMode === "delta") {
17658
+ values.push(plan.limit + 1);
17659
+ return {
17660
+ text: `${candidates}
17661
+ SELECT * FROM candidates
17662
+ ORDER BY "xactId" ASC, "cursorId" ASC
17663
+ LIMIT $${values.length}`,
17664
+ values
17665
+ };
17666
+ }
17604
17667
  if (plan.result === "groups") {
17605
17668
  const pageCondition = plan.cursor ? `AND "threadId" > $${values.length + 1}` : "";
17606
17669
  if (plan.cursor) values.push(plan.cursor.threadId);
@@ -17812,13 +17875,83 @@ async function getTraceQueryValues(client, schema, plan, timeoutMs) {
17812
17875
  valuesTruncated: rows.length > plan.limit
17813
17876
  });
17814
17877
  }
17878
+ function decodeTraceDeltaWatermark(watermark) {
17879
+ try {
17880
+ return decodeDeltaCursor(watermark);
17881
+ } catch {
17882
+ throw new _mastra_core_storage.TraceQueryCursorError("TRACE_QUERY_CURSOR_MALFORMED");
17883
+ }
17884
+ }
17885
+ function emptyDeltaWatermark(horizon, watermark) {
17886
+ if (watermark !== void 0 && BigInt(decodeTraceDeltaWatermark(watermark).xactId) >= BigInt(horizon)) return watermark;
17887
+ return encodeDeltaCursor(horizon, 0);
17888
+ }
17889
+ async function setRemainingTimeout(transaction, deadline) {
17890
+ const remainingTimeoutMs = Math.floor(deadline - performance.now());
17891
+ if (remainingTimeoutMs <= 0) throw new _mastra_core_storage.TraceQueryExecutionError();
17892
+ await transaction.query(`SELECT set_config('statement_timeout', $1, true)`, [`${remainingTimeoutMs}ms`]);
17893
+ }
17894
+ function traceRowToResult(row) {
17895
+ return {
17896
+ traceId: String(row.traceId),
17897
+ rootSpanId: String(row.rootSpanId),
17898
+ name: row.name,
17899
+ entityId: row.entityId ?? null,
17900
+ parentSpanId: row.parentSpanId ?? null,
17901
+ createdAt: asIsoTimestamp$1(row.startedAt),
17902
+ metadata: row.metadata ?? null,
17903
+ inputPreview: _mastra_core_storage.buildInputPreview(row.input) ?? null,
17904
+ threadId: row.threadId == null ? null : String(row.threadId),
17905
+ resourceId: row.resourceId == null ? null : String(row.resourceId),
17906
+ startedAt: asIsoTimestamp$1(row.startedAt),
17907
+ endedAt: asIsoTimestamp$1(row.endedAt),
17908
+ entityName: row.entityName == null ? null : String(row.entityName),
17909
+ entityType: row.entityType == null ? null : String(row.entityType),
17910
+ environment: row.environment == null ? null : String(row.environment),
17911
+ status: row.status
17912
+ };
17913
+ }
17815
17914
  async function queryTraces(client, schema, plan, timeoutMs) {
17915
+ if (plan.paginationMode === "delta") {
17916
+ assertDeltaPollingEnabled();
17917
+ const watermark = _mastra_core_storage.getTraceQueryDeltaWatermark(plan, "pg");
17918
+ if (watermark !== void 0) decodeTraceDeltaWatermark(watermark);
17919
+ const resolvedTimeoutMs = _mastra_core_storage.resolveTraceQueryTimeoutMs(timeoutMs);
17920
+ const deadline = performance.now() + resolvedTimeoutMs;
17921
+ return runWithPostgresTraceQueryTimeout(client, resolvedTimeoutMs, async (transaction) => {
17922
+ await setRemainingTimeout(transaction, deadline);
17923
+ const horizon = await readSafeXactHorizon(transaction);
17924
+ let rows = [];
17925
+ if (watermark !== void 0) {
17926
+ await setRemainingTimeout(transaction, deadline);
17927
+ const query = compilePostgresTraceQuery(schema, plan, "data", horizon);
17928
+ rows = await transaction.any(query.text, query.values);
17929
+ }
17930
+ const visible = rows.slice(0, plan.limit);
17931
+ const last = visible.at(-1);
17932
+ return _mastra_core_storage.traceQueryResponseSchema.parse({
17933
+ traces: visible.map(traceRowToResult),
17934
+ delta: {
17935
+ limit: plan.limit,
17936
+ hasMore: rows.length > plan.limit
17937
+ },
17938
+ deltaCursor: _mastra_core_storage.encodeTraceQueryDeltaCursor(plan, "pg", last ? encodeDeltaCursor(last.xactId, last.cursorId) : emptyDeltaWatermark(horizon, watermark))
17939
+ });
17940
+ }, { repeatableRead: true });
17941
+ }
17816
17942
  if (plan.paginationMode === "page") {
17817
17943
  const resolvedTimeoutMs = _mastra_core_storage.resolveTraceQueryTimeoutMs(timeoutMs);
17818
17944
  const deadline = performance.now() + resolvedTimeoutMs;
17819
17945
  const countQuery = compilePostgresTraceQuery(schema, plan, "count");
17820
17946
  const dataQuery = compilePostgresTraceQuery(schema, plan);
17821
- const { total, rows } = await runWithPostgresTraceQueryTimeout(client, resolvedTimeoutMs, async (transaction) => {
17947
+ const { total, rows, deltaCursor } = await runWithPostgresTraceQueryTimeout(client, resolvedTimeoutMs, async (transaction) => {
17948
+ let deltaCursor;
17949
+ if (deltaPollingFeatureEnabled() && typeof _mastra_core_storage.encodeTraceQueryDeltaCursor === "function") {
17950
+ await setRemainingTimeout(transaction, deadline);
17951
+ const horizon = await readSafeXactHorizon(transaction);
17952
+ deltaCursor = _mastra_core_storage.encodeTraceQueryDeltaCursor(plan, "pg", encodeDeltaCursor(horizon, 0));
17953
+ }
17954
+ if (deltaCursor !== void 0) await setRemainingTimeout(transaction, deadline);
17822
17955
  const countRows = await transaction.any(countQuery.text, countQuery.values);
17823
17956
  const remainingTimeoutMs = Math.floor(deadline - performance.now());
17824
17957
  if (remainingTimeoutMs <= 0) throw new _mastra_core_storage.TraceQueryExecutionError();
@@ -17826,29 +17959,14 @@ async function queryTraces(client, schema, plan, timeoutMs) {
17826
17959
  const rows = await transaction.any(dataQuery.text, dataQuery.values);
17827
17960
  return {
17828
17961
  total: Number(countRows[0]?.count ?? 0),
17829
- rows
17962
+ rows,
17963
+ deltaCursor
17830
17964
  };
17831
17965
  }, { repeatableRead: true });
17832
- const traces = rows.map((row) => ({
17833
- traceId: String(row.traceId),
17834
- rootSpanId: String(row.rootSpanId),
17835
- name: row.name,
17836
- entityId: row.entityId ?? null,
17837
- parentSpanId: row.parentSpanId ?? null,
17838
- createdAt: asIsoTimestamp$1(row.startedAt),
17839
- metadata: row.metadata ?? null,
17840
- inputPreview: _mastra_core_storage.buildInputPreview(row.input) ?? null,
17841
- threadId: row.threadId == null ? null : String(row.threadId),
17842
- resourceId: row.resourceId == null ? null : String(row.resourceId),
17843
- startedAt: asIsoTimestamp$1(row.startedAt),
17844
- endedAt: asIsoTimestamp$1(row.endedAt),
17845
- entityName: row.entityName == null ? null : String(row.entityName),
17846
- entityType: row.entityType == null ? null : String(row.entityType),
17847
- environment: row.environment == null ? null : String(row.environment),
17848
- status: row.status
17849
- }));
17966
+ const traces = rows.map(traceRowToResult);
17850
17967
  return _mastra_core_storage.traceQueryResponseSchema.parse({
17851
17968
  traces,
17969
+ ...deltaCursor === void 0 ? {} : { deltaCursor },
17852
17970
  pagination: {
17853
17971
  total,
17854
17972
  page: plan.page,
@@ -17871,24 +17989,7 @@ async function queryTraces(client, schema, plan, timeoutMs) {
17871
17989
  }) : null }
17872
17990
  });
17873
17991
  }
17874
- const traces = visibleRows.map((row) => ({
17875
- traceId: String(row.traceId),
17876
- rootSpanId: String(row.rootSpanId),
17877
- name: row.name,
17878
- entityId: row.entityId ?? null,
17879
- parentSpanId: row.parentSpanId ?? null,
17880
- createdAt: asIsoTimestamp$1(row.startedAt),
17881
- metadata: row.metadata ?? null,
17882
- inputPreview: _mastra_core_storage.buildInputPreview(row.input) ?? null,
17883
- threadId: row.threadId == null ? null : String(row.threadId),
17884
- resourceId: row.resourceId == null ? null : String(row.resourceId),
17885
- startedAt: asIsoTimestamp$1(row.startedAt),
17886
- endedAt: asIsoTimestamp$1(row.endedAt),
17887
- entityName: row.entityName == null ? null : String(row.entityName),
17888
- entityType: row.entityType == null ? null : String(row.entityType),
17889
- environment: row.environment == null ? null : String(row.environment),
17890
- status: row.status
17891
- }));
17992
+ const traces = visibleRows.map(traceRowToResult);
17892
17993
  const last = traces.at(-1);
17893
17994
  return _mastra_core_storage.traceQueryResponseSchema.parse({
17894
17995
  traces,
@@ -18627,7 +18728,7 @@ async function dangerouslyClearTracing(client, schema) {
18627
18728
  * Use it through `MastraCompositeStore` with a dedicated Postgres connection.
18628
18729
  */
18629
18730
  function wrapError(op, error, details) {
18630
- if (error instanceof _mastra_core_error.MastraError || error instanceof _mastra_core_storage.TraceQueryExecutionError || error instanceof _mastra_core_storage.TraceQueryResourceLimitError) throw error;
18731
+ 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;
18631
18732
  throw new _mastra_core_error.MastraError({
18632
18733
  id: (0, _mastra_core_storage.createStorageErrorId)("PG", op, "FAILED"),
18633
18734
  domain: _mastra_core_error.ErrorDomain.STORAGE,
@@ -22317,6 +22418,7 @@ const WORKFLOW_SNAPSHOT_STATUS_INDEX = "mastra_workflow_snapshot_name_status_cre
22317
22418
  * Schema-prefixed name of the status index, lowercased and truncated the same way Postgres
22318
22419
  * stores it, so the init snapshot's index set answers "does it exist?" without a probe or a
22319
22420
  * no-op `CREATE INDEX` (schema-prefixed names routinely exceed the 63-byte limit).
22421
+ * Exported for tests.
22320
22422
  */
22321
22423
  function workflowSnapshotStatusIndexName(schemaName) {
22322
22424
  return buildConstraintName({
@@ -22334,6 +22436,48 @@ function workflowSnapshotStatusIndexSQL(indexName, schemaName) {
22334
22436
  schemaName: getSchemaName(schemaName)
22335
22437
  })} (workflow_name, (snapshot ->> 'status'), "createdAt" DESC)`;
22336
22438
  }
22439
+ /** Base name (before any schema prefix) of the expression index backing the threadId filter. */
22440
+ const WORKFLOW_SNAPSHOT_THREAD_ID_INDEX = "mastra_workflow_snapshot_threadid_idx";
22441
+ /**
22442
+ * Schema-prefixed name of the threadId index (see workflowSnapshotStatusIndexName).
22443
+ *
22444
+ * Unlike the status index, truncation appends a collision hash: both index names share the
22445
+ * long `<schema>_mastra_workflow_snapshot_` prefix, so with a schema name of 37+ bytes plain
22446
+ * truncation collapses them to the same 63-byte identifier and `CREATE INDEX IF NOT EXISTS`
22447
+ * silently skips this index. The status index keeps plain truncation because its truncated
22448
+ * name already exists in deployed catalogs; this index is new and free to adopt the rule.
22449
+ * Exported for tests.
22450
+ */
22451
+ function workflowSnapshotThreadIdIndexName(schemaName) {
22452
+ return buildConstraintName({
22453
+ baseName: WORKFLOW_SNAPSHOT_THREAD_ID_INDEX,
22454
+ schemaName: schemaName && schemaName !== "public" ? schemaName : void 0,
22455
+ hashWhenTruncated: true
22456
+ });
22457
+ }
22458
+ /**
22459
+ * Expression extracting the thread id embedded in a snapshot (jsonb columns only). Mirrors
22460
+ * the canonical extraction in `@mastra/core` (`getSnapshotMemoryInfo`), which reads one of
22461
+ * two layouts:
22462
+ * 1. agentic-loop: `context.<suspended step>.suspendPayload.__streamState.messageList.memoryInfo.threadId`
22463
+ * 2. durable loop: `context.input.messageListState.memoryInfo.threadId`
22464
+ *
22465
+ * `jsonb_path_query_first(jsonb, jsonpath)` is IMMUTABLE, so the expression is valid in an
22466
+ * expression index. The WHERE clause in listWorkflowRuns() must use this exact expression
22467
+ * text so the planner can match it against the index. If the snapshot layout changes in
22468
+ * core, this expression must be updated in lockstep or it will wrongly exclude rows.
22469
+ */
22470
+ const WORKFLOW_SNAPSHOT_THREAD_ID_EXPR = `COALESCE(jsonb_path_query_first(snapshot, '$.context.* ? (@.status == "suspended").suspendPayload.__streamState.messageList.memoryInfo.threadId') #>> '{}', snapshot #>> '{context,input,messageListState,memoryInfo,threadId}')`;
22471
+ /**
22472
+ * Expression index on the snapshot-embedded thread id so listWorkflowRuns() threadId filters
22473
+ * (Agent.listSuspendedRuns) can use an index instead of detoasting every snapshot.
22474
+ */
22475
+ function workflowSnapshotThreadIdIndexSQL(indexName, schemaName) {
22476
+ return `CREATE INDEX IF NOT EXISTS "${indexName}" ON ${getTableName({
22477
+ indexName: _mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT,
22478
+ schemaName: getSchemaName(schemaName)
22479
+ })} ((${WORKFLOW_SNAPSHOT_THREAD_ID_EXPR}))`;
22480
+ }
22337
22481
  var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorage {
22338
22482
  #db;
22339
22483
  #schema;
@@ -22406,6 +22550,7 @@ var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorag
22406
22550
  }));
22407
22551
  for (const idx of WorkflowsPG.getDefaultIndexDefs(schemaPrefix)) statements.push(generateIndexSQL(idx, schemaName));
22408
22552
  statements.push(`${workflowSnapshotStatusIndexSQL(workflowSnapshotStatusIndexName(parsedSchema), schemaName)};`);
22553
+ statements.push(`${workflowSnapshotThreadIdIndexSQL(workflowSnapshotThreadIdIndexName(parsedSchema), schemaName)};`);
22409
22554
  return statements;
22410
22555
  }
22411
22556
  /**
@@ -22432,6 +22577,12 @@ var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorag
22432
22577
  } catch (error) {
22433
22578
  this.logger?.warn?.(`Failed to create index ${indexName}:`, error);
22434
22579
  }
22580
+ const threadIdIndexName = workflowSnapshotThreadIdIndexName(this.#schema);
22581
+ try {
22582
+ await this.#db.createIndexFromStatement(threadIdIndexName, workflowSnapshotThreadIdIndexSQL(threadIdIndexName, this.#schema));
22583
+ } catch (error) {
22584
+ this.logger?.warn?.(`Failed to create index ${threadIdIndexName}:`, error);
22585
+ }
22435
22586
  }
22436
22587
  async init() {
22437
22588
  await this.#db.createTable({
@@ -22712,7 +22863,7 @@ var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorag
22712
22863
  }, error);
22713
22864
  }
22714
22865
  }
22715
- async listWorkflowRuns({ workflowName, fromDate, toDate, perPage, page, resourceId, status } = {}) {
22866
+ async listWorkflowRuns({ workflowName, fromDate, toDate, perPage, page, resourceId, threadId, status } = {}) {
22716
22867
  try {
22717
22868
  const conditions = [];
22718
22869
  const values = [];
@@ -22733,6 +22884,11 @@ var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorag
22733
22884
  values.push(resourceId);
22734
22885
  paramIndex++;
22735
22886
  } else this.logger?.warn?.(`[${_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT}] resourceId column not found. Skipping resourceId filter.`);
22887
+ if (threadId) if (await this.#db.getColumnType(_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT, "snapshot") === "jsonb") {
22888
+ conditions.push(`${WORKFLOW_SNAPSHOT_THREAD_ID_EXPR} = $${paramIndex}`);
22889
+ values.push(threadId);
22890
+ paramIndex++;
22891
+ } else this.logger?.warn?.(`[${_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT}] snapshot column is not jsonb. Skipping threadId filter.`);
22736
22892
  if (fromDate) {
22737
22893
  conditions.push(`"createdAt" >= $${paramIndex}`);
22738
22894
  values.push(fromDate);