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