@mastra/libsql 0.0.0-bundle-studio-cloud-20251222034739 → 0.0.0-bundle-version-20260121132824

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/CHANGELOG.md +927 -4
  2. package/dist/docs/README.md +39 -0
  3. package/dist/docs/SKILL.md +40 -0
  4. package/dist/docs/SOURCE_MAP.json +6 -0
  5. package/dist/docs/agents/01-agent-memory.md +166 -0
  6. package/dist/docs/agents/02-networks.md +292 -0
  7. package/dist/docs/agents/03-agent-approval.md +377 -0
  8. package/dist/docs/agents/04-network-approval.md +274 -0
  9. package/dist/docs/core/01-reference.md +151 -0
  10. package/dist/docs/guides/01-ai-sdk.md +141 -0
  11. package/dist/docs/memory/01-overview.md +76 -0
  12. package/dist/docs/memory/02-storage.md +233 -0
  13. package/dist/docs/memory/03-working-memory.md +390 -0
  14. package/dist/docs/memory/04-semantic-recall.md +233 -0
  15. package/dist/docs/memory/05-memory-processors.md +318 -0
  16. package/dist/docs/memory/06-reference.md +133 -0
  17. package/dist/docs/observability/01-overview.md +64 -0
  18. package/dist/docs/observability/02-default.md +177 -0
  19. package/dist/docs/rag/01-retrieval.md +548 -0
  20. package/dist/docs/storage/01-reference.md +538 -0
  21. package/dist/docs/vectors/01-reference.md +213 -0
  22. package/dist/docs/workflows/01-snapshots.md +240 -0
  23. package/dist/index.cjs +839 -351
  24. package/dist/index.cjs.map +1 -1
  25. package/dist/index.js +837 -354
  26. package/dist/index.js.map +1 -1
  27. package/dist/storage/db/index.d.ts +46 -0
  28. package/dist/storage/db/index.d.ts.map +1 -1
  29. package/dist/storage/db/utils.d.ts +17 -13
  30. package/dist/storage/db/utils.d.ts.map +1 -1
  31. package/dist/storage/domains/memory/index.d.ts +3 -2
  32. package/dist/storage/domains/memory/index.d.ts.map +1 -1
  33. package/dist/storage/domains/observability/index.d.ts +37 -22
  34. package/dist/storage/domains/observability/index.d.ts.map +1 -1
  35. package/dist/storage/domains/scores/index.d.ts +6 -19
  36. package/dist/storage/domains/scores/index.d.ts.map +1 -1
  37. package/dist/storage/domains/workflows/index.d.ts +1 -0
  38. package/dist/storage/domains/workflows/index.d.ts.map +1 -1
  39. package/dist/storage/index.d.ts +28 -156
  40. package/dist/storage/index.d.ts.map +1 -1
  41. package/dist/vector/index.d.ts +6 -2
  42. package/dist/vector/index.d.ts.map +1 -1
  43. package/dist/vector/sql-builder.d.ts.map +1 -1
  44. package/package.json +10 -9
package/dist/index.cjs CHANGED
@@ -244,10 +244,10 @@ var FILTER_OPERATORS = {
244
244
  };
245
245
  },
246
246
  // Element Operators
247
- $exists: (key) => {
247
+ $exists: (key, value) => {
248
248
  const jsonPath = getJsonPath(key);
249
249
  return {
250
- sql: `json_extract(metadata, ${jsonPath}) IS NOT NULL`,
250
+ sql: value === false ? `json_extract(metadata, ${jsonPath}) IS NULL` : `json_extract(metadata, ${jsonPath}) IS NOT NULL`,
251
251
  needsValue: false
252
252
  };
253
253
  },
@@ -511,7 +511,7 @@ var LibSQLVector = class extends vector.MastraVector {
511
511
  maxRetries;
512
512
  initialBackoffMs;
513
513
  constructor({
514
- connectionUrl,
514
+ url,
515
515
  authToken,
516
516
  syncUrl,
517
517
  syncInterval,
@@ -521,14 +521,14 @@ var LibSQLVector = class extends vector.MastraVector {
521
521
  }) {
522
522
  super({ id });
523
523
  this.turso = client.createClient({
524
- url: connectionUrl,
524
+ url,
525
525
  syncUrl,
526
526
  authToken,
527
527
  syncInterval
528
528
  });
529
529
  this.maxRetries = maxRetries;
530
530
  this.initialBackoffMs = initialBackoffMs;
531
- if (connectionUrl.includes(`file:`) || connectionUrl.includes(`:memory:`)) {
531
+ if (url.includes(`file:`) || url.includes(`:memory:`)) {
532
532
  this.turso.execute("PRAGMA journal_mode=WAL;").then(() => this.logger.debug("LibSQLStore: PRAGMA journal_mode=WAL set.")).catch((err) => this.logger.warn("LibSQLStore: Failed to set PRAGMA journal_mode=WAL.", err));
533
533
  this.turso.execute("PRAGMA busy_timeout = 5000;").then(() => this.logger.debug("LibSQLStore: PRAGMA busy_timeout=5000 set.")).catch((err) => this.logger.warn("LibSQLStore: Failed to set PRAGMA busy_timeout=5000.", err));
534
534
  }
@@ -540,7 +540,7 @@ var LibSQLVector = class extends vector.MastraVector {
540
540
  try {
541
541
  return await operation();
542
542
  } catch (error) {
543
- if (error.code === "SQLITE_BUSY" || error.message && error.message.toLowerCase().includes("database is locked")) {
543
+ if (error.code === "SQLITE_BUSY" || error.code === "SQLITE_LOCKED" || error.code === "SQLITE_LOCKED_SHAREDCACHE" || error.message && error.message.toLowerCase().includes("database is locked") || error.message && error.message.toLowerCase().includes("database table is locked")) {
544
544
  attempts++;
545
545
  if (attempts >= this.maxRetries) {
546
546
  this.logger.error(
@@ -574,22 +574,14 @@ var LibSQLVector = class extends vector.MastraVector {
574
574
  minScore = -1
575
575
  // Default to -1 to include all results (cosine similarity ranges from -1 to 1)
576
576
  }) {
577
- try {
578
- if (!Number.isInteger(topK) || topK <= 0) {
579
- throw new Error("topK must be a positive integer");
580
- }
581
- if (!Array.isArray(queryVector) || !queryVector.every((x) => typeof x === "number" && Number.isFinite(x))) {
582
- throw new Error("queryVector must be an array of finite numbers");
583
- }
584
- } catch (error$1) {
585
- throw new error.MastraError(
586
- {
587
- id: storage.createVectorErrorId("LIBSQL", "QUERY", "INVALID_ARGS"),
588
- domain: error.ErrorDomain.STORAGE,
589
- category: error.ErrorCategory.USER
590
- },
591
- error$1
592
- );
577
+ vector.validateTopK("LIBSQL", topK);
578
+ if (!Array.isArray(queryVector) || !queryVector.every((x) => typeof x === "number" && Number.isFinite(x))) {
579
+ throw new error.MastraError({
580
+ id: storage.createVectorErrorId("LIBSQL", "QUERY", "INVALID_ARGS"),
581
+ domain: error.ErrorDomain.STORAGE,
582
+ category: error.ErrorCategory.USER,
583
+ details: { message: "queryVector must be an array of finite numbers" }
584
+ });
593
585
  }
594
586
  try {
595
587
  const parsedIndexName = utils.parseSqlIdentifier(indexName, "index name");
@@ -649,6 +641,7 @@ var LibSQLVector = class extends vector.MastraVector {
649
641
  }
650
642
  }
651
643
  async doUpsert({ indexName, vectors, metadata, ids }) {
644
+ vector.validateUpsertInput("LIBSQL", vectors, metadata, ids);
652
645
  const tx = await this.turso.transaction("write");
653
646
  try {
654
647
  const parsedIndexName = utils.parseSqlIdentifier(indexName, "index name");
@@ -1095,6 +1088,14 @@ var LibSQLVector = class extends vector.MastraVector {
1095
1088
  });
1096
1089
  }
1097
1090
  };
1091
+ function buildSelectColumns(tableName) {
1092
+ const schema = storage.TABLE_SCHEMAS[tableName];
1093
+ return Object.keys(schema).map((col) => {
1094
+ const colDef = schema[col];
1095
+ const parsedCol = utils.parseSqlIdentifier(col, "column name");
1096
+ return colDef?.type === "jsonb" ? `json(${parsedCol}) as ${parsedCol}` : parsedCol;
1097
+ }).join(", ");
1098
+ }
1098
1099
  function isLockError(error) {
1099
1100
  return error.code === "SQLITE_BUSY" || error.code === "SQLITE_LOCKED" || error.message?.toLowerCase().includes("database is locked") || error.message?.toLowerCase().includes("database table is locked") || error.message?.toLowerCase().includes("table is locked") || error.constructor.name === "SqliteError" && error.message?.toLowerCase().includes("locked");
1100
1101
  }
@@ -1143,17 +1144,27 @@ function createExecuteWriteOperationWithRetry({
1143
1144
  }
1144
1145
  function prepareStatement({ tableName, record }) {
1145
1146
  const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
1146
- const columns = Object.keys(record).map((col) => utils.parseSqlIdentifier(col, "column name"));
1147
- const values = Object.values(record).map((v) => {
1147
+ const schema = storage.TABLE_SCHEMAS[tableName];
1148
+ const columnNames = Object.keys(record);
1149
+ const columns = columnNames.map((col) => utils.parseSqlIdentifier(col, "column name"));
1150
+ const values = columnNames.map((col) => {
1151
+ const v = record[col];
1148
1152
  if (typeof v === `undefined` || v === null) {
1149
1153
  return null;
1150
1154
  }
1155
+ const colDef = schema[col];
1156
+ if (colDef?.type === "jsonb") {
1157
+ return JSON.stringify(v);
1158
+ }
1151
1159
  if (v instanceof Date) {
1152
1160
  return v.toISOString();
1153
1161
  }
1154
1162
  return typeof v === "object" ? JSON.stringify(v) : v;
1155
1163
  });
1156
- const placeholders = values.map(() => "?").join(", ");
1164
+ const placeholders = columnNames.map((col) => {
1165
+ const colDef = schema[col];
1166
+ return colDef?.type === "jsonb" ? "jsonb(?)" : "?";
1167
+ }).join(", ");
1157
1168
  return {
1158
1169
  sql: `INSERT OR REPLACE INTO ${parsedTableName} (${columns.join(", ")}) VALUES (${placeholders})`,
1159
1170
  args: values
@@ -1166,19 +1177,33 @@ function prepareUpdateStatement({
1166
1177
  }) {
1167
1178
  const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
1168
1179
  const schema = storage.TABLE_SCHEMAS[tableName];
1169
- const updateColumns = Object.keys(updates).map((col) => utils.parseSqlIdentifier(col, "column name"));
1170
- const updateValues = Object.values(updates).map(transformToSqlValue);
1171
- const setClause = updateColumns.map((col) => `${col} = ?`).join(", ");
1180
+ const updateColumnNames = Object.keys(updates);
1181
+ const updateColumns = updateColumnNames.map((col) => utils.parseSqlIdentifier(col, "column name"));
1182
+ const updateValues = updateColumnNames.map((col) => {
1183
+ const colDef = schema[col];
1184
+ const v = updates[col];
1185
+ if (colDef?.type === "jsonb") {
1186
+ return transformToSqlValue(v, true);
1187
+ }
1188
+ return transformToSqlValue(v, false);
1189
+ });
1190
+ const setClause = updateColumns.map((col, i) => {
1191
+ const colDef = schema[updateColumnNames[i]];
1192
+ return colDef?.type === "jsonb" ? `${col} = jsonb(?)` : `${col} = ?`;
1193
+ }).join(", ");
1172
1194
  const whereClause = prepareWhereClause(keys, schema);
1173
1195
  return {
1174
1196
  sql: `UPDATE ${parsedTableName} SET ${setClause}${whereClause.sql}`,
1175
1197
  args: [...updateValues, ...whereClause.args]
1176
1198
  };
1177
1199
  }
1178
- function transformToSqlValue(value) {
1200
+ function transformToSqlValue(value, forceJsonStringify = false) {
1179
1201
  if (typeof value === "undefined" || value === null) {
1180
1202
  return null;
1181
1203
  }
1204
+ if (forceJsonStringify) {
1205
+ return JSON.stringify(value);
1206
+ }
1182
1207
  if (value instanceof Date) {
1183
1208
  return value.toISOString();
1184
1209
  }
@@ -1241,19 +1266,6 @@ function buildDateRangeCondition(columnName, range) {
1241
1266
  args
1242
1267
  };
1243
1268
  }
1244
- function buildDateRangeFilter(dateRange, columnName = "createdAt") {
1245
- if (!dateRange?.start && !dateRange?.end) {
1246
- return {};
1247
- }
1248
- const filter = {};
1249
- if (dateRange.start) {
1250
- filter.startAt = new Date(dateRange.start).toISOString();
1251
- }
1252
- if (dateRange.end) {
1253
- filter.endAt = new Date(dateRange.end).toISOString();
1254
- }
1255
- return { [columnName]: filter };
1256
- }
1257
1269
  function transformFromSqlRow({
1258
1270
  tableName,
1259
1271
  sqlRow
@@ -1548,11 +1560,12 @@ var LibSQLDB = class extends base.MastraBase {
1548
1560
  */
1549
1561
  async select({ tableName, keys }) {
1550
1562
  const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
1563
+ const columns = buildSelectColumns(tableName);
1551
1564
  const parsedKeys = Object.keys(keys).map((key) => utils.parseSqlIdentifier(key, "column name"));
1552
1565
  const conditions = parsedKeys.map((key) => `${key} = ?`).join(" AND ");
1553
1566
  const values = Object.values(keys);
1554
1567
  const result = await this.client.execute({
1555
- sql: `SELECT * FROM ${parsedTableName} WHERE ${conditions} ORDER BY createdAt DESC LIMIT 1`,
1568
+ sql: `SELECT ${columns} FROM ${parsedTableName} WHERE ${conditions} ORDER BY createdAt DESC LIMIT 1`,
1556
1569
  args: values
1557
1570
  });
1558
1571
  if (!result.rows || result.rows.length === 0) {
@@ -1592,9 +1605,10 @@ var LibSQLDB = class extends base.MastraBase {
1592
1605
  args
1593
1606
  }) {
1594
1607
  const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
1595
- let statement = `SELECT * FROM ${parsedTableName}`;
1608
+ const columns = buildSelectColumns(tableName);
1609
+ let statement = `SELECT ${columns} FROM ${parsedTableName}`;
1596
1610
  if (whereClause?.sql) {
1597
- statement += `${whereClause.sql}`;
1611
+ statement += ` ${whereClause.sql}`;
1598
1612
  }
1599
1613
  if (orderBy) {
1600
1614
  statement += ` ORDER BY ${orderBy}`;
@@ -1609,7 +1623,17 @@ var LibSQLDB = class extends base.MastraBase {
1609
1623
  sql: statement,
1610
1624
  args: [...whereClause?.args ?? [], ...args ?? []]
1611
1625
  });
1612
- return result.rows;
1626
+ return (result.rows ?? []).map((row) => {
1627
+ return Object.fromEntries(
1628
+ Object.entries(row || {}).map(([k, v]) => {
1629
+ try {
1630
+ return [k, typeof v === "string" ? v.startsWith("{") || v.startsWith("[") ? JSON.parse(v) : v : v];
1631
+ } catch {
1632
+ return [k, v];
1633
+ }
1634
+ })
1635
+ );
1636
+ });
1613
1637
  }
1614
1638
  /**
1615
1639
  * Returns the total count of records matching the optional WHERE clause.
@@ -1653,7 +1677,7 @@ var LibSQLDB = class extends base.MastraBase {
1653
1677
  // SQLite uses 0/1 for booleans
1654
1678
  case "jsonb":
1655
1679
  return "TEXT";
1656
- // Store JSON as TEXT in SQLite
1680
+ // SQLite: column stores TEXT, we use jsonb()/json() functions for binary optimization
1657
1681
  default:
1658
1682
  return storage.getSqlType(type);
1659
1683
  }
@@ -1681,13 +1705,22 @@ var LibSQLDB = class extends base.MastraBase {
1681
1705
  if (tableName === storage.TABLE_WORKFLOW_SNAPSHOT) {
1682
1706
  tableConstraints.push("UNIQUE (workflow_name, run_id)");
1683
1707
  }
1708
+ if (tableName === storage.TABLE_SPANS) {
1709
+ tableConstraints.push("UNIQUE (spanId, traceId)");
1710
+ }
1684
1711
  const allDefinitions = [...columnDefinitions, ...tableConstraints].join(",\n ");
1685
1712
  const sql = `CREATE TABLE IF NOT EXISTS ${parsedTableName} (
1686
1713
  ${allDefinitions}
1687
1714
  )`;
1688
1715
  await this.client.execute(sql);
1689
1716
  this.logger.debug(`LibSQLDB: Created table ${tableName}`);
1717
+ if (tableName === storage.TABLE_SPANS) {
1718
+ await this.migrateSpansTable();
1719
+ }
1690
1720
  } catch (error$1) {
1721
+ if (error$1 instanceof error.MastraError) {
1722
+ throw error$1;
1723
+ }
1691
1724
  throw new error.MastraError(
1692
1725
  {
1693
1726
  id: storage.createStorageErrorId("LIBSQL", "CREATE_TABLE", "FAILED"),
@@ -1699,6 +1732,221 @@ var LibSQLDB = class extends base.MastraBase {
1699
1732
  );
1700
1733
  }
1701
1734
  }
1735
+ /**
1736
+ * Migrates the spans table schema from OLD_SPAN_SCHEMA to current SPAN_SCHEMA.
1737
+ * This adds new columns that don't exist in old schema and ensures required indexes exist.
1738
+ */
1739
+ async migrateSpansTable() {
1740
+ const schema = storage.TABLE_SCHEMAS[storage.TABLE_SPANS];
1741
+ try {
1742
+ for (const [columnName, columnDef] of Object.entries(schema)) {
1743
+ const columnExists = await this.hasColumn(storage.TABLE_SPANS, columnName);
1744
+ if (!columnExists) {
1745
+ const sqlType = this.getSqlType(columnDef.type);
1746
+ const alterSql = `ALTER TABLE "${storage.TABLE_SPANS}" ADD COLUMN "${columnName}" ${sqlType}`;
1747
+ await this.client.execute(alterSql);
1748
+ this.logger.debug(`LibSQLDB: Added column '${columnName}' to ${storage.TABLE_SPANS}`);
1749
+ }
1750
+ }
1751
+ const indexExists = await this.spansUniqueIndexExists();
1752
+ if (!indexExists) {
1753
+ const duplicateInfo = await this.checkForDuplicateSpans();
1754
+ if (duplicateInfo.hasDuplicates) {
1755
+ const errorMessage = `
1756
+ ===========================================================================
1757
+ MIGRATION REQUIRED: Duplicate spans detected in ${storage.TABLE_SPANS}
1758
+ ===========================================================================
1759
+
1760
+ Found ${duplicateInfo.duplicateCount} duplicate (traceId, spanId) combinations.
1761
+
1762
+ The spans table requires a unique constraint on (traceId, spanId), but your
1763
+ database contains duplicate entries that must be resolved first.
1764
+
1765
+ To fix this, run the manual migration command:
1766
+
1767
+ npx mastra migrate
1768
+
1769
+ This command will:
1770
+ 1. Remove duplicate spans (keeping the most complete/recent version)
1771
+ 2. Add the required unique constraint
1772
+
1773
+ Note: This migration may take some time for large tables.
1774
+ ===========================================================================
1775
+ `;
1776
+ throw new error.MastraError({
1777
+ id: storage.createStorageErrorId("LIBSQL", "MIGRATION_REQUIRED", "DUPLICATE_SPANS"),
1778
+ domain: error.ErrorDomain.STORAGE,
1779
+ category: error.ErrorCategory.USER,
1780
+ text: errorMessage
1781
+ });
1782
+ } else {
1783
+ await this.client.execute(
1784
+ `CREATE UNIQUE INDEX IF NOT EXISTS "mastra_ai_spans_spanid_traceid_idx" ON "${storage.TABLE_SPANS}" ("spanId", "traceId")`
1785
+ );
1786
+ this.logger.debug(`LibSQLDB: Created unique index on (spanId, traceId) for ${storage.TABLE_SPANS}`);
1787
+ }
1788
+ }
1789
+ this.logger.info(`LibSQLDB: Migration completed for ${storage.TABLE_SPANS}`);
1790
+ } catch (error$1) {
1791
+ if (error$1 instanceof error.MastraError) {
1792
+ throw error$1;
1793
+ }
1794
+ this.logger.warn(`LibSQLDB: Failed to migrate spans table ${storage.TABLE_SPANS}:`, error$1);
1795
+ }
1796
+ }
1797
+ /**
1798
+ * Checks if the unique index on (spanId, traceId) already exists on the spans table.
1799
+ * Used to skip deduplication when the index already exists (migration already complete).
1800
+ */
1801
+ async spansUniqueIndexExists() {
1802
+ try {
1803
+ const result = await this.client.execute(
1804
+ `SELECT 1 FROM sqlite_master WHERE type = 'index' AND name = 'mastra_ai_spans_spanid_traceid_idx'`
1805
+ );
1806
+ return (result.rows?.length ?? 0) > 0;
1807
+ } catch {
1808
+ return false;
1809
+ }
1810
+ }
1811
+ /**
1812
+ * Checks for duplicate (traceId, spanId) combinations in the spans table.
1813
+ * Returns information about duplicates for logging/CLI purposes.
1814
+ */
1815
+ async checkForDuplicateSpans() {
1816
+ try {
1817
+ const result = await this.client.execute(`
1818
+ SELECT COUNT(*) as duplicate_count FROM (
1819
+ SELECT "spanId", "traceId"
1820
+ FROM "${storage.TABLE_SPANS}"
1821
+ GROUP BY "spanId", "traceId"
1822
+ HAVING COUNT(*) > 1
1823
+ )
1824
+ `);
1825
+ const duplicateCount = Number(result.rows?.[0]?.duplicate_count ?? 0);
1826
+ return {
1827
+ hasDuplicates: duplicateCount > 0,
1828
+ duplicateCount
1829
+ };
1830
+ } catch (error) {
1831
+ this.logger.debug(`LibSQLDB: Could not check for duplicates: ${error}`);
1832
+ return { hasDuplicates: false, duplicateCount: 0 };
1833
+ }
1834
+ }
1835
+ /**
1836
+ * Manually run the spans migration to deduplicate and add the unique constraint.
1837
+ * This is intended to be called from the CLI when duplicates are detected.
1838
+ *
1839
+ * @returns Migration result with status and details
1840
+ */
1841
+ async migrateSpans() {
1842
+ const indexExists = await this.spansUniqueIndexExists();
1843
+ if (indexExists) {
1844
+ return {
1845
+ success: true,
1846
+ alreadyMigrated: true,
1847
+ duplicatesRemoved: 0,
1848
+ message: `Migration already complete. Unique index exists on ${storage.TABLE_SPANS}.`
1849
+ };
1850
+ }
1851
+ const duplicateInfo = await this.checkForDuplicateSpans();
1852
+ if (duplicateInfo.hasDuplicates) {
1853
+ this.logger.info(
1854
+ `Found ${duplicateInfo.duplicateCount} duplicate (traceId, spanId) combinations. Starting deduplication...`
1855
+ );
1856
+ await this.deduplicateSpans();
1857
+ } else {
1858
+ this.logger.info(`No duplicate spans found.`);
1859
+ }
1860
+ await this.client.execute(
1861
+ `CREATE UNIQUE INDEX IF NOT EXISTS "mastra_ai_spans_spanid_traceid_idx" ON "${storage.TABLE_SPANS}" ("spanId", "traceId")`
1862
+ );
1863
+ return {
1864
+ success: true,
1865
+ alreadyMigrated: false,
1866
+ duplicatesRemoved: duplicateInfo.duplicateCount,
1867
+ message: duplicateInfo.hasDuplicates ? `Migration complete. Removed duplicates and added unique index to ${storage.TABLE_SPANS}.` : `Migration complete. Added unique index to ${storage.TABLE_SPANS}.`
1868
+ };
1869
+ }
1870
+ /**
1871
+ * Check migration status for the spans table.
1872
+ * Returns information about whether migration is needed.
1873
+ */
1874
+ async checkSpansMigrationStatus() {
1875
+ const indexExists = await this.spansUniqueIndexExists();
1876
+ if (indexExists) {
1877
+ return {
1878
+ needsMigration: false,
1879
+ hasDuplicates: false,
1880
+ duplicateCount: 0,
1881
+ constraintExists: true,
1882
+ tableName: storage.TABLE_SPANS
1883
+ };
1884
+ }
1885
+ const duplicateInfo = await this.checkForDuplicateSpans();
1886
+ return {
1887
+ needsMigration: true,
1888
+ hasDuplicates: duplicateInfo.hasDuplicates,
1889
+ duplicateCount: duplicateInfo.duplicateCount,
1890
+ constraintExists: false,
1891
+ tableName: storage.TABLE_SPANS
1892
+ };
1893
+ }
1894
+ /**
1895
+ * Deduplicates spans table by removing duplicate (spanId, traceId) combinations.
1896
+ * Keeps the "best" record for each duplicate group based on:
1897
+ * 1. Completed spans (endedAt IS NOT NULL) over incomplete ones
1898
+ * 2. Most recently updated (updatedAt DESC)
1899
+ * 3. Most recently created (createdAt DESC) as tiebreaker
1900
+ */
1901
+ async deduplicateSpans() {
1902
+ try {
1903
+ const duplicateCheck = await this.client.execute(`
1904
+ SELECT COUNT(*) as duplicate_count FROM (
1905
+ SELECT "spanId", "traceId"
1906
+ FROM "${storage.TABLE_SPANS}"
1907
+ GROUP BY "spanId", "traceId"
1908
+ HAVING COUNT(*) > 1
1909
+ )
1910
+ `);
1911
+ const duplicateCount = Number(duplicateCheck.rows?.[0]?.duplicate_count ?? 0);
1912
+ if (duplicateCount === 0) {
1913
+ this.logger.debug(`LibSQLDB: No duplicate spans found, skipping deduplication`);
1914
+ return;
1915
+ }
1916
+ this.logger.warn(`LibSQLDB: Found ${duplicateCount} duplicate (spanId, traceId) combinations, deduplicating...`);
1917
+ const deleteResult = await this.client.execute(`
1918
+ DELETE FROM "${storage.TABLE_SPANS}"
1919
+ WHERE rowid NOT IN (
1920
+ SELECT MIN(best_rowid) FROM (
1921
+ SELECT
1922
+ rowid as best_rowid,
1923
+ "spanId",
1924
+ "traceId",
1925
+ ROW_NUMBER() OVER (
1926
+ PARTITION BY "spanId", "traceId"
1927
+ ORDER BY
1928
+ CASE WHEN "endedAt" IS NOT NULL THEN 0 ELSE 1 END,
1929
+ "updatedAt" DESC,
1930
+ "createdAt" DESC
1931
+ ) as rn
1932
+ FROM "${storage.TABLE_SPANS}"
1933
+ ) ranked
1934
+ WHERE rn = 1
1935
+ GROUP BY "spanId", "traceId"
1936
+ )
1937
+ AND ("spanId", "traceId") IN (
1938
+ SELECT "spanId", "traceId"
1939
+ FROM "${storage.TABLE_SPANS}"
1940
+ GROUP BY "spanId", "traceId"
1941
+ HAVING COUNT(*) > 1
1942
+ )
1943
+ `);
1944
+ const deletedCount = deleteResult.rowsAffected ?? 0;
1945
+ this.logger.warn(`LibSQLDB: Deleted ${deletedCount} duplicate span records`);
1946
+ } catch (error) {
1947
+ this.logger.warn(`LibSQLDB: Failed to deduplicate spans:`, error);
1948
+ }
1949
+ }
1702
1950
  /**
1703
1951
  * Gets a default value for a column type (used when adding NOT NULL columns).
1704
1952
  */
@@ -2201,13 +2449,15 @@ var MemoryLibSQL = class extends storage.MemoryStorage {
2201
2449
  queryParams.push(resourceId);
2202
2450
  }
2203
2451
  if (filter?.dateRange?.start) {
2204
- conditions.push(`"createdAt" >= ?`);
2452
+ const startOp = filter.dateRange.startExclusive ? ">" : ">=";
2453
+ conditions.push(`"createdAt" ${startOp} ?`);
2205
2454
  queryParams.push(
2206
2455
  filter.dateRange.start instanceof Date ? filter.dateRange.start.toISOString() : filter.dateRange.start
2207
2456
  );
2208
2457
  }
2209
2458
  if (filter?.dateRange?.end) {
2210
- conditions.push(`"createdAt" <= ?`);
2459
+ const endOp = filter.dateRange.endExclusive ? "<" : "<=";
2460
+ conditions.push(`"createdAt" ${endOp} ?`);
2211
2461
  queryParams.push(
2212
2462
  filter.dateRange.end instanceof Date ? filter.dateRange.end.toISOString() : filter.dateRange.end
2213
2463
  );
@@ -2513,8 +2763,8 @@ var MemoryLibSQL = class extends storage.MemoryStorage {
2513
2763
  await this.#db.insert({
2514
2764
  tableName: storage.TABLE_RESOURCES,
2515
2765
  record: {
2516
- ...resource,
2517
- metadata: JSON.stringify(resource.metadata)
2766
+ ...resource
2767
+ // metadata is handled by prepareStatement which stringifies jsonb columns
2518
2768
  }
2519
2769
  });
2520
2770
  return resource;
@@ -2551,7 +2801,7 @@ var MemoryLibSQL = class extends storage.MemoryStorage {
2551
2801
  values.push(workingMemory);
2552
2802
  }
2553
2803
  if (metadata) {
2554
- updates.push("metadata = ?");
2804
+ updates.push("metadata = jsonb(?)");
2555
2805
  values.push(JSON.stringify(updatedResource.metadata));
2556
2806
  }
2557
2807
  updates.push("updatedAt = ?");
@@ -2590,33 +2840,76 @@ var MemoryLibSQL = class extends storage.MemoryStorage {
2590
2840
  );
2591
2841
  }
2592
2842
  }
2593
- async listThreadsByResourceId(args) {
2594
- const { resourceId, page = 0, perPage: perPageInput, orderBy } = args;
2595
- if (page < 0) {
2843
+ async listThreads(args) {
2844
+ const { page = 0, perPage: perPageInput, orderBy, filter } = args;
2845
+ try {
2846
+ this.validatePaginationInput(page, perPageInput ?? 100);
2847
+ } catch (error$1) {
2596
2848
  throw new error.MastraError(
2597
2849
  {
2598
- id: storage.createStorageErrorId("LIBSQL", "LIST_THREADS_BY_RESOURCE_ID", "INVALID_PAGE"),
2850
+ id: storage.createStorageErrorId("LIBSQL", "LIST_THREADS", "INVALID_PAGE"),
2599
2851
  domain: error.ErrorDomain.STORAGE,
2600
2852
  category: error.ErrorCategory.USER,
2601
- details: { page }
2853
+ details: { page, ...perPageInput !== void 0 && { perPage: perPageInput } }
2602
2854
  },
2603
- new Error("page must be >= 0")
2855
+ error$1 instanceof Error ? error$1 : new Error("Invalid pagination parameters")
2604
2856
  );
2605
2857
  }
2606
2858
  const perPage = storage.normalizePerPage(perPageInput, 100);
2859
+ try {
2860
+ this.validateMetadataKeys(filter?.metadata);
2861
+ } catch (error$1) {
2862
+ throw new error.MastraError(
2863
+ {
2864
+ id: storage.createStorageErrorId("LIBSQL", "LIST_THREADS", "INVALID_METADATA_KEY"),
2865
+ domain: error.ErrorDomain.STORAGE,
2866
+ category: error.ErrorCategory.USER,
2867
+ details: { metadataKeys: filter?.metadata ? Object.keys(filter.metadata).join(", ") : "" }
2868
+ },
2869
+ error$1 instanceof Error ? error$1 : new Error("Invalid metadata key")
2870
+ );
2871
+ }
2607
2872
  const { offset, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
2608
2873
  const { field, direction } = this.parseOrderBy(orderBy);
2609
2874
  try {
2610
- const baseQuery = `FROM ${storage.TABLE_THREADS} WHERE resourceId = ?`;
2611
- const queryParams = [resourceId];
2875
+ const whereClauses = [];
2876
+ const queryParams = [];
2877
+ if (filter?.resourceId) {
2878
+ whereClauses.push("resourceId = ?");
2879
+ queryParams.push(filter.resourceId);
2880
+ }
2881
+ if (filter?.metadata && Object.keys(filter.metadata).length > 0) {
2882
+ for (const [key, value] of Object.entries(filter.metadata)) {
2883
+ if (value === null) {
2884
+ whereClauses.push(`json_extract(metadata, '$.${key}') IS NULL`);
2885
+ } else if (typeof value === "boolean") {
2886
+ whereClauses.push(`json_extract(metadata, '$.${key}') = ?`);
2887
+ queryParams.push(value ? 1 : 0);
2888
+ } else if (typeof value === "number") {
2889
+ whereClauses.push(`json_extract(metadata, '$.${key}') = ?`);
2890
+ queryParams.push(value);
2891
+ } else if (typeof value === "string") {
2892
+ whereClauses.push(`json_extract(metadata, '$.${key}') = ?`);
2893
+ queryParams.push(value);
2894
+ } else {
2895
+ throw new error.MastraError({
2896
+ id: storage.createStorageErrorId("LIBSQL", "LIST_THREADS", "INVALID_METADATA_VALUE"),
2897
+ domain: error.ErrorDomain.STORAGE,
2898
+ category: error.ErrorCategory.USER,
2899
+ text: `Metadata filter value for key "${key}" must be a scalar type (string, number, boolean, or null), got ${typeof value}`,
2900
+ details: { key, valueType: typeof value }
2901
+ });
2902
+ }
2903
+ }
2904
+ }
2905
+ const whereClause = whereClauses.length > 0 ? `WHERE ${whereClauses.join(" AND ")}` : "";
2906
+ const baseQuery = `FROM ${storage.TABLE_THREADS} ${whereClause}`;
2612
2907
  const mapRowToStorageThreadType = (row) => ({
2613
2908
  id: row.id,
2614
2909
  resourceId: row.resourceId,
2615
2910
  title: row.title,
2616
2911
  createdAt: new Date(row.createdAt),
2617
- // Convert string to Date
2618
2912
  updatedAt: new Date(row.updatedAt),
2619
- // Convert string to Date
2620
2913
  metadata: typeof row.metadata === "string" ? JSON.parse(row.metadata) : row.metadata
2621
2914
  });
2622
2915
  const countResult = await this.#client.execute({
@@ -2635,7 +2928,7 @@ var MemoryLibSQL = class extends storage.MemoryStorage {
2635
2928
  }
2636
2929
  const limitValue = perPageInput === false ? total : perPage;
2637
2930
  const dataResult = await this.#client.execute({
2638
- sql: `SELECT * ${baseQuery} ORDER BY "${field}" ${direction} LIMIT ? OFFSET ?`,
2931
+ sql: `SELECT ${buildSelectColumns(storage.TABLE_THREADS)} ${baseQuery} ORDER BY "${field}" ${direction} LIMIT ? OFFSET ?`,
2639
2932
  args: [...queryParams, limitValue, offset]
2640
2933
  });
2641
2934
  const threads = (dataResult.rows || []).map(mapRowToStorageThreadType);
@@ -2647,12 +2940,18 @@ var MemoryLibSQL = class extends storage.MemoryStorage {
2647
2940
  hasMore: perPageInput === false ? false : offset + perPage < total
2648
2941
  };
2649
2942
  } catch (error$1) {
2943
+ if (error$1 instanceof error.MastraError && error$1.category === error.ErrorCategory.USER) {
2944
+ throw error$1;
2945
+ }
2650
2946
  const mastraError = new error.MastraError(
2651
2947
  {
2652
- id: storage.createStorageErrorId("LIBSQL", "LIST_THREADS_BY_RESOURCE_ID", "FAILED"),
2948
+ id: storage.createStorageErrorId("LIBSQL", "LIST_THREADS", "FAILED"),
2653
2949
  domain: error.ErrorDomain.STORAGE,
2654
2950
  category: error.ErrorCategory.THIRD_PARTY,
2655
- details: { resourceId }
2951
+ details: {
2952
+ ...filter?.resourceId && { resourceId: filter.resourceId },
2953
+ hasMetadataFilter: !!filter?.metadata
2954
+ }
2656
2955
  },
2657
2956
  error$1
2658
2957
  );
@@ -2672,8 +2971,8 @@ var MemoryLibSQL = class extends storage.MemoryStorage {
2672
2971
  await this.#db.insert({
2673
2972
  tableName: storage.TABLE_THREADS,
2674
2973
  record: {
2675
- ...thread,
2676
- metadata: JSON.stringify(thread.metadata)
2974
+ ...thread
2975
+ // metadata is handled by prepareStatement which stringifies jsonb columns
2677
2976
  }
2678
2977
  });
2679
2978
  return thread;
@@ -2720,7 +3019,7 @@ var MemoryLibSQL = class extends storage.MemoryStorage {
2720
3019
  };
2721
3020
  try {
2722
3021
  await this.#client.execute({
2723
- sql: `UPDATE ${storage.TABLE_THREADS} SET title = ?, metadata = ? WHERE id = ?`,
3022
+ sql: `UPDATE ${storage.TABLE_THREADS} SET title = ?, metadata = jsonb(?) WHERE id = ?`,
2724
3023
  args: [title, JSON.stringify(updatedThread.metadata), id]
2725
3024
  });
2726
3025
  return updatedThread;
@@ -2759,6 +3058,148 @@ var MemoryLibSQL = class extends storage.MemoryStorage {
2759
3058
  );
2760
3059
  }
2761
3060
  }
3061
+ async cloneThread(args) {
3062
+ const { sourceThreadId, newThreadId: providedThreadId, resourceId, title, metadata, options } = args;
3063
+ const sourceThread = await this.getThreadById({ threadId: sourceThreadId });
3064
+ if (!sourceThread) {
3065
+ throw new error.MastraError({
3066
+ id: storage.createStorageErrorId("LIBSQL", "CLONE_THREAD", "SOURCE_NOT_FOUND"),
3067
+ domain: error.ErrorDomain.STORAGE,
3068
+ category: error.ErrorCategory.USER,
3069
+ text: `Source thread with id ${sourceThreadId} not found`,
3070
+ details: { sourceThreadId }
3071
+ });
3072
+ }
3073
+ const newThreadId = providedThreadId || crypto.randomUUID();
3074
+ const existingThread = await this.getThreadById({ threadId: newThreadId });
3075
+ if (existingThread) {
3076
+ throw new error.MastraError({
3077
+ id: storage.createStorageErrorId("LIBSQL", "CLONE_THREAD", "THREAD_EXISTS"),
3078
+ domain: error.ErrorDomain.STORAGE,
3079
+ category: error.ErrorCategory.USER,
3080
+ text: `Thread with id ${newThreadId} already exists`,
3081
+ details: { newThreadId }
3082
+ });
3083
+ }
3084
+ try {
3085
+ let messageQuery = `SELECT id, content, role, type, "createdAt", thread_id, "resourceId"
3086
+ FROM "${storage.TABLE_MESSAGES}" WHERE thread_id = ?`;
3087
+ const messageParams = [sourceThreadId];
3088
+ if (options?.messageFilter?.startDate) {
3089
+ messageQuery += ` AND "createdAt" >= ?`;
3090
+ messageParams.push(
3091
+ options.messageFilter.startDate instanceof Date ? options.messageFilter.startDate.toISOString() : options.messageFilter.startDate
3092
+ );
3093
+ }
3094
+ if (options?.messageFilter?.endDate) {
3095
+ messageQuery += ` AND "createdAt" <= ?`;
3096
+ messageParams.push(
3097
+ options.messageFilter.endDate instanceof Date ? options.messageFilter.endDate.toISOString() : options.messageFilter.endDate
3098
+ );
3099
+ }
3100
+ if (options?.messageFilter?.messageIds && options.messageFilter.messageIds.length > 0) {
3101
+ messageQuery += ` AND id IN (${options.messageFilter.messageIds.map(() => "?").join(", ")})`;
3102
+ messageParams.push(...options.messageFilter.messageIds);
3103
+ }
3104
+ messageQuery += ` ORDER BY "createdAt" ASC`;
3105
+ if (options?.messageLimit && options.messageLimit > 0) {
3106
+ const limitQuery = `SELECT * FROM (${messageQuery.replace('ORDER BY "createdAt" ASC', 'ORDER BY "createdAt" DESC')} LIMIT ?) ORDER BY "createdAt" ASC`;
3107
+ messageParams.push(options.messageLimit);
3108
+ messageQuery = limitQuery;
3109
+ }
3110
+ const sourceMessagesResult = await this.#client.execute({ sql: messageQuery, args: messageParams });
3111
+ const sourceMessages = sourceMessagesResult.rows || [];
3112
+ const now = /* @__PURE__ */ new Date();
3113
+ const nowStr = now.toISOString();
3114
+ const lastMessageId = sourceMessages.length > 0 ? sourceMessages[sourceMessages.length - 1].id : void 0;
3115
+ const cloneMetadata = {
3116
+ sourceThreadId,
3117
+ clonedAt: now,
3118
+ ...lastMessageId && { lastMessageId }
3119
+ };
3120
+ const newThread = {
3121
+ id: newThreadId,
3122
+ resourceId: resourceId || sourceThread.resourceId,
3123
+ title: title || (sourceThread.title ? `Clone of ${sourceThread.title}` : void 0),
3124
+ metadata: {
3125
+ ...metadata,
3126
+ clone: cloneMetadata
3127
+ },
3128
+ createdAt: now,
3129
+ updatedAt: now
3130
+ };
3131
+ const tx = await this.#client.transaction("write");
3132
+ try {
3133
+ await tx.execute({
3134
+ sql: `INSERT INTO "${storage.TABLE_THREADS}" (id, "resourceId", title, metadata, "createdAt", "updatedAt")
3135
+ VALUES (?, ?, ?, jsonb(?), ?, ?)`,
3136
+ args: [
3137
+ newThread.id,
3138
+ newThread.resourceId,
3139
+ newThread.title || null,
3140
+ JSON.stringify(newThread.metadata),
3141
+ nowStr,
3142
+ nowStr
3143
+ ]
3144
+ });
3145
+ const clonedMessages = [];
3146
+ const targetResourceId = resourceId || sourceThread.resourceId;
3147
+ for (const sourceMsg of sourceMessages) {
3148
+ const newMessageId = crypto.randomUUID();
3149
+ const contentStr = sourceMsg.content;
3150
+ let parsedContent;
3151
+ try {
3152
+ parsedContent = JSON.parse(contentStr);
3153
+ } catch {
3154
+ parsedContent = { format: 2, parts: [{ type: "text", text: contentStr }] };
3155
+ }
3156
+ await tx.execute({
3157
+ sql: `INSERT INTO "${storage.TABLE_MESSAGES}" (id, thread_id, content, role, type, "createdAt", "resourceId")
3158
+ VALUES (?, ?, ?, ?, ?, ?, ?)`,
3159
+ args: [
3160
+ newMessageId,
3161
+ newThreadId,
3162
+ contentStr,
3163
+ sourceMsg.role,
3164
+ sourceMsg.type || "v2",
3165
+ sourceMsg.createdAt,
3166
+ targetResourceId
3167
+ ]
3168
+ });
3169
+ clonedMessages.push({
3170
+ id: newMessageId,
3171
+ threadId: newThreadId,
3172
+ content: parsedContent,
3173
+ role: sourceMsg.role,
3174
+ type: sourceMsg.type || void 0,
3175
+ createdAt: new Date(sourceMsg.createdAt),
3176
+ resourceId: targetResourceId
3177
+ });
3178
+ }
3179
+ await tx.commit();
3180
+ return {
3181
+ thread: newThread,
3182
+ clonedMessages
3183
+ };
3184
+ } catch (error) {
3185
+ await tx.rollback();
3186
+ throw error;
3187
+ }
3188
+ } catch (error$1) {
3189
+ if (error$1 instanceof error.MastraError) {
3190
+ throw error$1;
3191
+ }
3192
+ throw new error.MastraError(
3193
+ {
3194
+ id: storage.createStorageErrorId("LIBSQL", "CLONE_THREAD", "FAILED"),
3195
+ domain: error.ErrorDomain.STORAGE,
3196
+ category: error.ErrorCategory.THIRD_PARTY,
3197
+ details: { sourceThreadId, newThreadId }
3198
+ },
3199
+ error$1
3200
+ );
3201
+ }
3202
+ }
2762
3203
  };
2763
3204
  var ObservabilityLibSQL = class extends storage.ObservabilityStorage {
2764
3205
  #db;
@@ -2773,11 +3214,38 @@ var ObservabilityLibSQL = class extends storage.ObservabilityStorage {
2773
3214
  async dangerouslyClearAll() {
2774
3215
  await this.#db.deleteData({ tableName: storage.TABLE_SPANS });
2775
3216
  }
2776
- async createSpan(span) {
3217
+ /**
3218
+ * Manually run the spans migration to deduplicate and add the unique constraint.
3219
+ * This is intended to be called from the CLI when duplicates are detected.
3220
+ *
3221
+ * @returns Migration result with status and details
3222
+ */
3223
+ async migrateSpans() {
3224
+ return this.#db.migrateSpans();
3225
+ }
3226
+ /**
3227
+ * Check migration status for the spans table.
3228
+ * Returns information about whether migration is needed.
3229
+ */
3230
+ async checkSpansMigrationStatus() {
3231
+ return this.#db.checkSpansMigrationStatus();
3232
+ }
3233
+ get tracingStrategy() {
3234
+ return {
3235
+ preferred: "batch-with-updates",
3236
+ supported: ["batch-with-updates", "insert-only"]
3237
+ };
3238
+ }
3239
+ async createSpan(args) {
3240
+ const { span } = args;
2777
3241
  try {
3242
+ const startedAt = span.startedAt instanceof Date ? span.startedAt.toISOString() : span.startedAt;
3243
+ const endedAt = span.endedAt instanceof Date ? span.endedAt.toISOString() : span.endedAt;
2778
3244
  const now = (/* @__PURE__ */ new Date()).toISOString();
2779
3245
  const record = {
2780
3246
  ...span,
3247
+ startedAt,
3248
+ endedAt,
2781
3249
  createdAt: now,
2782
3250
  updatedAt: now
2783
3251
  };
@@ -2792,19 +3260,72 @@ var ObservabilityLibSQL = class extends storage.ObservabilityStorage {
2792
3260
  spanId: span.spanId,
2793
3261
  traceId: span.traceId,
2794
3262
  spanType: span.spanType,
2795
- spanName: span.name
3263
+ name: span.name
2796
3264
  }
2797
3265
  },
2798
3266
  error$1
2799
3267
  );
2800
3268
  }
2801
3269
  }
2802
- async getTrace(traceId) {
3270
+ async getSpan(args) {
3271
+ const { traceId, spanId } = args;
3272
+ try {
3273
+ const rows = await this.#db.selectMany({
3274
+ tableName: storage.TABLE_SPANS,
3275
+ whereClause: { sql: " WHERE traceId = ? AND spanId = ?", args: [traceId, spanId] },
3276
+ limit: 1
3277
+ });
3278
+ if (!rows || rows.length === 0) {
3279
+ return null;
3280
+ }
3281
+ return {
3282
+ span: transformFromSqlRow({ tableName: storage.TABLE_SPANS, sqlRow: rows[0] })
3283
+ };
3284
+ } catch (error$1) {
3285
+ throw new error.MastraError(
3286
+ {
3287
+ id: storage.createStorageErrorId("LIBSQL", "GET_SPAN", "FAILED"),
3288
+ domain: error.ErrorDomain.STORAGE,
3289
+ category: error.ErrorCategory.USER,
3290
+ details: { traceId, spanId }
3291
+ },
3292
+ error$1
3293
+ );
3294
+ }
3295
+ }
3296
+ async getRootSpan(args) {
3297
+ const { traceId } = args;
3298
+ try {
3299
+ const rows = await this.#db.selectMany({
3300
+ tableName: storage.TABLE_SPANS,
3301
+ whereClause: { sql: " WHERE traceId = ? AND parentSpanId IS NULL", args: [traceId] },
3302
+ limit: 1
3303
+ });
3304
+ if (!rows || rows.length === 0) {
3305
+ return null;
3306
+ }
3307
+ return {
3308
+ span: transformFromSqlRow({ tableName: storage.TABLE_SPANS, sqlRow: rows[0] })
3309
+ };
3310
+ } catch (error$1) {
3311
+ throw new error.MastraError(
3312
+ {
3313
+ id: storage.createStorageErrorId("LIBSQL", "GET_ROOT_SPAN", "FAILED"),
3314
+ domain: error.ErrorDomain.STORAGE,
3315
+ category: error.ErrorCategory.USER,
3316
+ details: { traceId }
3317
+ },
3318
+ error$1
3319
+ );
3320
+ }
3321
+ }
3322
+ async getTrace(args) {
3323
+ const { traceId } = args;
2803
3324
  try {
2804
3325
  const spans = await this.#db.selectMany({
2805
3326
  tableName: storage.TABLE_SPANS,
2806
3327
  whereClause: { sql: " WHERE traceId = ?", args: [traceId] },
2807
- orderBy: "startedAt DESC"
3328
+ orderBy: "startedAt ASC"
2808
3329
  });
2809
3330
  if (!spans || spans.length === 0) {
2810
3331
  return null;
@@ -2827,16 +3348,21 @@ var ObservabilityLibSQL = class extends storage.ObservabilityStorage {
2827
3348
  );
2828
3349
  }
2829
3350
  }
2830
- async updateSpan({
2831
- spanId,
2832
- traceId,
2833
- updates
2834
- }) {
3351
+ async updateSpan(args) {
3352
+ const { traceId, spanId, updates } = args;
2835
3353
  try {
3354
+ const data = { ...updates };
3355
+ if (data.endedAt instanceof Date) {
3356
+ data.endedAt = data.endedAt.toISOString();
3357
+ }
3358
+ if (data.startedAt instanceof Date) {
3359
+ data.startedAt = data.startedAt.toISOString();
3360
+ }
3361
+ data.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
2836
3362
  await this.#db.update({
2837
3363
  tableName: storage.TABLE_SPANS,
2838
3364
  keys: { spanId, traceId },
2839
- data: { ...updates, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }
3365
+ data
2840
3366
  });
2841
3367
  } catch (error$1) {
2842
3368
  throw new error.MastraError(
@@ -2853,83 +3379,175 @@ var ObservabilityLibSQL = class extends storage.ObservabilityStorage {
2853
3379
  );
2854
3380
  }
2855
3381
  }
2856
- async getTracesPaginated({
2857
- filters,
2858
- pagination
2859
- }) {
2860
- const page = pagination?.page ?? 0;
2861
- const perPage = pagination?.perPage ?? 10;
2862
- const { entityId, entityType, ...actualFilters } = filters || {};
2863
- const filtersWithDateRange = {
2864
- ...actualFilters,
2865
- ...buildDateRangeFilter(pagination?.dateRange, "startedAt"),
2866
- parentSpanId: null
2867
- };
2868
- const whereClause = prepareWhereClause(filtersWithDateRange, storage.SPAN_SCHEMA);
2869
- let actualWhereClause = whereClause.sql || "";
2870
- if (entityId && entityType) {
2871
- const statement = `name = ?`;
2872
- let name = "";
2873
- if (entityType === "workflow") {
2874
- name = `workflow run: '${entityId}'`;
2875
- } else if (entityType === "agent") {
2876
- name = `agent run: '${entityId}'`;
2877
- } else {
2878
- const error$1 = new error.MastraError({
2879
- id: storage.createStorageErrorId("LIBSQL", "GET_TRACES_PAGINATED", "INVALID_ENTITY_TYPE"),
2880
- domain: error.ErrorDomain.STORAGE,
2881
- category: error.ErrorCategory.USER,
2882
- details: {
2883
- entityType
2884
- },
2885
- text: `Cannot filter by entity type: ${entityType}`
2886
- });
2887
- this.logger?.trackException(error$1);
2888
- throw error$1;
3382
+ async listTraces(args) {
3383
+ const { filters, pagination, orderBy } = storage.listTracesArgsSchema.parse(args);
3384
+ const { page, perPage } = pagination;
3385
+ const tableName = utils.parseSqlIdentifier(storage.TABLE_SPANS, "table name");
3386
+ try {
3387
+ const conditions = ["parentSpanId IS NULL"];
3388
+ const queryArgs = [];
3389
+ if (filters) {
3390
+ if (filters.startedAt?.start) {
3391
+ conditions.push(`startedAt >= ?`);
3392
+ queryArgs.push(filters.startedAt.start.toISOString());
3393
+ }
3394
+ if (filters.startedAt?.end) {
3395
+ conditions.push(`startedAt <= ?`);
3396
+ queryArgs.push(filters.startedAt.end.toISOString());
3397
+ }
3398
+ if (filters.endedAt?.start) {
3399
+ conditions.push(`endedAt >= ?`);
3400
+ queryArgs.push(filters.endedAt.start.toISOString());
3401
+ }
3402
+ if (filters.endedAt?.end) {
3403
+ conditions.push(`endedAt <= ?`);
3404
+ queryArgs.push(filters.endedAt.end.toISOString());
3405
+ }
3406
+ if (filters.spanType !== void 0) {
3407
+ conditions.push(`spanType = ?`);
3408
+ queryArgs.push(filters.spanType);
3409
+ }
3410
+ if (filters.entityType !== void 0) {
3411
+ conditions.push(`entityType = ?`);
3412
+ queryArgs.push(filters.entityType);
3413
+ }
3414
+ if (filters.entityId !== void 0) {
3415
+ conditions.push(`entityId = ?`);
3416
+ queryArgs.push(filters.entityId);
3417
+ }
3418
+ if (filters.entityName !== void 0) {
3419
+ conditions.push(`entityName = ?`);
3420
+ queryArgs.push(filters.entityName);
3421
+ }
3422
+ if (filters.userId !== void 0) {
3423
+ conditions.push(`userId = ?`);
3424
+ queryArgs.push(filters.userId);
3425
+ }
3426
+ if (filters.organizationId !== void 0) {
3427
+ conditions.push(`organizationId = ?`);
3428
+ queryArgs.push(filters.organizationId);
3429
+ }
3430
+ if (filters.resourceId !== void 0) {
3431
+ conditions.push(`resourceId = ?`);
3432
+ queryArgs.push(filters.resourceId);
3433
+ }
3434
+ if (filters.runId !== void 0) {
3435
+ conditions.push(`runId = ?`);
3436
+ queryArgs.push(filters.runId);
3437
+ }
3438
+ if (filters.sessionId !== void 0) {
3439
+ conditions.push(`sessionId = ?`);
3440
+ queryArgs.push(filters.sessionId);
3441
+ }
3442
+ if (filters.threadId !== void 0) {
3443
+ conditions.push(`threadId = ?`);
3444
+ queryArgs.push(filters.threadId);
3445
+ }
3446
+ if (filters.requestId !== void 0) {
3447
+ conditions.push(`requestId = ?`);
3448
+ queryArgs.push(filters.requestId);
3449
+ }
3450
+ if (filters.environment !== void 0) {
3451
+ conditions.push(`environment = ?`);
3452
+ queryArgs.push(filters.environment);
3453
+ }
3454
+ if (filters.source !== void 0) {
3455
+ conditions.push(`source = ?`);
3456
+ queryArgs.push(filters.source);
3457
+ }
3458
+ if (filters.serviceName !== void 0) {
3459
+ conditions.push(`serviceName = ?`);
3460
+ queryArgs.push(filters.serviceName);
3461
+ }
3462
+ if (filters.scope != null) {
3463
+ for (const [key, value] of Object.entries(filters.scope)) {
3464
+ if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(key)) {
3465
+ throw new error.MastraError({
3466
+ id: storage.createStorageErrorId("LIBSQL", "LIST_TRACES", "INVALID_FILTER_KEY"),
3467
+ domain: error.ErrorDomain.STORAGE,
3468
+ category: error.ErrorCategory.USER,
3469
+ details: { key }
3470
+ });
3471
+ }
3472
+ conditions.push(`json_extract(scope, '$.${key}') = ?`);
3473
+ queryArgs.push(typeof value === "string" ? value : JSON.stringify(value));
3474
+ }
3475
+ }
3476
+ if (filters.metadata != null) {
3477
+ for (const [key, value] of Object.entries(filters.metadata)) {
3478
+ if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(key)) {
3479
+ throw new error.MastraError({
3480
+ id: storage.createStorageErrorId("LIBSQL", "LIST_TRACES", "INVALID_FILTER_KEY"),
3481
+ domain: error.ErrorDomain.STORAGE,
3482
+ category: error.ErrorCategory.USER,
3483
+ details: { key }
3484
+ });
3485
+ }
3486
+ conditions.push(`json_extract(metadata, '$.${key}') = ?`);
3487
+ queryArgs.push(typeof value === "string" ? value : JSON.stringify(value));
3488
+ }
3489
+ }
3490
+ if (filters.tags != null && filters.tags.length > 0) {
3491
+ for (const tag of filters.tags) {
3492
+ conditions.push(`EXISTS (SELECT 1 FROM json_each(${tableName}.tags) WHERE value = ?)`);
3493
+ queryArgs.push(tag);
3494
+ }
3495
+ }
3496
+ if (filters.status !== void 0) {
3497
+ switch (filters.status) {
3498
+ case storage.TraceStatus.ERROR:
3499
+ conditions.push(`error IS NOT NULL`);
3500
+ break;
3501
+ case storage.TraceStatus.RUNNING:
3502
+ conditions.push(`endedAt IS NULL AND error IS NULL`);
3503
+ break;
3504
+ case storage.TraceStatus.SUCCESS:
3505
+ conditions.push(`endedAt IS NOT NULL AND error IS NULL`);
3506
+ break;
3507
+ }
3508
+ }
3509
+ if (filters.hasChildError !== void 0) {
3510
+ if (filters.hasChildError) {
3511
+ conditions.push(`EXISTS (
3512
+ SELECT 1 FROM ${tableName} c
3513
+ WHERE c.traceId = ${tableName}.traceId AND c.error IS NOT NULL
3514
+ )`);
3515
+ } else {
3516
+ conditions.push(`NOT EXISTS (
3517
+ SELECT 1 FROM ${tableName} c
3518
+ WHERE c.traceId = ${tableName}.traceId AND c.error IS NOT NULL
3519
+ )`);
3520
+ }
3521
+ }
2889
3522
  }
2890
- whereClause.args.push(name);
2891
- if (actualWhereClause) {
2892
- actualWhereClause += ` AND ${statement}`;
3523
+ const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
3524
+ const sortField = orderBy.field;
3525
+ const sortDirection = orderBy.direction;
3526
+ let orderByClause;
3527
+ if (sortField === "endedAt") {
3528
+ orderByClause = sortDirection === "DESC" ? `CASE WHEN ${sortField} IS NULL THEN 0 ELSE 1 END, ${sortField} DESC` : `CASE WHEN ${sortField} IS NULL THEN 1 ELSE 0 END, ${sortField} ASC`;
2893
3529
  } else {
2894
- actualWhereClause += `WHERE ${statement}`;
3530
+ orderByClause = `${sortField} ${sortDirection}`;
2895
3531
  }
2896
- }
2897
- const orderBy = "startedAt DESC";
2898
- let count = 0;
2899
- try {
2900
- count = await this.#db.selectTotalCount({
3532
+ const count = await this.#db.selectTotalCount({
2901
3533
  tableName: storage.TABLE_SPANS,
2902
- whereClause: { sql: actualWhereClause, args: whereClause.args }
3534
+ whereClause: { sql: whereClause, args: queryArgs }
2903
3535
  });
2904
- } catch (error$1) {
2905
- throw new error.MastraError(
2906
- {
2907
- id: storage.createStorageErrorId("LIBSQL", "GET_TRACES_PAGINATED", "COUNT_FAILED"),
2908
- domain: error.ErrorDomain.STORAGE,
2909
- category: error.ErrorCategory.USER
2910
- },
2911
- error$1
2912
- );
2913
- }
2914
- if (count === 0) {
2915
- return {
2916
- pagination: {
2917
- total: 0,
2918
- page,
2919
- perPage,
2920
- hasMore: false
2921
- },
2922
- spans: []
2923
- };
2924
- }
2925
- try {
3536
+ if (count === 0) {
3537
+ return {
3538
+ pagination: {
3539
+ total: 0,
3540
+ page,
3541
+ perPage,
3542
+ hasMore: false
3543
+ },
3544
+ spans: []
3545
+ };
3546
+ }
2926
3547
  const spans = await this.#db.selectMany({
2927
3548
  tableName: storage.TABLE_SPANS,
2928
- whereClause: {
2929
- sql: actualWhereClause,
2930
- args: whereClause.args
2931
- },
2932
- orderBy,
3549
+ whereClause: { sql: whereClause, args: queryArgs },
3550
+ orderBy: orderByClause,
2933
3551
  offset: page * perPage,
2934
3552
  limit: perPage
2935
3553
  });
@@ -2938,14 +3556,14 @@ var ObservabilityLibSQL = class extends storage.ObservabilityStorage {
2938
3556
  total: count,
2939
3557
  page,
2940
3558
  perPage,
2941
- hasMore: spans.length === perPage
3559
+ hasMore: (page + 1) * perPage < count
2942
3560
  },
2943
3561
  spans: spans.map((span) => transformFromSqlRow({ tableName: storage.TABLE_SPANS, sqlRow: span }))
2944
3562
  };
2945
3563
  } catch (error$1) {
2946
3564
  throw new error.MastraError(
2947
3565
  {
2948
- id: storage.createStorageErrorId("LIBSQL", "GET_TRACES_PAGINATED", "FAILED"),
3566
+ id: storage.createStorageErrorId("LIBSQL", "LIST_TRACES", "FAILED"),
2949
3567
  domain: error.ErrorDomain.STORAGE,
2950
3568
  category: error.ErrorCategory.USER
2951
3569
  },
@@ -2956,13 +3574,20 @@ var ObservabilityLibSQL = class extends storage.ObservabilityStorage {
2956
3574
  async batchCreateSpans(args) {
2957
3575
  try {
2958
3576
  const now = (/* @__PURE__ */ new Date()).toISOString();
2959
- return this.#db.batchInsert({
2960
- tableName: storage.TABLE_SPANS,
2961
- records: args.records.map((record) => ({
3577
+ const records = args.records.map((record) => {
3578
+ const startedAt = record.startedAt instanceof Date ? record.startedAt.toISOString() : record.startedAt;
3579
+ const endedAt = record.endedAt instanceof Date ? record.endedAt.toISOString() : record.endedAt;
3580
+ return {
2962
3581
  ...record,
3582
+ startedAt,
3583
+ endedAt,
2963
3584
  createdAt: now,
2964
3585
  updatedAt: now
2965
- }))
3586
+ };
3587
+ });
3588
+ return this.#db.batchInsert({
3589
+ tableName: storage.TABLE_SPANS,
3590
+ records
2966
3591
  });
2967
3592
  } catch (error$1) {
2968
3593
  throw new error.MastraError(
@@ -2976,13 +3601,24 @@ var ObservabilityLibSQL = class extends storage.ObservabilityStorage {
2976
3601
  }
2977
3602
  }
2978
3603
  async batchUpdateSpans(args) {
3604
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2979
3605
  try {
2980
3606
  return this.#db.batchUpdate({
2981
3607
  tableName: storage.TABLE_SPANS,
2982
- updates: args.records.map((record) => ({
2983
- keys: { spanId: record.spanId, traceId: record.traceId },
2984
- data: { ...record.updates, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }
2985
- }))
3608
+ updates: args.records.map((record) => {
3609
+ const data = { ...record.updates };
3610
+ if (data.endedAt instanceof Date) {
3611
+ data.endedAt = data.endedAt.toISOString();
3612
+ }
3613
+ if (data.startedAt instanceof Date) {
3614
+ data.startedAt = data.startedAt.toISOString();
3615
+ }
3616
+ data.updatedAt = now;
3617
+ return {
3618
+ keys: { spanId: record.spanId, traceId: record.traceId },
3619
+ data
3620
+ };
3621
+ })
2986
3622
  });
2987
3623
  } catch (error$1) {
2988
3624
  throw new error.MastraError(
@@ -3061,7 +3697,7 @@ var ScoresLibSQL = class extends storage.ScoresStorage {
3061
3697
  const limitValue = perPageInput === false ? total : perPage;
3062
3698
  const end = perPageInput === false ? total : start + perPage;
3063
3699
  const result = await this.#client.execute({
3064
- sql: `SELECT * FROM ${storage.TABLE_SCORERS} WHERE runId = ? ORDER BY createdAt DESC LIMIT ? OFFSET ?`,
3700
+ sql: `SELECT ${buildSelectColumns(storage.TABLE_SCORERS)} FROM ${storage.TABLE_SCORERS} WHERE runId = ? ORDER BY createdAt DESC LIMIT ? OFFSET ?`,
3065
3701
  args: [runId, limitValue, start]
3066
3702
  });
3067
3703
  const scores = result.rows?.map((row) => this.transformScoreRow(row)) ?? [];
@@ -3134,7 +3770,7 @@ var ScoresLibSQL = class extends storage.ScoresStorage {
3134
3770
  const limitValue = perPageInput === false ? total : perPage;
3135
3771
  const end = perPageInput === false ? total : start + perPage;
3136
3772
  const result = await this.#client.execute({
3137
- sql: `SELECT * FROM ${storage.TABLE_SCORERS} ${whereClause} ORDER BY createdAt DESC LIMIT ? OFFSET ?`,
3773
+ sql: `SELECT ${buildSelectColumns(storage.TABLE_SCORERS)} FROM ${storage.TABLE_SCORERS} ${whereClause} ORDER BY createdAt DESC LIMIT ? OFFSET ?`,
3138
3774
  args: [...queryParams, limitValue, start]
3139
3775
  });
3140
3776
  const scores = result.rows?.map((row) => this.transformScoreRow(row)) ?? [];
@@ -3160,16 +3796,13 @@ var ScoresLibSQL = class extends storage.ScoresStorage {
3160
3796
  }
3161
3797
  /**
3162
3798
  * LibSQL-specific score row transformation.
3163
- * Maps additionalLLMContext column to additionalContext field.
3164
3799
  */
3165
3800
  transformScoreRow(row) {
3166
- return storage.transformScoreRow(row, {
3167
- fieldMappings: { additionalContext: "additionalLLMContext" }
3168
- });
3801
+ return storage.transformScoreRow(row);
3169
3802
  }
3170
3803
  async getScoreById({ id }) {
3171
3804
  const result = await this.#client.execute({
3172
- sql: `SELECT * FROM ${storage.TABLE_SCORERS} WHERE id = ?`,
3805
+ sql: `SELECT ${buildSelectColumns(storage.TABLE_SCORERS)} FROM ${storage.TABLE_SCORERS} WHERE id = ?`,
3173
3806
  args: [id]
3174
3807
  });
3175
3808
  return result.rows?.[0] ? this.transformScoreRow(result.rows[0]) : null;
@@ -3185,7 +3818,7 @@ var ScoresLibSQL = class extends storage.ScoresStorage {
3185
3818
  domain: error.ErrorDomain.STORAGE,
3186
3819
  category: error.ErrorCategory.USER,
3187
3820
  details: {
3188
- scorer: score.scorer?.id ?? "unknown",
3821
+ scorer: typeof score.scorer?.id === "string" ? score.scorer.id : String(score.scorer?.id ?? "unknown"),
3189
3822
  entityId: score.entityId ?? "unknown",
3190
3823
  entityType: score.entityType ?? "unknown",
3191
3824
  traceId: score.traceId ?? "",
@@ -3247,7 +3880,7 @@ var ScoresLibSQL = class extends storage.ScoresStorage {
3247
3880
  const limitValue = perPageInput === false ? total : perPage;
3248
3881
  const end = perPageInput === false ? total : start + perPage;
3249
3882
  const result = await this.#client.execute({
3250
- sql: `SELECT * FROM ${storage.TABLE_SCORERS} WHERE entityId = ? AND entityType = ? ORDER BY createdAt DESC LIMIT ? OFFSET ?`,
3883
+ sql: `SELECT ${buildSelectColumns(storage.TABLE_SCORERS)} FROM ${storage.TABLE_SCORERS} WHERE entityId = ? AND entityType = ? ORDER BY createdAt DESC LIMIT ? OFFSET ?`,
3251
3884
  args: [entityId, entityType, limitValue, start]
3252
3885
  });
3253
3886
  const scores = result.rows?.map((row) => this.transformScoreRow(row)) ?? [];
@@ -3288,7 +3921,7 @@ var ScoresLibSQL = class extends storage.ScoresStorage {
3288
3921
  const limitValue = perPageInput === false ? total : perPage;
3289
3922
  const end = perPageInput === false ? total : start + perPage;
3290
3923
  const result = await this.#client.execute({
3291
- sql: `SELECT * FROM ${storage.TABLE_SCORERS} WHERE traceId = ? AND spanId = ? ORDER BY createdAt DESC LIMIT ? OFFSET ?`,
3924
+ sql: `SELECT ${buildSelectColumns(storage.TABLE_SCORERS)} FROM ${storage.TABLE_SCORERS} WHERE traceId = ? AND spanId = ? ORDER BY createdAt DESC LIMIT ? OFFSET ?`,
3292
3925
  args: [traceId, spanId, limitValue, start]
3293
3926
  });
3294
3927
  const scores = result.rows?.map((row) => this.transformScoreRow(row)) ?? [];
@@ -3313,24 +3946,6 @@ var ScoresLibSQL = class extends storage.ScoresStorage {
3313
3946
  }
3314
3947
  }
3315
3948
  };
3316
- function parseWorkflowRun(row) {
3317
- let parsedSnapshot = row.snapshot;
3318
- if (typeof parsedSnapshot === "string") {
3319
- try {
3320
- parsedSnapshot = JSON.parse(row.snapshot);
3321
- } catch (e) {
3322
- console.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
3323
- }
3324
- }
3325
- return {
3326
- workflowName: row.workflow_name,
3327
- runId: row.run_id,
3328
- snapshot: parsedSnapshot,
3329
- resourceId: row.resourceId,
3330
- createdAt: new Date(row.createdAt),
3331
- updatedAt: new Date(row.updatedAt)
3332
- };
3333
- }
3334
3949
  var WorkflowsLibSQL = class extends storage.WorkflowsStorage {
3335
3950
  #db;
3336
3951
  #client;
@@ -3351,6 +3966,24 @@ var WorkflowsLibSQL = class extends storage.WorkflowsStorage {
3351
3966
  (err) => this.logger.warn("LibSQL Workflows: Failed to setup PRAGMA settings.", err)
3352
3967
  );
3353
3968
  }
3969
+ parseWorkflowRun(row) {
3970
+ let parsedSnapshot = row.snapshot;
3971
+ if (typeof parsedSnapshot === "string") {
3972
+ try {
3973
+ parsedSnapshot = JSON.parse(row.snapshot);
3974
+ } catch (e) {
3975
+ this.logger.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
3976
+ }
3977
+ }
3978
+ return {
3979
+ workflowName: row.workflow_name,
3980
+ runId: row.run_id,
3981
+ snapshot: parsedSnapshot,
3982
+ resourceId: row.resourceId,
3983
+ createdAt: new Date(row.createdAt),
3984
+ updatedAt: new Date(row.updatedAt)
3985
+ };
3986
+ }
3354
3987
  async init() {
3355
3988
  const schema = storage.TABLE_SCHEMAS[storage.TABLE_WORKFLOW_SNAPSHOT];
3356
3989
  await this.#db.createTable({ tableName: storage.TABLE_WORKFLOW_SNAPSHOT, schema });
@@ -3394,7 +4027,7 @@ var WorkflowsLibSQL = class extends storage.WorkflowsStorage {
3394
4027
  const tx = await this.#client.transaction("write");
3395
4028
  try {
3396
4029
  const existingSnapshotResult = await tx.execute({
3397
- sql: `SELECT snapshot FROM ${storage.TABLE_WORKFLOW_SNAPSHOT} WHERE workflow_name = ? AND run_id = ?`,
4030
+ sql: `SELECT json(snapshot) as snapshot FROM ${storage.TABLE_WORKFLOW_SNAPSHOT} WHERE workflow_name = ? AND run_id = ?`,
3398
4031
  args: [workflowName, runId]
3399
4032
  });
3400
4033
  let snapshot;
@@ -3419,9 +4052,13 @@ var WorkflowsLibSQL = class extends storage.WorkflowsStorage {
3419
4052
  }
3420
4053
  snapshot.context[stepId] = result;
3421
4054
  snapshot.requestContext = { ...snapshot.requestContext, ...requestContext };
4055
+ const now = (/* @__PURE__ */ new Date()).toISOString();
3422
4056
  await tx.execute({
3423
- sql: `UPDATE ${storage.TABLE_WORKFLOW_SNAPSHOT} SET snapshot = ? WHERE workflow_name = ? AND run_id = ?`,
3424
- args: [JSON.stringify(snapshot), workflowName, runId]
4057
+ sql: `INSERT INTO ${storage.TABLE_WORKFLOW_SNAPSHOT} (workflow_name, run_id, snapshot, createdAt, updatedAt)
4058
+ VALUES (?, ?, jsonb(?), ?, ?)
4059
+ ON CONFLICT(workflow_name, run_id)
4060
+ DO UPDATE SET snapshot = excluded.snapshot, updatedAt = excluded.updatedAt`,
4061
+ args: [workflowName, runId, JSON.stringify(snapshot), now, now]
3425
4062
  });
3426
4063
  await tx.commit();
3427
4064
  return snapshot.context;
@@ -3442,7 +4079,7 @@ var WorkflowsLibSQL = class extends storage.WorkflowsStorage {
3442
4079
  const tx = await this.#client.transaction("write");
3443
4080
  try {
3444
4081
  const existingSnapshotResult = await tx.execute({
3445
- sql: `SELECT snapshot FROM ${storage.TABLE_WORKFLOW_SNAPSHOT} WHERE workflow_name = ? AND run_id = ?`,
4082
+ sql: `SELECT json(snapshot) as snapshot FROM ${storage.TABLE_WORKFLOW_SNAPSHOT} WHERE workflow_name = ? AND run_id = ?`,
3446
4083
  args: [workflowName, runId]
3447
4084
  });
3448
4085
  if (!existingSnapshotResult.rows?.[0]) {
@@ -3457,7 +4094,7 @@ var WorkflowsLibSQL = class extends storage.WorkflowsStorage {
3457
4094
  }
3458
4095
  const updatedSnapshot = { ...snapshot, ...opts };
3459
4096
  await tx.execute({
3460
- sql: `UPDATE ${storage.TABLE_WORKFLOW_SNAPSHOT} SET snapshot = ? WHERE workflow_name = ? AND run_id = ?`,
4097
+ sql: `UPDATE ${storage.TABLE_WORKFLOW_SNAPSHOT} SET snapshot = jsonb(?) WHERE workflow_name = ? AND run_id = ?`,
3461
4098
  args: [JSON.stringify(updatedSnapshot), workflowName, runId]
3462
4099
  });
3463
4100
  await tx.commit();
@@ -3521,13 +4158,13 @@ var WorkflowsLibSQL = class extends storage.WorkflowsStorage {
3521
4158
  const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
3522
4159
  try {
3523
4160
  const result = await this.#client.execute({
3524
- sql: `SELECT * FROM ${storage.TABLE_WORKFLOW_SNAPSHOT} ${whereClause} ORDER BY createdAt DESC LIMIT 1`,
4161
+ sql: `SELECT workflow_name, run_id, resourceId, json(snapshot) as snapshot, createdAt, updatedAt FROM ${storage.TABLE_WORKFLOW_SNAPSHOT} ${whereClause} ORDER BY createdAt DESC LIMIT 1`,
3525
4162
  args
3526
4163
  });
3527
4164
  if (!result.rows?.[0]) {
3528
4165
  return null;
3529
4166
  }
3530
- return parseWorkflowRun(result.rows[0]);
4167
+ return this.parseWorkflowRun(result.rows[0]);
3531
4168
  } catch (error$1) {
3532
4169
  throw new error.MastraError(
3533
4170
  {
@@ -3593,7 +4230,7 @@ var WorkflowsLibSQL = class extends storage.WorkflowsStorage {
3593
4230
  conditions.push("resourceId = ?");
3594
4231
  args.push(resourceId);
3595
4232
  } else {
3596
- console.warn(`[${storage.TABLE_WORKFLOW_SNAPSHOT}] resourceId column not found. Skipping resourceId filter.`);
4233
+ this.logger.warn(`[${storage.TABLE_WORKFLOW_SNAPSHOT}] resourceId column not found. Skipping resourceId filter.`);
3597
4234
  }
3598
4235
  }
3599
4236
  const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
@@ -3609,10 +4246,10 @@ var WorkflowsLibSQL = class extends storage.WorkflowsStorage {
3609
4246
  const normalizedPerPage = usePagination ? storage.normalizePerPage(perPage, Number.MAX_SAFE_INTEGER) : 0;
3610
4247
  const offset = usePagination ? page * normalizedPerPage : 0;
3611
4248
  const result = await this.#client.execute({
3612
- sql: `SELECT * FROM ${storage.TABLE_WORKFLOW_SNAPSHOT} ${whereClause} ORDER BY createdAt DESC${usePagination ? ` LIMIT ? OFFSET ?` : ""}`,
4249
+ sql: `SELECT workflow_name, run_id, resourceId, json(snapshot) as snapshot, createdAt, updatedAt FROM ${storage.TABLE_WORKFLOW_SNAPSHOT} ${whereClause} ORDER BY createdAt DESC${usePagination ? ` LIMIT ? OFFSET ?` : ""}`,
3613
4250
  args: usePagination ? [...args, normalizedPerPage, offset] : args
3614
4251
  });
3615
- const runs = (result.rows || []).map((row) => parseWorkflowRun(row));
4252
+ const runs = (result.rows || []).map((row) => this.parseWorkflowRun(row));
3616
4253
  return { runs, total: total || runs.length };
3617
4254
  } catch (error$1) {
3618
4255
  throw new error.MastraError(
@@ -3628,7 +4265,7 @@ var WorkflowsLibSQL = class extends storage.WorkflowsStorage {
3628
4265
  };
3629
4266
 
3630
4267
  // src/storage/index.ts
3631
- var LibSQLStore = class extends storage.MastraStorage {
4268
+ var LibSQLStore = class extends storage.MastraCompositeStore {
3632
4269
  client;
3633
4270
  maxRetries;
3634
4271
  initialBackoffMs;
@@ -3673,160 +4310,6 @@ var LibSQLStore = class extends storage.MastraStorage {
3673
4310
  agents
3674
4311
  };
3675
4312
  }
3676
- get supports() {
3677
- return {
3678
- selectByIncludeResourceScope: true,
3679
- resourceWorkingMemory: true,
3680
- hasColumn: true,
3681
- createTable: true,
3682
- deleteMessages: true,
3683
- observabilityInstance: true,
3684
- listScoresBySpan: true,
3685
- agents: true
3686
- };
3687
- }
3688
- async getThreadById({ threadId }) {
3689
- return this.stores.memory.getThreadById({ threadId });
3690
- }
3691
- async saveThread({ thread }) {
3692
- return this.stores.memory.saveThread({ thread });
3693
- }
3694
- async updateThread({
3695
- id,
3696
- title,
3697
- metadata
3698
- }) {
3699
- return this.stores.memory.updateThread({ id, title, metadata });
3700
- }
3701
- async deleteThread({ threadId }) {
3702
- return this.stores.memory.deleteThread({ threadId });
3703
- }
3704
- async listMessagesById({ messageIds }) {
3705
- return this.stores.memory.listMessagesById({ messageIds });
3706
- }
3707
- async saveMessages(args) {
3708
- const result = await this.stores.memory.saveMessages({ messages: args.messages });
3709
- return { messages: result.messages };
3710
- }
3711
- async updateMessages({
3712
- messages
3713
- }) {
3714
- return this.stores.memory.updateMessages({ messages });
3715
- }
3716
- async deleteMessages(messageIds) {
3717
- return this.stores.memory.deleteMessages(messageIds);
3718
- }
3719
- async getScoreById({ id }) {
3720
- return this.stores.scores.getScoreById({ id });
3721
- }
3722
- async saveScore(score) {
3723
- return this.stores.scores.saveScore(score);
3724
- }
3725
- async listScoresByScorerId({
3726
- scorerId,
3727
- entityId,
3728
- entityType,
3729
- source,
3730
- pagination
3731
- }) {
3732
- return this.stores.scores.listScoresByScorerId({ scorerId, entityId, entityType, source, pagination });
3733
- }
3734
- async listScoresByRunId({
3735
- runId,
3736
- pagination
3737
- }) {
3738
- return this.stores.scores.listScoresByRunId({ runId, pagination });
3739
- }
3740
- async listScoresByEntityId({
3741
- entityId,
3742
- entityType,
3743
- pagination
3744
- }) {
3745
- return this.stores.scores.listScoresByEntityId({ entityId, entityType, pagination });
3746
- }
3747
- /**
3748
- * WORKFLOWS
3749
- */
3750
- async updateWorkflowResults({
3751
- workflowName,
3752
- runId,
3753
- stepId,
3754
- result,
3755
- requestContext
3756
- }) {
3757
- return this.stores.workflows.updateWorkflowResults({ workflowName, runId, stepId, result, requestContext });
3758
- }
3759
- async updateWorkflowState({
3760
- workflowName,
3761
- runId,
3762
- opts
3763
- }) {
3764
- return this.stores.workflows.updateWorkflowState({ workflowName, runId, opts });
3765
- }
3766
- async persistWorkflowSnapshot({
3767
- workflowName,
3768
- runId,
3769
- resourceId,
3770
- snapshot
3771
- }) {
3772
- return this.stores.workflows.persistWorkflowSnapshot({ workflowName, runId, resourceId, snapshot });
3773
- }
3774
- async loadWorkflowSnapshot({
3775
- workflowName,
3776
- runId
3777
- }) {
3778
- return this.stores.workflows.loadWorkflowSnapshot({ workflowName, runId });
3779
- }
3780
- async listWorkflowRuns(args = {}) {
3781
- return this.stores.workflows.listWorkflowRuns(args);
3782
- }
3783
- async getWorkflowRunById({
3784
- runId,
3785
- workflowName
3786
- }) {
3787
- return this.stores.workflows.getWorkflowRunById({ runId, workflowName });
3788
- }
3789
- async deleteWorkflowRunById({ runId, workflowName }) {
3790
- return this.stores.workflows.deleteWorkflowRunById({ runId, workflowName });
3791
- }
3792
- async getResourceById({ resourceId }) {
3793
- return this.stores.memory.getResourceById({ resourceId });
3794
- }
3795
- async saveResource({ resource }) {
3796
- return this.stores.memory.saveResource({ resource });
3797
- }
3798
- async updateResource({
3799
- resourceId,
3800
- workingMemory,
3801
- metadata
3802
- }) {
3803
- return this.stores.memory.updateResource({ resourceId, workingMemory, metadata });
3804
- }
3805
- async createSpan(span) {
3806
- return this.stores.observability.createSpan(span);
3807
- }
3808
- async updateSpan(params) {
3809
- return this.stores.observability.updateSpan(params);
3810
- }
3811
- async getTrace(traceId) {
3812
- return this.stores.observability.getTrace(traceId);
3813
- }
3814
- async getTracesPaginated(args) {
3815
- return this.stores.observability.getTracesPaginated(args);
3816
- }
3817
- async listScoresBySpan({
3818
- traceId,
3819
- spanId,
3820
- pagination
3821
- }) {
3822
- return this.stores.scores.listScoresBySpan({ traceId, spanId, pagination });
3823
- }
3824
- async batchCreateSpans(args) {
3825
- return this.stores.observability.batchCreateSpans(args);
3826
- }
3827
- async batchUpdateSpans(args) {
3828
- return this.stores.observability.batchUpdateSpans(args);
3829
- }
3830
4313
  };
3831
4314
 
3832
4315
  // src/vector/prompt.ts
@@ -3928,9 +4411,14 @@ Example Complex Query:
3928
4411
  ]
3929
4412
  }`;
3930
4413
 
4414
+ exports.AgentsLibSQL = AgentsLibSQL;
3931
4415
  exports.DefaultStorage = LibSQLStore;
3932
4416
  exports.LIBSQL_PROMPT = LIBSQL_PROMPT;
3933
4417
  exports.LibSQLStore = LibSQLStore;
3934
4418
  exports.LibSQLVector = LibSQLVector;
4419
+ exports.MemoryLibSQL = MemoryLibSQL;
4420
+ exports.ObservabilityLibSQL = ObservabilityLibSQL;
4421
+ exports.ScoresLibSQL = ScoresLibSQL;
4422
+ exports.WorkflowsLibSQL = WorkflowsLibSQL;
3935
4423
  //# sourceMappingURL=index.cjs.map
3936
4424
  //# sourceMappingURL=index.cjs.map