@mastra/pg 1.7.2 → 1.8.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 (32) hide show
  1. package/CHANGELOG.md +124 -0
  2. package/dist/docs/SKILL.md +14 -14
  3. package/dist/docs/assets/SOURCE_MAP.json +1 -1
  4. package/dist/docs/references/docs-memory-semantic-recall.md +9 -9
  5. package/dist/docs/references/docs-memory-storage.md +1 -1
  6. package/dist/docs/references/docs-memory-working-memory.md +20 -20
  7. package/dist/docs/references/docs-rag-overview.md +2 -2
  8. package/dist/docs/references/docs-rag-retrieval.md +4 -4
  9. package/dist/docs/references/docs-rag-vector-databases.md +13 -13
  10. package/dist/docs/references/reference-memory-memory-class.md +1 -1
  11. package/dist/docs/references/reference-processors-message-history-processor.md +1 -1
  12. package/dist/docs/references/reference-rag-metadata-filters.md +15 -15
  13. package/dist/docs/references/reference-storage-composite.md +1 -1
  14. package/dist/docs/references/reference-storage-dynamodb.md +7 -7
  15. package/dist/docs/references/reference-storage-postgresql.md +7 -7
  16. package/dist/docs/references/reference-tools-vector-query-tool.md +12 -12
  17. package/dist/docs/references/reference-vectors-pg.md +23 -21
  18. package/dist/index.cjs +379 -91
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.js +379 -91
  21. package/dist/index.js.map +1 -1
  22. package/dist/storage/db/index.d.ts +13 -0
  23. package/dist/storage/db/index.d.ts.map +1 -1
  24. package/dist/storage/domains/datasets/index.d.ts.map +1 -1
  25. package/dist/storage/domains/memory/index.d.ts +7 -2
  26. package/dist/storage/domains/memory/index.d.ts.map +1 -1
  27. package/dist/storage/domains/observability/index.d.ts.map +1 -1
  28. package/dist/vector/index.d.ts +44 -10
  29. package/dist/vector/index.d.ts.map +1 -1
  30. package/dist/vector/types.d.ts +32 -2
  31. package/dist/vector/types.d.ts.map +1 -1
  32. package/package.json +7 -7
package/dist/index.js CHANGED
@@ -724,11 +724,22 @@ var PgVector = class extends MastraVector {
724
724
  }
725
725
  return major > 0 || major === 0 && minor >= 7;
726
726
  }
727
+ /** Checks if pgvector >= 0.7.0 (required for bit type). */
728
+ supportsBit() {
729
+ return this.supportsHalfvec();
730
+ }
731
+ /** Checks if pgvector >= 0.7.0 (required for sparsevec type). */
732
+ supportsSparsevec() {
733
+ return this.supportsHalfvec();
734
+ }
727
735
  /**
728
736
  * Gets the properly qualified vector type name
729
- * @param vectorType - The type of vector storage ('vector' or 'halfvec')
737
+ * @param vectorType - The type of vector storage
730
738
  */
731
- getVectorTypeName(vectorType = "vector") {
739
+ getVectorTypeName(vectorType = "vector", dimension) {
740
+ if (vectorType === "bit") {
741
+ return dimension ? `bit(${dimension})` : "bit";
742
+ }
732
743
  if (this.vectorExtensionSchema) {
733
744
  if (this.vectorExtensionSchema === "pg_catalog") {
734
745
  return vectorType;
@@ -739,20 +750,76 @@ var PgVector = class extends MastraVector {
739
750
  return vectorType;
740
751
  }
741
752
  /**
742
- * Gets the operator class for index creation based on metric and vector type.
743
- * pgvector uses different operator classes for vector vs halfvec types.
753
+ * Returns the operator class, distance operator, and score expression for a
754
+ * standard (non-bit) vector type prefix and metric.
744
755
  */
745
- getMetricOperatorClass(metric, vectorType) {
746
- const prefix = vectorType === "halfvec" ? "halfvec" : "vector";
756
+ getMetricOps(prefix, metric) {
747
757
  switch (metric) {
748
- case "cosine":
749
- return `${prefix}_cosine_ops`;
750
758
  case "euclidean":
751
- return `${prefix}_l2_ops`;
759
+ return {
760
+ operatorClass: `${prefix}_l2_ops`,
761
+ distanceOperator: "<->",
762
+ scoreExpr: (d) => `1.0 / (1.0 + (${d}))`
763
+ };
752
764
  case "dotproduct":
753
- return `${prefix}_ip_ops`;
765
+ return {
766
+ operatorClass: `${prefix}_ip_ops`,
767
+ distanceOperator: "<#>",
768
+ scoreExpr: (d) => `(${d}) * -1`
769
+ };
770
+ default:
771
+ return {
772
+ operatorClass: `${prefix}_cosine_ops`,
773
+ distanceOperator: "<=>",
774
+ scoreExpr: (d) => `1 - (${d})`
775
+ };
776
+ }
777
+ }
778
+ /**
779
+ * Returns all vector-type-specific operations for the given vectorType and metric.
780
+ */
781
+ getVectorOps(vectorType, metric) {
782
+ switch (vectorType) {
783
+ case "bit":
784
+ return {
785
+ operatorClass: metric === "jaccard" ? "bit_jaccard_ops" : "bit_hamming_ops",
786
+ distanceOperator: metric === "jaccard" ? "<%>" : "<~>",
787
+ scoreExpr: (d) => metric === "jaccard" ? `1 - (${d})` : `1 - ((${d})::float / bit_length(embedding))`,
788
+ formatVector: (v) => v.map((x) => x ? "1" : "0").join(""),
789
+ parseEmbedding: (e) => e.split("").map((c) => c === "1" ? 1 : 0)
790
+ };
791
+ case "sparsevec":
792
+ return {
793
+ ...this.getMetricOps("sparsevec", metric),
794
+ formatVector: (v, dimension) => {
795
+ const dim = dimension ?? v.length;
796
+ const nonZero = v.map((val, i) => val !== 0 ? `${i + 1}:${val}` : null).filter(Boolean).join(",");
797
+ return `{${nonZero}}/${dim}`;
798
+ },
799
+ parseEmbedding: (e) => {
800
+ const match = e.match(/^\{([^}]*)\}\/(\d+)$/);
801
+ if (!match) return [];
802
+ const dim = parseInt(match[2], 10);
803
+ const result = new Array(dim).fill(0);
804
+ const entries = match[1];
805
+ if (entries.trim()) {
806
+ for (const entry of entries.split(",")) {
807
+ const [idxStr, valStr] = entry.split(":");
808
+ const idx = parseInt(idxStr.trim(), 10) - 1;
809
+ result[idx] = parseFloat(valStr.trim());
810
+ }
811
+ }
812
+ return result;
813
+ }
814
+ };
815
+ case "halfvec":
816
+ case "vector":
754
817
  default:
755
- return `${prefix}_cosine_ops`;
818
+ return {
819
+ ...this.getMetricOps(vectorType === "halfvec" ? "halfvec" : "vector", metric),
820
+ formatVector: (v) => `[${v.join(",")}]`,
821
+ parseEmbedding: (e) => JSON.parse(e)
822
+ };
756
823
  }
757
824
  }
758
825
  getTableName(indexName) {
@@ -855,10 +922,12 @@ var PgVector = class extends MastraVector {
855
922
  const client = await this.pool.connect();
856
923
  try {
857
924
  await client.query("BEGIN");
858
- const vectorStr = `[${queryVector.join(",")}]`;
859
925
  const translatedFilter = this.transformFilter(filter);
860
926
  const { sql: filterQuery, values: filterValues } = buildFilterQuery(translatedFilter, minScore, topK);
861
927
  const indexInfo = await this.getIndexInfo({ indexName });
928
+ const metric = indexInfo.metric ?? "cosine";
929
+ const ops = this.getVectorOps(indexInfo.vectorType, metric);
930
+ const vectorStr = ops.formatVector(queryVector, indexInfo.dimension);
862
931
  if (indexInfo.type === "hnsw") {
863
932
  const calculatedEf = ef ?? Math.max(topK, (indexInfo?.config?.m ?? 16) * topK);
864
933
  const searchEf = Math.min(1e3, Math.max(1, calculatedEf));
@@ -868,12 +937,14 @@ var PgVector = class extends MastraVector {
868
937
  await client.query(`SET LOCAL ivfflat.probes = ${probes}`);
869
938
  }
870
939
  const { tableName } = this.getTableName(indexName);
871
- const qualifiedVectorType = this.getVectorTypeName(indexInfo.vectorType);
940
+ const qualifiedVectorType = this.getVectorTypeName(indexInfo.vectorType, indexInfo.dimension);
941
+ const distanceExpr = `embedding ${ops.distanceOperator} '${vectorStr}'::${qualifiedVectorType}`;
942
+ const scoreExpr = ops.scoreExpr(distanceExpr);
872
943
  const query = `
873
944
  WITH vector_scores AS (
874
945
  SELECT
875
946
  vector_id as id,
876
- 1 - (embedding <=> '${vectorStr}'::${qualifiedVectorType}) as score,
947
+ ${scoreExpr} as score,
877
948
  metadata
878
949
  ${includeVector ? ", embedding" : ""}
879
950
  FROM ${tableName}
@@ -890,7 +961,7 @@ var PgVector = class extends MastraVector {
890
961
  id,
891
962
  score,
892
963
  metadata,
893
- ...includeVector && embedding && { vector: JSON.parse(embedding) }
964
+ ...includeVector && embedding && { vector: ops.parseEmbedding(embedding) }
894
965
  }));
895
966
  } catch (error) {
896
967
  await client.query("ROLLBACK");
@@ -939,8 +1010,10 @@ var PgVector = class extends MastraVector {
939
1010
  }
940
1011
  const vectorIds = ids || vectors.map(() => crypto.randomUUID());
941
1012
  const indexInfo = await this.getIndexInfo({ indexName });
942
- const qualifiedVectorType = this.getVectorTypeName(indexInfo.vectorType);
1013
+ const qualifiedVectorType = this.getVectorTypeName(indexInfo.vectorType, indexInfo.dimension);
1014
+ const ops = this.getVectorOps(indexInfo.vectorType, indexInfo.metric ?? "cosine");
943
1015
  for (let i = 0; i < vectors.length; i++) {
1016
+ const vectorStr = ops.formatVector(vectors[i], indexInfo.dimension);
944
1017
  const query = `
945
1018
  INSERT INTO ${tableName} (vector_id, embedding, metadata)
946
1019
  VALUES ($1, $2::${qualifiedVectorType}, $3::jsonb)
@@ -950,7 +1023,7 @@ var PgVector = class extends MastraVector {
950
1023
  metadata = $3::jsonb
951
1024
  RETURNING embedding::text
952
1025
  `;
953
- await client.query(query, [vectorIds[i], `[${vectors[i]?.join(",")}]`, JSON.stringify(metadata?.[i] || {})]);
1026
+ await client.query(query, [vectorIds[i], vectorStr, JSON.stringify(metadata?.[i] || {})]);
954
1027
  }
955
1028
  await client.query("COMMIT");
956
1029
  this.logger?.debug(`Upserted ${vectors.length} vectors to ${indexName}`, {
@@ -1006,9 +1079,17 @@ var PgVector = class extends MastraVector {
1006
1079
  dimension,
1007
1080
  metric,
1008
1081
  type,
1009
- vectorType = "vector"
1082
+ vectorType = "vector",
1083
+ metadataIndexes
1010
1084
  }) {
1011
- const input = indexName + dimension + metric + (type || "ivfflat") + vectorType;
1085
+ const input = JSON.stringify([
1086
+ indexName,
1087
+ dimension,
1088
+ metric,
1089
+ type || "ivfflat",
1090
+ vectorType,
1091
+ metadataIndexes?.toSorted() ?? []
1092
+ ]);
1012
1093
  return (await this.hasher).h32(input);
1013
1094
  }
1014
1095
  cachedIndexExists(indexName, newKey) {
@@ -1059,11 +1140,13 @@ var PgVector = class extends MastraVector {
1059
1140
  async createIndex({
1060
1141
  indexName,
1061
1142
  dimension,
1062
- metric = "cosine",
1143
+ metric: rawMetric = "cosine",
1063
1144
  indexConfig = {},
1064
1145
  buildIndex = true,
1065
- vectorType = "vector"
1146
+ vectorType = "vector",
1147
+ metadataIndexes
1066
1148
  }) {
1149
+ const metric = vectorType === "bit" && rawMetric !== "hamming" && rawMetric !== "jaccard" ? "hamming" : rawMetric;
1067
1150
  const { tableName } = this.getTableName(indexName);
1068
1151
  try {
1069
1152
  if (!indexName.match(/^[a-zA-Z_][a-zA-Z0-9_]*$/)) {
@@ -1072,8 +1155,17 @@ var PgVector = class extends MastraVector {
1072
1155
  if (!Number.isInteger(dimension) || dimension <= 0) {
1073
1156
  throw new Error("Dimension must be a positive integer");
1074
1157
  }
1075
- if (vectorType !== "vector" && vectorType !== "halfvec") {
1076
- throw new Error('vectorType must be "vector" or "halfvec"');
1158
+ if (vectorType !== "vector" && vectorType !== "halfvec" && vectorType !== "bit" && vectorType !== "sparsevec") {
1159
+ throw new Error('vectorType must be "vector", "halfvec", "bit", or "sparsevec"');
1160
+ }
1161
+ if (vectorType === "bit" && dimension > 64e3) {
1162
+ throw new Error("bit vectors support up to 64,000 dimensions for indexes");
1163
+ }
1164
+ if ((metric === "hamming" || metric === "jaccard") && vectorType !== "bit") {
1165
+ throw new Error(`${metric} metric is only valid with vectorType 'bit'`);
1166
+ }
1167
+ if (indexConfig?.type === "ivfflat" && vectorType === "bit" && metric === "jaccard") {
1168
+ throw new Error("IVFFlat indexes do not support Jaccard distance for bit vectors. Use HNSW instead.");
1077
1169
  }
1078
1170
  } catch (error) {
1079
1171
  const mastraError = new MastraError(
@@ -1095,7 +1187,8 @@ var PgVector = class extends MastraVector {
1095
1187
  dimension,
1096
1188
  type: indexConfig.type,
1097
1189
  metric,
1098
- vectorType
1190
+ vectorType,
1191
+ metadataIndexes
1099
1192
  });
1100
1193
  if (this.cachedIndexExists(indexName, indexCacheKey)) {
1101
1194
  return;
@@ -1123,6 +1216,33 @@ var PgVector = class extends MastraVector {
1123
1216
  }
1124
1217
  });
1125
1218
  }
1219
+ if (vectorType === "bit" && !this.supportsBit()) {
1220
+ throw new MastraError({
1221
+ id: createVectorErrorId("PG", "CREATE_INDEX", "VECTOR_TYPE_NOT_SUPPORTED"),
1222
+ text: `${vectorType} type requires pgvector >= 0.7.0, but version ${this.vectorExtensionVersion || "unknown"} is installed. Either upgrade pgvector or use vectorType: 'vector'.`,
1223
+ domain: ErrorDomain.MASTRA_VECTOR,
1224
+ category: ErrorCategory.USER,
1225
+ details: {
1226
+ indexName,
1227
+ vectorType,
1228
+ installedVersion: this.vectorExtensionVersion
1229
+ }
1230
+ });
1231
+ }
1232
+ if (vectorType === "sparsevec" && !this.supportsSparsevec()) {
1233
+ throw new MastraError({
1234
+ id: createVectorErrorId("PG", "CREATE_INDEX", "VECTOR_TYPE_NOT_SUPPORTED"),
1235
+ text: `${vectorType} type requires pgvector >= 0.7.0, but version ${this.vectorExtensionVersion || "unknown"} is installed. Either upgrade pgvector or use vectorType: 'vector'.`,
1236
+ domain: ErrorDomain.MASTRA_VECTOR,
1237
+ category: ErrorCategory.USER,
1238
+ details: {
1239
+ indexName,
1240
+ requestedVectorType: vectorType,
1241
+ pgvectorVersion: this.vectorExtensionVersion || "unknown",
1242
+ requiredVersion: "0.7.0"
1243
+ }
1244
+ });
1245
+ }
1126
1246
  if (this.schema && this.vectorExtensionSchema && this.schema !== this.vectorExtensionSchema && this.vectorExtensionSchema !== "pg_catalog") {
1127
1247
  await client.query(`SET search_path TO ${this.getSchemaName()}, "${this.vectorExtensionSchema}"`);
1128
1248
  }
@@ -1140,6 +1260,9 @@ var PgVector = class extends MastraVector {
1140
1260
  if (buildIndex) {
1141
1261
  await this.setupIndex({ indexName, metric, indexConfig, vectorType }, client);
1142
1262
  }
1263
+ if (metadataIndexes?.length) {
1264
+ await this.createMetadataIndexes(tableName, indexName, metadataIndexes);
1265
+ }
1143
1266
  } catch (error) {
1144
1267
  this.createdIndexes.delete(indexName);
1145
1268
  this.indexVectorTypes.delete(indexName);
@@ -1163,10 +1286,10 @@ var PgVector = class extends MastraVector {
1163
1286
  throw mastraError;
1164
1287
  });
1165
1288
  }
1166
- async buildIndex({ indexName, metric = "cosine", indexConfig }) {
1289
+ async buildIndex({ indexName, metric = "cosine", indexConfig, vectorType }) {
1167
1290
  const client = await this.pool.connect();
1168
1291
  try {
1169
- await this.setupIndex({ indexName, metric, indexConfig }, client);
1292
+ await this.setupIndex({ indexName, metric, indexConfig, vectorType }, client);
1170
1293
  } catch (error) {
1171
1294
  const mastraError = new MastraError(
1172
1295
  {
@@ -1189,7 +1312,26 @@ var PgVector = class extends MastraVector {
1189
1312
  const mutex = this.getMutexByName(`build-${indexName}`);
1190
1313
  await mutex.runExclusive(async () => {
1191
1314
  const isConfigEmpty = !indexConfig || Object.keys(indexConfig).length === 0 || !indexConfig.type && !indexConfig.ivf && !indexConfig.hnsw;
1192
- const indexType = isConfigEmpty ? "ivfflat" : indexConfig.type || "ivfflat";
1315
+ const defaultIndexType = vectorType === "sparsevec" ? "hnsw" : "ivfflat";
1316
+ const indexType = isConfigEmpty ? defaultIndexType : indexConfig.type || defaultIndexType;
1317
+ if (indexType === "ivfflat" && vectorType === "sparsevec") {
1318
+ throw new MastraError({
1319
+ id: createVectorErrorId("PG", "BUILD_INDEX", "UNSUPPORTED_INDEX_TYPE"),
1320
+ text: `IVFFlat indexes do not support sparsevec type. Use HNSW instead.`,
1321
+ domain: ErrorDomain.MASTRA_VECTOR,
1322
+ category: ErrorCategory.USER,
1323
+ details: { indexName, vectorType, indexType }
1324
+ });
1325
+ }
1326
+ if (indexType === "ivfflat" && vectorType === "bit" && metric === "jaccard") {
1327
+ throw new MastraError({
1328
+ id: createVectorErrorId("PG", "BUILD_INDEX", "UNSUPPORTED_INDEX_TYPE"),
1329
+ text: `IVFFlat indexes do not support Jaccard distance for bit vectors. Use HNSW instead.`,
1330
+ domain: ErrorDomain.MASTRA_VECTOR,
1331
+ category: ErrorCategory.USER,
1332
+ details: { indexName, vectorType, indexType, metric }
1333
+ });
1334
+ }
1193
1335
  const { tableName, vectorIndexName } = this.getTableName(indexName);
1194
1336
  let existingIndexInfo = null;
1195
1337
  let dimension = 0;
@@ -1247,7 +1389,7 @@ var PgVector = class extends MastraVector {
1247
1389
  return;
1248
1390
  }
1249
1391
  const effectiveVectorType = existingIndexInfo?.vectorType ?? vectorType;
1250
- const metricOp = this.getMetricOperatorClass(metric, effectiveVectorType);
1392
+ const metricOp = this.getVectorOps(effectiveVectorType, metric).operatorClass;
1251
1393
  let indexSQL;
1252
1394
  if (indexType === "hnsw") {
1253
1395
  const m = indexConfig.hnsw?.m ?? 8;
@@ -1279,6 +1421,21 @@ var PgVector = class extends MastraVector {
1279
1421
  await client.query(indexSQL);
1280
1422
  });
1281
1423
  }
1424
+ async createMetadataIndexes(tableName, indexName, metadataFields) {
1425
+ const hasher = await this.hasher;
1426
+ for (const field of metadataFields) {
1427
+ const fieldHash = hasher.h32(field).toString(16);
1428
+ const prefix = indexName.slice(0, 63 - "_md__idx".length - fieldHash.length);
1429
+ const metadataIdxName = `"${prefix}_md_${fieldHash}_idx"`;
1430
+ const escapedField = field.replace(/'/g, "''");
1431
+ await this.pool.query(
1432
+ `
1433
+ CREATE INDEX CONCURRENTLY IF NOT EXISTS ${metadataIdxName}
1434
+ ON ${tableName} ((metadata->>'${escapedField}'))
1435
+ `
1436
+ );
1437
+ }
1438
+ }
1282
1439
  async installVectorExtension(client) {
1283
1440
  if (this.vectorExtensionInstalled) {
1284
1441
  return;
@@ -1365,7 +1522,7 @@ var PgVector = class extends MastraVector {
1365
1522
  WHERE c.table_schema = t.table_schema
1366
1523
  AND c.table_name = t.table_name
1367
1524
  AND c.column_name = 'embedding'
1368
- AND c.udt_name IN ('vector', 'halfvec')
1525
+ AND c.udt_name IN ('vector', 'halfvec', 'bit', 'sparsevec')
1369
1526
  )
1370
1527
  AND EXISTS (
1371
1528
  SELECT 1
@@ -1408,14 +1565,15 @@ var PgVector = class extends MastraVector {
1408
1565
  FROM information_schema.columns
1409
1566
  WHERE table_schema = $1
1410
1567
  AND table_name = $2
1411
- AND udt_name IN ('vector', 'halfvec')
1568
+ AND udt_name IN ('vector', 'halfvec', 'bit', 'sparsevec')
1412
1569
  LIMIT 1;
1413
1570
  `;
1414
1571
  const tableExists = await client.query(tableExistsQuery, [this.schema || "public", indexName]);
1415
1572
  if (tableExists.rows.length === 0) {
1416
1573
  throw new Error(`Vector table ${tableName} does not exist`);
1417
1574
  }
1418
- const vectorType = tableExists.rows[0].udt_name === "halfvec" ? "halfvec" : "vector";
1575
+ const udtName = tableExists.rows[0].udt_name;
1576
+ const vectorType = udtName === "halfvec" ? "halfvec" : udtName === "bit" ? "bit" : udtName === "sparsevec" ? "sparsevec" : "vector";
1419
1577
  const dimensionQuery = `
1420
1578
  SELECT atttypmod as dimension
1421
1579
  FROM pg_attribute
@@ -1447,7 +1605,7 @@ var PgVector = class extends MastraVector {
1447
1605
  index_def: "",
1448
1606
  operator_class: "cosine"
1449
1607
  };
1450
- const metric = operator_class.includes("l2") ? "euclidean" : operator_class.includes("ip") ? "dotproduct" : "cosine";
1608
+ const metric = operator_class.includes("hamming") ? "hamming" : operator_class.includes("jaccard") ? "jaccard" : operator_class.includes("l2") ? "euclidean" : operator_class.includes("ip") ? "dotproduct" : "cosine";
1451
1609
  const config = {};
1452
1610
  if (index_method === "hnsw") {
1453
1611
  const m = index_def.match(/m\s*=\s*'?(\d+)'?/)?.[1];
@@ -1582,13 +1740,14 @@ var PgVector = class extends MastraVector {
1582
1740
  client = await this.pool.connect();
1583
1741
  const { tableName } = this.getTableName(indexName);
1584
1742
  const indexInfo = await this.getIndexInfo({ indexName });
1585
- const qualifiedVectorType = this.getVectorTypeName(indexInfo.vectorType);
1743
+ const qualifiedVectorType = this.getVectorTypeName(indexInfo.vectorType, indexInfo.dimension);
1744
+ const ops = this.getVectorOps(indexInfo.vectorType, indexInfo.metric ?? "cosine");
1586
1745
  let updateParts = [];
1587
1746
  let values = [];
1588
1747
  let valueIndex = 1;
1589
1748
  if (update.vector) {
1590
1749
  updateParts.push(`embedding = $${valueIndex}::${qualifiedVectorType}`);
1591
- values.push(`[${update.vector.join(",")}]`);
1750
+ values.push(ops.formatVector(update.vector, indexInfo.dimension));
1592
1751
  valueIndex++;
1593
1752
  }
1594
1753
  if (update.metadata) {
@@ -2156,6 +2315,8 @@ var PgDB = class extends MastraBase {
2156
2315
  client;
2157
2316
  schemaName;
2158
2317
  skipDefaultIndexes;
2318
+ /** Cache of actual table columns: tableName -> Set<columnName> */
2319
+ tableColumnsCache = /* @__PURE__ */ new Map();
2159
2320
  constructor(config) {
2160
2321
  super({
2161
2322
  component: "STORAGE",
@@ -2165,6 +2326,40 @@ var PgDB = class extends MastraBase {
2165
2326
  this.schemaName = config.schemaName;
2166
2327
  this.skipDefaultIndexes = config.skipDefaultIndexes;
2167
2328
  }
2329
+ /**
2330
+ * Gets the set of column names that actually exist in the database table.
2331
+ * Results are cached; the cache is invalidated when alterTable() adds new columns.
2332
+ */
2333
+ async getTableColumns(tableName) {
2334
+ const cached = this.tableColumnsCache.get(tableName);
2335
+ if (cached) return cached;
2336
+ const schema = this.schemaName || "public";
2337
+ const rows = await this.client.manyOrNone(
2338
+ `SELECT column_name FROM information_schema.columns WHERE table_schema = $1 AND table_name = $2`,
2339
+ [schema, tableName]
2340
+ );
2341
+ const columns = new Set(rows.map((r) => r.column_name));
2342
+ if (columns.size > 0) {
2343
+ this.tableColumnsCache.set(tableName, columns);
2344
+ }
2345
+ return columns;
2346
+ }
2347
+ /**
2348
+ * Filters a record to only include columns that exist in the actual database table.
2349
+ * Unknown columns are silently dropped to ensure forward compatibility when newer
2350
+ * domain packages add fields that haven't been migrated yet.
2351
+ */
2352
+ async filterRecordToKnownColumns(tableName, record) {
2353
+ const knownColumns = await this.getTableColumns(tableName);
2354
+ if (knownColumns.size === 0) return record;
2355
+ const filtered = {};
2356
+ for (const [key, value] of Object.entries(record)) {
2357
+ if (knownColumns.has(key)) {
2358
+ filtered[key] = value;
2359
+ }
2360
+ }
2361
+ return filtered;
2362
+ }
2168
2363
  async hasColumn(table, column) {
2169
2364
  const schema = this.schemaName || "public";
2170
2365
  const result = await this.client.oneOrNone(
@@ -2281,9 +2476,11 @@ var PgDB = class extends MastraBase {
2281
2476
  async insert({ tableName, record }) {
2282
2477
  try {
2283
2478
  this.addTimestampZColumns(record);
2479
+ const filteredRecord = await this.filterRecordToKnownColumns(tableName, record);
2284
2480
  const schemaName = getSchemaName(this.schemaName);
2285
- const columns = Object.keys(record).map((col) => parseSqlIdentifier(col, "column name"));
2286
- const values = this.prepareValuesForInsert(record, tableName);
2481
+ const columns = Object.keys(filteredRecord).map((col) => parseSqlIdentifier(col, "column name"));
2482
+ if (columns.length === 0) return;
2483
+ const values = this.prepareValuesForInsert(filteredRecord, tableName);
2287
2484
  const placeholders = values.map((_, i) => `$${i + 1}`).join(", ");
2288
2485
  const fullTableName = getTableName({ indexName: tableName, schemaName });
2289
2486
  const columnList = columns.map((c) => `"${c}"`).join(", ");
@@ -2419,6 +2616,8 @@ Note: This migration may take some time for large tables.
2419
2616
  },
2420
2617
  error
2421
2618
  );
2619
+ } finally {
2620
+ this.tableColumnsCache.delete(tableName);
2422
2621
  }
2423
2622
  }
2424
2623
  async setupTimestampTriggers(tableName) {
@@ -2738,6 +2937,8 @@ Note: This migration may take some time for large tables.
2738
2937
  },
2739
2938
  error
2740
2939
  );
2940
+ } finally {
2941
+ this.tableColumnsCache.delete(tableName);
2741
2942
  }
2742
2943
  }
2743
2944
  async load({ tableName, keys }) {
@@ -2814,6 +3015,8 @@ Note: This migration may take some time for large tables.
2814
3015
  },
2815
3016
  error
2816
3017
  );
3018
+ } finally {
3019
+ this.tableColumnsCache.delete(tableName);
2817
3020
  }
2818
3021
  }
2819
3022
  async createIndex(options) {
@@ -3058,10 +3261,12 @@ Note: This migration may take some time for large tables.
3058
3261
  data
3059
3262
  }) {
3060
3263
  try {
3264
+ const filteredData = await this.filterRecordToKnownColumns(tableName, data);
3265
+ if (Object.keys(filteredData).length === 0) return;
3061
3266
  const setColumns = [];
3062
3267
  const setValues = [];
3063
3268
  let paramIndex = 1;
3064
- Object.entries(data).forEach(([key, value]) => {
3269
+ Object.entries(filteredData).forEach(([key, value]) => {
3065
3270
  const parsedKey = parseSqlIdentifier(key, "column name");
3066
3271
  setColumns.push(`"${parsedKey}" = $${paramIndex++}`);
3067
3272
  setValues.push(this.prepareValue(value, key, tableName));
@@ -4120,9 +4325,18 @@ var DatasetsPG = class _DatasetsPG extends DatasetsStorage {
4120
4325
  compositePrimaryKey: TABLE_CONFIGS[TABLE_DATASET_ITEMS]?.compositePrimaryKey
4121
4326
  });
4122
4327
  await this.#db.createTable({ tableName: TABLE_DATASET_VERSIONS, schema: DATASET_VERSIONS_SCHEMA });
4328
+ await this.#addColumnIfNotExists(TABLE_DATASETS, "requestContextSchema", "JSONB");
4329
+ await this.#addColumnIfNotExists(TABLE_DATASET_ITEMS, "requestContext", "JSONB");
4123
4330
  await this.createDefaultIndexes();
4124
4331
  await this.createCustomIndexes();
4125
4332
  }
4333
+ async #addColumnIfNotExists(table, column, sqlType) {
4334
+ const exists = await this.#db.hasColumn(table, column);
4335
+ if (!exists) {
4336
+ const fullTableName = getTableName2({ indexName: table, schemaName: getSchemaName2(this.#schema) });
4337
+ await this.#db.client.none(`ALTER TABLE ${fullTableName} ADD COLUMN "${column}" ${sqlType}`);
4338
+ }
4339
+ }
4126
4340
  getDefaultIndexDefinitions() {
4127
4341
  return [
4128
4342
  { name: "idx_dataset_items_dataset_validto", table: TABLE_DATASET_ITEMS, columns: ["datasetId", "validTo"] },
@@ -4178,6 +4392,7 @@ var DatasetsPG = class _DatasetsPG extends DatasetsStorage {
4178
4392
  metadata: row.metadata ? safelyParseJSON(row.metadata) : void 0,
4179
4393
  inputSchema: row.inputSchema ? safelyParseJSON(row.inputSchema) : void 0,
4180
4394
  groundTruthSchema: row.groundTruthSchema ? safelyParseJSON(row.groundTruthSchema) : void 0,
4395
+ requestContextSchema: row.requestContextSchema ? safelyParseJSON(row.requestContextSchema) : void 0,
4181
4396
  version: row.version,
4182
4397
  createdAt: ensureDate(row.createdAtZ || row.createdAt),
4183
4398
  updatedAt: ensureDate(row.updatedAtZ || row.updatedAt)
@@ -4190,6 +4405,7 @@ var DatasetsPG = class _DatasetsPG extends DatasetsStorage {
4190
4405
  datasetVersion: row.datasetVersion,
4191
4406
  input: safelyParseJSON(row.input),
4192
4407
  groundTruth: row.groundTruth ? safelyParseJSON(row.groundTruth) : void 0,
4408
+ requestContext: row.requestContext ? safelyParseJSON(row.requestContext) : void 0,
4193
4409
  metadata: row.metadata ? safelyParseJSON(row.metadata) : void 0,
4194
4410
  createdAt: ensureDate(row.createdAtZ || row.createdAt),
4195
4411
  updatedAt: ensureDate(row.updatedAtZ || row.updatedAt)
@@ -4204,6 +4420,7 @@ var DatasetsPG = class _DatasetsPG extends DatasetsStorage {
4204
4420
  isDeleted: Boolean(row.isDeleted),
4205
4421
  input: safelyParseJSON(row.input),
4206
4422
  groundTruth: row.groundTruth ? safelyParseJSON(row.groundTruth) : void 0,
4423
+ requestContext: row.requestContext ? safelyParseJSON(row.requestContext) : void 0,
4207
4424
  metadata: row.metadata ? safelyParseJSON(row.metadata) : void 0,
4208
4425
  createdAt: ensureDate(row.createdAtZ || row.createdAt),
4209
4426
  updatedAt: ensureDate(row.updatedAtZ || row.updatedAt)
@@ -4232,6 +4449,7 @@ var DatasetsPG = class _DatasetsPG extends DatasetsStorage {
4232
4449
  metadata: input.metadata ?? null,
4233
4450
  inputSchema: input.inputSchema ?? null,
4234
4451
  groundTruthSchema: input.groundTruthSchema ?? null,
4452
+ requestContextSchema: input.requestContextSchema ?? null,
4235
4453
  version: 0,
4236
4454
  createdAt: nowIso,
4237
4455
  updatedAt: nowIso
@@ -4244,6 +4462,7 @@ var DatasetsPG = class _DatasetsPG extends DatasetsStorage {
4244
4462
  metadata: input.metadata,
4245
4463
  inputSchema: input.inputSchema ?? void 0,
4246
4464
  groundTruthSchema: input.groundTruthSchema ?? void 0,
4465
+ requestContextSchema: input.requestContextSchema ?? void 0,
4247
4466
  version: 0,
4248
4467
  createdAt: now,
4249
4468
  updatedAt: now
@@ -4311,6 +4530,10 @@ var DatasetsPG = class _DatasetsPG extends DatasetsStorage {
4311
4530
  setClauses.push(`"groundTruthSchema" = $${paramIndex++}`);
4312
4531
  values.push(args.groundTruthSchema === null ? null : JSON.stringify(args.groundTruthSchema));
4313
4532
  }
4533
+ if (args.requestContextSchema !== void 0) {
4534
+ setClauses.push(`"requestContextSchema" = $${paramIndex++}`);
4535
+ values.push(args.requestContextSchema === null ? null : JSON.stringify(args.requestContextSchema));
4536
+ }
4314
4537
  values.push(args.id);
4315
4538
  await this.#db.client.none(
4316
4539
  `UPDATE ${tableName} SET ${setClauses.join(", ")} WHERE "id" = $${paramIndex}`,
@@ -4323,6 +4546,7 @@ var DatasetsPG = class _DatasetsPG extends DatasetsStorage {
4323
4546
  metadata: args.metadata ?? existing.metadata,
4324
4547
  inputSchema: (args.inputSchema !== void 0 ? args.inputSchema : existing.inputSchema) ?? void 0,
4325
4548
  groundTruthSchema: (args.groundTruthSchema !== void 0 ? args.groundTruthSchema : existing.groundTruthSchema) ?? void 0,
4549
+ requestContextSchema: (args.requestContextSchema !== void 0 ? args.requestContextSchema : existing.requestContextSchema) ?? void 0,
4326
4550
  updatedAt: new Date(now)
4327
4551
  };
4328
4552
  } catch (error) {
@@ -4438,13 +4662,14 @@ var DatasetsPG = class _DatasetsPG extends DatasetsStorage {
4438
4662
  );
4439
4663
  newVersion = row.version;
4440
4664
  await t.none(
4441
- `INSERT INTO ${itemsTable} ("id","datasetId","datasetVersion","validTo","isDeleted","input","groundTruth","metadata","createdAt","createdAtZ","updatedAt","updatedAtZ") VALUES ($1,$2,$3,NULL,false,$4,$5,$6,$7,$8,$9,$10)`,
4665
+ `INSERT INTO ${itemsTable} ("id","datasetId","datasetVersion","validTo","isDeleted","input","groundTruth","requestContext","metadata","createdAt","createdAtZ","updatedAt","updatedAtZ") VALUES ($1,$2,$3,NULL,false,$4,$5,$6,$7,$8,$9,$10,$11)`,
4442
4666
  [
4443
4667
  id,
4444
4668
  args.datasetId,
4445
4669
  newVersion,
4446
4670
  JSON.stringify(args.input),
4447
4671
  jsonbArg(args.groundTruth),
4672
+ jsonbArg(args.requestContext),
4448
4673
  jsonbArg(args.metadata),
4449
4674
  nowIso,
4450
4675
  nowIso,
@@ -4463,6 +4688,7 @@ var DatasetsPG = class _DatasetsPG extends DatasetsStorage {
4463
4688
  datasetVersion: newVersion,
4464
4689
  input: args.input,
4465
4690
  groundTruth: args.groundTruth,
4691
+ requestContext: args.requestContext,
4466
4692
  metadata: args.metadata,
4467
4693
  createdAt: now,
4468
4694
  updatedAt: now
@@ -4509,6 +4735,7 @@ var DatasetsPG = class _DatasetsPG extends DatasetsStorage {
4509
4735
  const nowIso = now.toISOString();
4510
4736
  const mergedInput = args.input ?? existing.input;
4511
4737
  const mergedGroundTruth = args.groundTruth ?? existing.groundTruth;
4738
+ const mergedRequestContext = args.requestContext ?? existing.requestContext;
4512
4739
  const mergedMetadata = args.metadata ?? existing.metadata;
4513
4740
  let newVersion;
4514
4741
  await this.#db.client.tx(async (t) => {
@@ -4522,13 +4749,14 @@ var DatasetsPG = class _DatasetsPG extends DatasetsStorage {
4522
4749
  [newVersion, args.id]
4523
4750
  );
4524
4751
  await t.none(
4525
- `INSERT INTO ${itemsTable} ("id","datasetId","datasetVersion","validTo","isDeleted","input","groundTruth","metadata","createdAt","createdAtZ","updatedAt","updatedAtZ") VALUES ($1,$2,$3,NULL,false,$4,$5,$6,$7,$8,$9,$10)`,
4752
+ `INSERT INTO ${itemsTable} ("id","datasetId","datasetVersion","validTo","isDeleted","input","groundTruth","requestContext","metadata","createdAt","createdAtZ","updatedAt","updatedAtZ") VALUES ($1,$2,$3,NULL,false,$4,$5,$6,$7,$8,$9,$10,$11)`,
4526
4753
  [
4527
4754
  args.id,
4528
4755
  args.datasetId,
4529
4756
  newVersion,
4530
4757
  JSON.stringify(mergedInput),
4531
4758
  jsonbArg(mergedGroundTruth),
4759
+ jsonbArg(mergedRequestContext),
4532
4760
  jsonbArg(mergedMetadata),
4533
4761
  existing.createdAt.toISOString(),
4534
4762
  existing.createdAt.toISOString(),
@@ -4546,6 +4774,7 @@ var DatasetsPG = class _DatasetsPG extends DatasetsStorage {
4546
4774
  datasetVersion: newVersion,
4547
4775
  input: mergedInput,
4548
4776
  groundTruth: mergedGroundTruth,
4777
+ requestContext: mergedRequestContext,
4549
4778
  metadata: mergedMetadata,
4550
4779
  updatedAt: now
4551
4780
  };
@@ -4592,13 +4821,14 @@ var DatasetsPG = class _DatasetsPG extends DatasetsStorage {
4592
4821
  [newVersion, id]
4593
4822
  );
4594
4823
  await t.none(
4595
- `INSERT INTO ${itemsTable} ("id","datasetId","datasetVersion","validTo","isDeleted","input","groundTruth","metadata","createdAt","createdAtZ","updatedAt","updatedAtZ") VALUES ($1,$2,$3,NULL,true,$4,$5,$6,$7,$8,$9,$10)`,
4824
+ `INSERT INTO ${itemsTable} ("id","datasetId","datasetVersion","validTo","isDeleted","input","groundTruth","requestContext","metadata","createdAt","createdAtZ","updatedAt","updatedAtZ") VALUES ($1,$2,$3,NULL,true,$4,$5,$6,$7,$8,$9,$10,$11)`,
4596
4825
  [
4597
4826
  id,
4598
4827
  datasetId,
4599
4828
  newVersion,
4600
4829
  JSON.stringify(existing.input),
4601
4830
  jsonbArg(existing.groundTruth),
4831
+ jsonbArg(existing.requestContext),
4602
4832
  jsonbArg(existing.metadata),
4603
4833
  existing.createdAt.toISOString(),
4604
4834
  existing.createdAt.toISOString(),
@@ -4653,13 +4883,14 @@ var DatasetsPG = class _DatasetsPG extends DatasetsStorage {
4653
4883
  newVersion = row.version;
4654
4884
  for (const { id, input: itemInput } of itemsWithIds) {
4655
4885
  await t.none(
4656
- `INSERT INTO ${itemsTable} ("id","datasetId","datasetVersion","validTo","isDeleted","input","groundTruth","metadata","createdAt","createdAtZ","updatedAt","updatedAtZ") VALUES ($1,$2,$3,NULL,false,$4,$5,$6,$7,$8,$9,$10)`,
4886
+ `INSERT INTO ${itemsTable} ("id","datasetId","datasetVersion","validTo","isDeleted","input","groundTruth","requestContext","metadata","createdAt","createdAtZ","updatedAt","updatedAtZ") VALUES ($1,$2,$3,NULL,false,$4,$5,$6,$7,$8,$9,$10,$11)`,
4657
4887
  [
4658
4888
  id,
4659
4889
  input.datasetId,
4660
4890
  newVersion,
4661
4891
  JSON.stringify(itemInput.input),
4662
4892
  jsonbArg(itemInput.groundTruth),
4893
+ jsonbArg(itemInput.requestContext),
4663
4894
  jsonbArg(itemInput.metadata),
4664
4895
  nowIso,
4665
4896
  nowIso,
@@ -4679,6 +4910,7 @@ var DatasetsPG = class _DatasetsPG extends DatasetsStorage {
4679
4910
  datasetVersion: newVersion,
4680
4911
  input: itemInput.input,
4681
4912
  groundTruth: itemInput.groundTruth,
4913
+ requestContext: itemInput.requestContext,
4682
4914
  metadata: itemInput.metadata,
4683
4915
  createdAt: now,
4684
4916
  updatedAt: now
@@ -4734,13 +4966,14 @@ var DatasetsPG = class _DatasetsPG extends DatasetsStorage {
4734
4966
  [newVersion, item.id]
4735
4967
  );
4736
4968
  await t.none(
4737
- `INSERT INTO ${itemsTable} ("id","datasetId","datasetVersion","validTo","isDeleted","input","groundTruth","metadata","createdAt","createdAtZ","updatedAt","updatedAtZ") VALUES ($1,$2,$3,NULL,true,$4,$5,$6,$7,$8,$9,$10)`,
4969
+ `INSERT INTO ${itemsTable} ("id","datasetId","datasetVersion","validTo","isDeleted","input","groundTruth","requestContext","metadata","createdAt","createdAtZ","updatedAt","updatedAtZ") VALUES ($1,$2,$3,NULL,true,$4,$5,$6,$7,$8,$9,$10,$11)`,
4738
4970
  [
4739
4971
  item.id,
4740
4972
  input.datasetId,
4741
4973
  newVersion,
4742
4974
  JSON.stringify(item.input),
4743
4975
  jsonbArg(item.groundTruth),
4976
+ jsonbArg(item.requestContext),
4744
4977
  jsonbArg(item.metadata),
4745
4978
  item.createdAt.toISOString(),
4746
4979
  item.createdAt.toISOString(),
@@ -7147,39 +7380,73 @@ var MemoryPG = class _MemoryPG extends MemoryStorage {
7147
7380
  * issues on large tables (see GitHub issue #11150). The old approach required
7148
7381
  * scanning and sorting ALL messages in a thread to assign row numbers.
7149
7382
  *
7150
- * The new approach uses the existing (thread_id, createdAt) index to efficiently
7151
- * fetch only the messages needed by using createdAt as a cursor.
7383
+ * The current approach uses two phases for optimal performance:
7384
+ * 1. Batch-fetch all target messages' metadata (thread_id, createdAt) in one query
7385
+ * 2. Build cursor subqueries using "createdAt" directly (not COALESCE) so that
7386
+ * the existing (thread_id, createdAt DESC) index can be used for index scans
7387
+ * instead of sequential scans. This fixes GitHub issue #11702 where semantic
7388
+ * recall latency scaled linearly with message count (~30s for 7.4k messages).
7152
7389
  */
7390
+ _sortMessages(messages, field, direction) {
7391
+ return messages.sort((a, b) => {
7392
+ const aValue = field === "createdAt" ? new Date(a.createdAt).getTime() : a[field];
7393
+ const bValue = field === "createdAt" ? new Date(b.createdAt).getTime() : b[field];
7394
+ if (aValue == null && bValue == null) return a.id.localeCompare(b.id);
7395
+ if (aValue == null) return 1;
7396
+ if (bValue == null) return -1;
7397
+ if (aValue === bValue) {
7398
+ return a.id.localeCompare(b.id);
7399
+ }
7400
+ if (typeof aValue === "number" && typeof bValue === "number") {
7401
+ return direction === "ASC" ? aValue - bValue : bValue - aValue;
7402
+ }
7403
+ return direction === "ASC" ? String(aValue).localeCompare(String(bValue)) : String(bValue).localeCompare(String(aValue));
7404
+ });
7405
+ }
7153
7406
  async _getIncludedMessages({ include }) {
7154
7407
  if (!include || include.length === 0) return null;
7155
7408
  const tableName = getTableName3({ indexName: TABLE_MESSAGES, schemaName: getSchemaName3(this.#schema) });
7156
7409
  const selectColumns = `id, content, role, type, "createdAt", "createdAtZ", thread_id AS "threadId", "resourceId"`;
7410
+ const targetIds = include.map((inc) => inc.id).filter(Boolean);
7411
+ if (targetIds.length === 0) return null;
7412
+ const idPlaceholders = targetIds.map((_, i) => "$" + (i + 1)).join(", ");
7413
+ const targetRows = await this.#db.client.manyOrNone(`SELECT id, thread_id, "createdAt" FROM ${tableName} WHERE id IN (${idPlaceholders})`, targetIds);
7414
+ if (targetRows.length === 0) return null;
7415
+ const targetMap = new Map(targetRows.map((r) => [r.id, { threadId: r.thread_id, createdAt: r.createdAt }]));
7157
7416
  const unionQueries = [];
7158
7417
  const params = [];
7159
7418
  let paramIdx = 1;
7160
7419
  for (const inc of include) {
7161
7420
  const { id, withPreviousMessages = 0, withNextMessages = 0 } = inc;
7421
+ const target = targetMap.get(id);
7422
+ if (!target) continue;
7423
+ const p1 = "$" + paramIdx;
7424
+ const p2 = "$" + (paramIdx + 1);
7425
+ const p3 = "$" + (paramIdx + 2);
7162
7426
  unionQueries.push(`(
7163
7427
  SELECT ${selectColumns}
7164
7428
  FROM ${tableName} m
7165
- WHERE m.thread_id = (SELECT thread_id FROM ${tableName} WHERE id = $${paramIdx})
7166
- AND COALESCE(m."createdAtZ", m."createdAt") <= (SELECT COALESCE("createdAtZ", "createdAt") FROM ${tableName} WHERE id = $${paramIdx})
7167
- ORDER BY COALESCE(m."createdAtZ", m."createdAt") DESC
7168
- LIMIT $${paramIdx + 1}
7429
+ WHERE m.thread_id = ${p1}
7430
+ AND m."createdAt" <= ${p2}
7431
+ ORDER BY m."createdAt" DESC, m.id DESC
7432
+ LIMIT ${p3}
7169
7433
  )`);
7170
- params.push(id, withPreviousMessages + 1);
7171
- paramIdx += 2;
7434
+ params.push(target.threadId, target.createdAt, withPreviousMessages + 1);
7435
+ paramIdx += 3;
7172
7436
  if (withNextMessages > 0) {
7437
+ const p4 = "$" + paramIdx;
7438
+ const p5 = "$" + (paramIdx + 1);
7439
+ const p6 = "$" + (paramIdx + 2);
7173
7440
  unionQueries.push(`(
7174
7441
  SELECT ${selectColumns}
7175
7442
  FROM ${tableName} m
7176
- WHERE m.thread_id = (SELECT thread_id FROM ${tableName} WHERE id = $${paramIdx})
7177
- AND COALESCE(m."createdAtZ", m."createdAt") > (SELECT COALESCE("createdAtZ", "createdAt") FROM ${tableName} WHERE id = $${paramIdx})
7178
- ORDER BY COALESCE(m."createdAtZ", m."createdAt") ASC
7179
- LIMIT $${paramIdx + 1}
7443
+ WHERE m.thread_id = ${p4}
7444
+ AND m."createdAt" > ${p5}
7445
+ ORDER BY m."createdAt" ASC, m.id ASC
7446
+ LIMIT ${p6}
7180
7447
  )`);
7181
- params.push(id, withNextMessages);
7182
- paramIdx += 2;
7448
+ params.push(target.threadId, target.createdAt, withNextMessages);
7449
+ paramIdx += 3;
7183
7450
  }
7184
7451
  }
7185
7452
  if (unionQueries.length === 0) return null;
@@ -7187,7 +7454,7 @@ var MemoryPG = class _MemoryPG extends MemoryStorage {
7187
7454
  if (unionQueries.length === 1) {
7188
7455
  finalQuery = unionQueries[0].slice(1, -1);
7189
7456
  } else {
7190
- finalQuery = `SELECT * FROM (${unionQueries.join(" UNION ALL ")}) AS combined ORDER BY "createdAt" ASC`;
7457
+ finalQuery = `SELECT * FROM (${unionQueries.join(" UNION ALL ")}) AS combined ORDER BY "createdAt" ASC, id ASC`;
7191
7458
  }
7192
7459
  const includedRows = await this.#db.client.manyOrNone(finalQuery, params);
7193
7460
  const seen = /* @__PURE__ */ new Set();
@@ -7301,6 +7568,24 @@ var MemoryPG = class _MemoryPG extends MemoryStorage {
7301
7568
  queryParams.push(filter.dateRange.end);
7302
7569
  }
7303
7570
  const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
7571
+ if (perPage === 0 && (!include || include.length === 0)) {
7572
+ return { messages: [], total: 0, page, perPage: perPageForResponse, hasMore: false };
7573
+ }
7574
+ if (perPage === 0 && include && include.length > 0) {
7575
+ const includeMessages = await this._getIncludedMessages({ include });
7576
+ if (!includeMessages || includeMessages.length === 0) {
7577
+ return { messages: [], total: 0, page, perPage: perPageForResponse, hasMore: false };
7578
+ }
7579
+ const messagesWithParsedContent2 = includeMessages.map((row) => this.parseRow(row));
7580
+ const list2 = new MessageList().add(messagesWithParsedContent2, "memory");
7581
+ return {
7582
+ messages: this._sortMessages(list2.get.all.db(), field, direction),
7583
+ total: 0,
7584
+ page,
7585
+ perPage: perPageForResponse,
7586
+ hasMore: false
7587
+ };
7588
+ }
7304
7589
  const countQuery = `SELECT COUNT(*) FROM ${tableName} ${whereClause}`;
7305
7590
  const countResult = await this.#db.client.one(countQuery, queryParams);
7306
7591
  const total = parseInt(countResult.count, 10);
@@ -7331,21 +7616,7 @@ var MemoryPG = class _MemoryPG extends MemoryStorage {
7331
7616
  }
7332
7617
  const messagesWithParsedContent = messages.map((row) => this.parseRow(row));
7333
7618
  const list = new MessageList().add(messagesWithParsedContent, "memory");
7334
- let finalMessages = list.get.all.db();
7335
- finalMessages = finalMessages.sort((a, b) => {
7336
- const aValue = field === "createdAt" ? new Date(a.createdAt).getTime() : a[field];
7337
- const bValue = field === "createdAt" ? new Date(b.createdAt).getTime() : b[field];
7338
- if (aValue == null && bValue == null) return a.id.localeCompare(b.id);
7339
- if (aValue == null) return 1;
7340
- if (bValue == null) return -1;
7341
- if (aValue === bValue) {
7342
- return a.id.localeCompare(b.id);
7343
- }
7344
- if (typeof aValue === "number" && typeof bValue === "number") {
7345
- return direction === "ASC" ? aValue - bValue : bValue - aValue;
7346
- }
7347
- return direction === "ASC" ? String(aValue).localeCompare(String(bValue)) : String(bValue).localeCompare(String(aValue));
7348
- });
7619
+ const finalMessages = this._sortMessages(list.get.all.db(), field, direction);
7349
7620
  const threadIdSet = new Set(threadIds);
7350
7621
  const returnedThreadMessageIds = new Set(
7351
7622
  finalMessages.filter((m) => m.threadId && threadIdSet.has(m.threadId)).map((m) => m.id)
@@ -7434,6 +7705,30 @@ var MemoryPG = class _MemoryPG extends MemoryStorage {
7434
7705
  queryParams.push(filter.dateRange.end);
7435
7706
  }
7436
7707
  const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
7708
+ if (perPage === 0 && (!include || include.length === 0)) {
7709
+ return { messages: [], total: 0, page, perPage: perPageForResponse, hasMore: false };
7710
+ }
7711
+ if (perPage === 0 && include && include.length > 0) {
7712
+ const includeMessages = await this._getIncludedMessages({ include });
7713
+ if (!includeMessages || includeMessages.length === 0) {
7714
+ return {
7715
+ messages: [],
7716
+ total: 0,
7717
+ page,
7718
+ perPage: perPageForResponse,
7719
+ hasMore: false
7720
+ };
7721
+ }
7722
+ const messagesWithParsedContent2 = includeMessages.map((row) => this.parseRow(row));
7723
+ const list2 = new MessageList().add(messagesWithParsedContent2, "memory");
7724
+ return {
7725
+ messages: this._sortMessages(list2.get.all.db(), field, direction),
7726
+ total: 0,
7727
+ page,
7728
+ perPage: perPageForResponse,
7729
+ hasMore: false
7730
+ };
7731
+ }
7437
7732
  const countQuery = `SELECT COUNT(*) FROM ${tableName} ${whereClause}`;
7438
7733
  const countResult = await this.#db.client.one(countQuery, queryParams);
7439
7734
  const total = parseInt(countResult.count, 10);
@@ -7464,21 +7759,7 @@ var MemoryPG = class _MemoryPG extends MemoryStorage {
7464
7759
  }
7465
7760
  const messagesWithParsedContent = messages.map((row) => this.parseRow(row));
7466
7761
  const list = new MessageList().add(messagesWithParsedContent, "memory");
7467
- let finalMessages = list.get.all.db();
7468
- finalMessages = finalMessages.sort((a, b) => {
7469
- const aValue = field === "createdAt" ? new Date(a.createdAt).getTime() : a[field];
7470
- const bValue = field === "createdAt" ? new Date(b.createdAt).getTime() : b[field];
7471
- if (aValue == null && bValue == null) return a.id.localeCompare(b.id);
7472
- if (aValue == null) return 1;
7473
- if (bValue == null) return -1;
7474
- if (aValue === bValue) {
7475
- return a.id.localeCompare(b.id);
7476
- }
7477
- if (typeof aValue === "number" && typeof bValue === "number") {
7478
- return direction === "ASC" ? aValue - bValue : bValue - aValue;
7479
- }
7480
- return direction === "ASC" ? String(aValue).localeCompare(String(bValue)) : String(bValue).localeCompare(String(aValue));
7481
- });
7762
+ const finalMessages = this._sortMessages(list.get.all.db(), field, direction);
7482
7763
  const hasMore = perPageInput !== false && offset + perPage < total;
7483
7764
  return {
7484
7765
  messages: finalMessages,
@@ -8932,6 +9213,11 @@ var ObservabilityPG = class _ObservabilityPG extends ObservabilityStorage {
8932
9213
  }
8933
9214
  async init() {
8934
9215
  await this.#db.createTable({ tableName: TABLE_SPANS, schema: TABLE_SCHEMAS[TABLE_SPANS] });
9216
+ await this.#db.alterTable({
9217
+ tableName: TABLE_SPANS,
9218
+ schema: TABLE_SCHEMAS[TABLE_SPANS],
9219
+ ifNotExists: ["requestContext"]
9220
+ });
8935
9221
  await this.createDefaultIndexes();
8936
9222
  await this.createCustomIndexes();
8937
9223
  }
@@ -9286,7 +9572,8 @@ var ObservabilityPG = class _ObservabilityPG extends ObservabilityStorage {
9286
9572
  }
9287
9573
  async listTraces(args) {
9288
9574
  const { filters, pagination, orderBy } = listTracesArgsSchema.parse(args);
9289
- const { page, perPage } = pagination;
9575
+ const page = pagination?.page ?? 0;
9576
+ const perPage = pagination?.perPage ?? 10;
9290
9577
  const tableName = getTableName2({
9291
9578
  indexName: TABLE_SPANS,
9292
9579
  schemaName: getSchemaName2(this.#schema)
@@ -9408,10 +9695,11 @@ var ObservabilityPG = class _ObservabilityPG extends ObservabilityStorage {
9408
9695
  }
9409
9696
  }
9410
9697
  const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
9411
- const sortField = `${orderBy.field}Z`;
9412
- const sortDirection = orderBy.direction;
9698
+ const orderField = orderBy?.field ?? "startedAt";
9699
+ const sortField = `${orderField}Z`;
9700
+ const sortDirection = orderBy?.direction ?? "DESC";
9413
9701
  let orderClause;
9414
- if (orderBy.field === "endedAt") {
9702
+ if (orderField === "endedAt") {
9415
9703
  const nullsOrder = sortDirection === "DESC" ? "NULLS FIRST" : "NULLS LAST";
9416
9704
  orderClause = `ORDER BY r."${sortField}" ${sortDirection} ${nullsOrder}`;
9417
9705
  } else {