@mastra/libsql 0.0.0-bundle-recursion-20251030002519 → 0.0.0-bundle-studio-cloud-20251222034739

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 (31) hide show
  1. package/CHANGELOG.md +861 -3
  2. package/README.md +30 -20
  3. package/dist/index.cjs +2098 -1668
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.js +2032 -1602
  6. package/dist/index.js.map +1 -1
  7. package/dist/storage/db/index.d.ts +259 -0
  8. package/dist/storage/db/index.d.ts.map +1 -0
  9. package/dist/storage/{domains → db}/utils.d.ts +4 -0
  10. package/dist/storage/db/utils.d.ts.map +1 -0
  11. package/dist/storage/domains/agents/index.d.ts +23 -0
  12. package/dist/storage/domains/agents/index.d.ts.map +1 -0
  13. package/dist/storage/domains/memory/index.d.ts +19 -53
  14. package/dist/storage/domains/memory/index.d.ts.map +1 -1
  15. package/dist/storage/domains/observability/index.d.ts +17 -17
  16. package/dist/storage/domains/observability/index.d.ts.map +1 -1
  17. package/dist/storage/domains/scores/index.d.ts +15 -14
  18. package/dist/storage/domains/scores/index.d.ts.map +1 -1
  19. package/dist/storage/domains/workflows/index.d.ts +18 -32
  20. package/dist/storage/domains/workflows/index.d.ts.map +1 -1
  21. package/dist/storage/index.d.ts +70 -128
  22. package/dist/storage/index.d.ts.map +1 -1
  23. package/dist/vector/index.d.ts +7 -3
  24. package/dist/vector/index.d.ts.map +1 -1
  25. package/dist/vector/sql-builder.d.ts.map +1 -1
  26. package/package.json +12 -8
  27. package/dist/storage/domains/legacy-evals/index.d.ts +0 -18
  28. package/dist/storage/domains/legacy-evals/index.d.ts.map +0 -1
  29. package/dist/storage/domains/operations/index.d.ts +0 -110
  30. package/dist/storage/domains/operations/index.d.ts.map +0 -1
  31. package/dist/storage/domains/utils.d.ts.map +0 -1
package/dist/index.cjs CHANGED
@@ -2,12 +2,13 @@
2
2
 
3
3
  var client = require('@libsql/client');
4
4
  var error = require('@mastra/core/error');
5
+ var storage = require('@mastra/core/storage');
5
6
  var utils = require('@mastra/core/utils');
6
7
  var vector = require('@mastra/core/vector');
7
8
  var filter = require('@mastra/core/vector/filter');
8
- var storage = require('@mastra/core/storage');
9
+ var base = require('@mastra/core/base');
9
10
  var agent = require('@mastra/core/agent');
10
- var scores = require('@mastra/core/scores');
11
+ var evals = require('@mastra/core/evals');
11
12
 
12
13
  // src/vector/index.ts
13
14
  var LibSQLFilterTranslator = class extends filter.BaseFilterTranslator {
@@ -92,12 +93,20 @@ var createBasicOperator = (symbol) => {
92
93
  };
93
94
  };
94
95
  var createNumericOperator = (symbol) => {
95
- return (key) => {
96
+ return (key, value) => {
96
97
  const jsonPath = getJsonPath(key);
97
- return {
98
- sql: `CAST(json_extract(metadata, ${jsonPath}) AS NUMERIC) ${symbol} ?`,
99
- needsValue: true
100
- };
98
+ const isNumeric = typeof value === "number" || typeof value === "string" && !isNaN(Number(value)) && value.trim() !== "";
99
+ if (isNumeric) {
100
+ return {
101
+ sql: `CAST(json_extract(metadata, ${jsonPath}) AS NUMERIC) ${symbol} ?`,
102
+ needsValue: true
103
+ };
104
+ } else {
105
+ return {
106
+ sql: `CAST(json_extract(metadata, ${jsonPath}) AS TEXT) ${symbol} ?`,
107
+ needsValue: true
108
+ };
109
+ }
101
110
  };
102
111
  };
103
112
  var validateJsonArray = (key) => {
@@ -507,9 +516,10 @@ var LibSQLVector = class extends vector.MastraVector {
507
516
  syncUrl,
508
517
  syncInterval,
509
518
  maxRetries = 5,
510
- initialBackoffMs = 100
519
+ initialBackoffMs = 100,
520
+ id
511
521
  }) {
512
- super();
522
+ super({ id });
513
523
  this.turso = client.createClient({
514
524
  url: connectionUrl,
515
525
  syncUrl,
@@ -574,7 +584,7 @@ var LibSQLVector = class extends vector.MastraVector {
574
584
  } catch (error$1) {
575
585
  throw new error.MastraError(
576
586
  {
577
- id: "LIBSQL_VECTOR_QUERY_INVALID_ARGS",
587
+ id: storage.createVectorErrorId("LIBSQL", "QUERY", "INVALID_ARGS"),
578
588
  domain: error.ErrorDomain.STORAGE,
579
589
  category: error.ErrorCategory.USER
580
590
  },
@@ -616,7 +626,7 @@ var LibSQLVector = class extends vector.MastraVector {
616
626
  } catch (error$1) {
617
627
  throw new error.MastraError(
618
628
  {
619
- id: "LIBSQL_VECTOR_QUERY_FAILED",
629
+ id: storage.createVectorErrorId("LIBSQL", "QUERY", "FAILED"),
620
630
  domain: error.ErrorDomain.STORAGE,
621
631
  category: error.ErrorCategory.THIRD_PARTY
622
632
  },
@@ -630,7 +640,7 @@ var LibSQLVector = class extends vector.MastraVector {
630
640
  } catch (error$1) {
631
641
  throw new error.MastraError(
632
642
  {
633
- id: "LIBSQL_VECTOR_UPSERT_FAILED",
643
+ id: storage.createVectorErrorId("LIBSQL", "UPSERT", "FAILED"),
634
644
  domain: error.ErrorDomain.STORAGE,
635
645
  category: error.ErrorCategory.THIRD_PARTY
636
646
  },
@@ -684,7 +694,7 @@ var LibSQLVector = class extends vector.MastraVector {
684
694
  } catch (error$1) {
685
695
  throw new error.MastraError(
686
696
  {
687
- id: "LIBSQL_VECTOR_CREATE_INDEX_FAILED",
697
+ id: storage.createVectorErrorId("LIBSQL", "CREATE_INDEX", "FAILED"),
688
698
  domain: error.ErrorDomain.STORAGE,
689
699
  category: error.ErrorCategory.THIRD_PARTY,
690
700
  details: { indexName: args.indexName, dimension: args.dimension }
@@ -723,7 +733,7 @@ var LibSQLVector = class extends vector.MastraVector {
723
733
  } catch (error$1) {
724
734
  throw new error.MastraError(
725
735
  {
726
- id: "LIBSQL_VECTOR_DELETE_INDEX_FAILED",
736
+ id: storage.createVectorErrorId("LIBSQL", "DELETE_INDEX", "FAILED"),
727
737
  domain: error.ErrorDomain.STORAGE,
728
738
  category: error.ErrorCategory.THIRD_PARTY,
729
739
  details: { indexName: args.indexName }
@@ -754,7 +764,7 @@ var LibSQLVector = class extends vector.MastraVector {
754
764
  } catch (error$1) {
755
765
  throw new error.MastraError(
756
766
  {
757
- id: "LIBSQL_VECTOR_LIST_INDEXES_FAILED",
767
+ id: storage.createVectorErrorId("LIBSQL", "LIST_INDEXES", "FAILED"),
758
768
  domain: error.ErrorDomain.STORAGE,
759
769
  category: error.ErrorCategory.THIRD_PARTY
760
770
  },
@@ -802,7 +812,7 @@ var LibSQLVector = class extends vector.MastraVector {
802
812
  } catch (e) {
803
813
  throw new error.MastraError(
804
814
  {
805
- id: "LIBSQL_VECTOR_DESCRIBE_INDEX_FAILED",
815
+ id: storage.createVectorErrorId("LIBSQL", "DESCRIBE_INDEX", "FAILED"),
806
816
  domain: error.ErrorDomain.STORAGE,
807
817
  category: error.ErrorCategory.THIRD_PARTY,
808
818
  details: { indexName }
@@ -825,8 +835,27 @@ var LibSQLVector = class extends vector.MastraVector {
825
835
  updateVector(args) {
826
836
  return this.executeWriteOperationWithRetry(() => this.doUpdateVector(args));
827
837
  }
828
- async doUpdateVector({ indexName, id, update }) {
838
+ async doUpdateVector(params) {
839
+ const { indexName, update } = params;
829
840
  const parsedIndexName = utils.parseSqlIdentifier(indexName, "index name");
841
+ if ("id" in params && params.id && "filter" in params && params.filter) {
842
+ throw new error.MastraError({
843
+ id: storage.createVectorErrorId("LIBSQL", "UPDATE_VECTOR", "MUTUALLY_EXCLUSIVE"),
844
+ domain: error.ErrorDomain.STORAGE,
845
+ category: error.ErrorCategory.USER,
846
+ details: { indexName },
847
+ text: "id and filter are mutually exclusive - provide only one"
848
+ });
849
+ }
850
+ if (!update.vector && !update.metadata) {
851
+ throw new error.MastraError({
852
+ id: storage.createVectorErrorId("LIBSQL", "UPDATE_VECTOR", "NO_PAYLOAD"),
853
+ domain: error.ErrorDomain.STORAGE,
854
+ category: error.ErrorCategory.USER,
855
+ details: { indexName },
856
+ text: "No updates provided"
857
+ });
858
+ }
830
859
  const updates = [];
831
860
  const args = [];
832
861
  if (update.vector) {
@@ -838,32 +867,81 @@ var LibSQLVector = class extends vector.MastraVector {
838
867
  args.push(JSON.stringify(update.metadata));
839
868
  }
840
869
  if (updates.length === 0) {
870
+ return;
871
+ }
872
+ let whereClause;
873
+ let whereValues;
874
+ if ("id" in params && params.id) {
875
+ whereClause = "vector_id = ?";
876
+ whereValues = [params.id];
877
+ } else if ("filter" in params && params.filter) {
878
+ const filter = params.filter;
879
+ if (!filter || Object.keys(filter).length === 0) {
880
+ throw new error.MastraError({
881
+ id: storage.createVectorErrorId("LIBSQL", "UPDATE_VECTOR", "EMPTY_FILTER"),
882
+ domain: error.ErrorDomain.STORAGE,
883
+ category: error.ErrorCategory.USER,
884
+ details: { indexName },
885
+ text: "Cannot update with empty filter"
886
+ });
887
+ }
888
+ const translatedFilter = this.transformFilter(filter);
889
+ const { sql: filterSql, values: filterValues } = buildFilterQuery(translatedFilter);
890
+ if (!filterSql || filterSql.trim() === "") {
891
+ throw new error.MastraError({
892
+ id: storage.createVectorErrorId("LIBSQL", "UPDATE_VECTOR", "INVALID_FILTER"),
893
+ domain: error.ErrorDomain.STORAGE,
894
+ category: error.ErrorCategory.USER,
895
+ details: { indexName },
896
+ text: "Filter produced empty WHERE clause"
897
+ });
898
+ }
899
+ const normalizedCondition = filterSql.replace(/^\s*WHERE\s+/i, "").trim().toLowerCase();
900
+ const matchAllPatterns = ["true", "1 = 1", "1=1"];
901
+ if (matchAllPatterns.includes(normalizedCondition)) {
902
+ throw new error.MastraError({
903
+ id: storage.createVectorErrorId("LIBSQL", "UPDATE_VECTOR", "MATCH_ALL_FILTER"),
904
+ domain: error.ErrorDomain.STORAGE,
905
+ category: error.ErrorCategory.USER,
906
+ details: { indexName, filterSql: normalizedCondition },
907
+ text: "Filter matches all vectors. Provide a specific filter to update targeted vectors."
908
+ });
909
+ }
910
+ whereClause = filterSql.replace(/^WHERE\s+/i, "");
911
+ whereValues = filterValues;
912
+ } else {
841
913
  throw new error.MastraError({
842
- id: "LIBSQL_VECTOR_UPDATE_VECTOR_INVALID_ARGS",
914
+ id: storage.createVectorErrorId("LIBSQL", "UPDATE_VECTOR", "NO_TARGET"),
843
915
  domain: error.ErrorDomain.STORAGE,
844
916
  category: error.ErrorCategory.USER,
845
- details: { indexName, id },
846
- text: "No updates provided"
917
+ details: { indexName },
918
+ text: "Either id or filter must be provided"
847
919
  });
848
920
  }
849
- args.push(id);
850
921
  const query = `
851
- UPDATE ${parsedIndexName}
852
- SET ${updates.join(", ")}
853
- WHERE vector_id = ?;
854
- `;
922
+ UPDATE ${parsedIndexName}
923
+ SET ${updates.join(", ")}
924
+ WHERE ${whereClause};
925
+ `;
855
926
  try {
856
927
  await this.turso.execute({
857
928
  sql: query,
858
- args
929
+ args: [...args, ...whereValues]
859
930
  });
860
931
  } catch (error$1) {
932
+ const errorDetails = { indexName };
933
+ if ("id" in params && params.id) {
934
+ errorDetails.id = params.id;
935
+ }
936
+ if ("filter" in params && params.filter) {
937
+ errorDetails.filter = JSON.stringify(params.filter);
938
+ }
861
939
  throw new error.MastraError(
862
940
  {
863
- id: "LIBSQL_VECTOR_UPDATE_VECTOR_FAILED",
941
+ id: storage.createVectorErrorId("LIBSQL", "UPDATE_VECTOR", "FAILED"),
864
942
  domain: error.ErrorDomain.STORAGE,
865
943
  category: error.ErrorCategory.THIRD_PARTY,
866
- details: { indexName, id }
944
+ details: errorDetails
867
945
  },
868
946
  error$1
869
947
  );
@@ -882,10 +960,13 @@ var LibSQLVector = class extends vector.MastraVector {
882
960
  } catch (error$1) {
883
961
  throw new error.MastraError(
884
962
  {
885
- id: "LIBSQL_VECTOR_DELETE_VECTOR_FAILED",
963
+ id: storage.createVectorErrorId("LIBSQL", "DELETE_VECTOR", "FAILED"),
886
964
  domain: error.ErrorDomain.STORAGE,
887
965
  category: error.ErrorCategory.THIRD_PARTY,
888
- details: { indexName: args.indexName, id: args.id }
966
+ details: {
967
+ indexName: args.indexName,
968
+ ...args.id && { id: args.id }
969
+ }
889
970
  },
890
971
  error$1
891
972
  );
@@ -898,13 +979,107 @@ var LibSQLVector = class extends vector.MastraVector {
898
979
  args: [id]
899
980
  });
900
981
  }
982
+ deleteVectors(args) {
983
+ return this.executeWriteOperationWithRetry(() => this.doDeleteVectors(args));
984
+ }
985
+ async doDeleteVectors({ indexName, filter, ids }) {
986
+ const parsedIndexName = utils.parseSqlIdentifier(indexName, "index name");
987
+ if (!filter && !ids) {
988
+ throw new error.MastraError({
989
+ id: storage.createVectorErrorId("LIBSQL", "DELETE_VECTORS", "NO_TARGET"),
990
+ domain: error.ErrorDomain.STORAGE,
991
+ category: error.ErrorCategory.USER,
992
+ details: { indexName },
993
+ text: "Either filter or ids must be provided"
994
+ });
995
+ }
996
+ if (filter && ids) {
997
+ throw new error.MastraError({
998
+ id: storage.createVectorErrorId("LIBSQL", "DELETE_VECTORS", "MUTUALLY_EXCLUSIVE"),
999
+ domain: error.ErrorDomain.STORAGE,
1000
+ category: error.ErrorCategory.USER,
1001
+ details: { indexName },
1002
+ text: "Cannot provide both filter and ids - they are mutually exclusive"
1003
+ });
1004
+ }
1005
+ let query;
1006
+ let values;
1007
+ if (ids) {
1008
+ if (ids.length === 0) {
1009
+ throw new error.MastraError({
1010
+ id: storage.createVectorErrorId("LIBSQL", "DELETE_VECTORS", "EMPTY_IDS"),
1011
+ domain: error.ErrorDomain.STORAGE,
1012
+ category: error.ErrorCategory.USER,
1013
+ details: { indexName },
1014
+ text: "Cannot delete with empty ids array"
1015
+ });
1016
+ }
1017
+ const placeholders = ids.map(() => "?").join(", ");
1018
+ query = `DELETE FROM ${parsedIndexName} WHERE vector_id IN (${placeholders})`;
1019
+ values = ids;
1020
+ } else {
1021
+ if (!filter || Object.keys(filter).length === 0) {
1022
+ throw new error.MastraError({
1023
+ id: storage.createVectorErrorId("LIBSQL", "DELETE_VECTORS", "EMPTY_FILTER"),
1024
+ domain: error.ErrorDomain.STORAGE,
1025
+ category: error.ErrorCategory.USER,
1026
+ details: { indexName },
1027
+ text: "Cannot delete with empty filter. Use deleteIndex to delete all vectors."
1028
+ });
1029
+ }
1030
+ const translatedFilter = this.transformFilter(filter);
1031
+ const { sql: filterSql, values: filterValues } = buildFilterQuery(translatedFilter);
1032
+ if (!filterSql || filterSql.trim() === "") {
1033
+ throw new error.MastraError({
1034
+ id: storage.createVectorErrorId("LIBSQL", "DELETE_VECTORS", "INVALID_FILTER"),
1035
+ domain: error.ErrorDomain.STORAGE,
1036
+ category: error.ErrorCategory.USER,
1037
+ details: { indexName },
1038
+ text: "Filter produced empty WHERE clause"
1039
+ });
1040
+ }
1041
+ const normalizedCondition = filterSql.replace(/^\s*WHERE\s+/i, "").trim().toLowerCase();
1042
+ const matchAllPatterns = ["true", "1 = 1", "1=1"];
1043
+ if (matchAllPatterns.includes(normalizedCondition)) {
1044
+ throw new error.MastraError({
1045
+ id: storage.createVectorErrorId("LIBSQL", "DELETE_VECTORS", "MATCH_ALL_FILTER"),
1046
+ domain: error.ErrorDomain.STORAGE,
1047
+ category: error.ErrorCategory.USER,
1048
+ details: { indexName, filterSql: normalizedCondition },
1049
+ text: "Filter matches all vectors. Use deleteIndex to delete all vectors from an index."
1050
+ });
1051
+ }
1052
+ query = `DELETE FROM ${parsedIndexName} ${filterSql}`;
1053
+ values = filterValues;
1054
+ }
1055
+ try {
1056
+ await this.turso.execute({
1057
+ sql: query,
1058
+ args: values
1059
+ });
1060
+ } catch (error$1) {
1061
+ throw new error.MastraError(
1062
+ {
1063
+ id: storage.createVectorErrorId("LIBSQL", "DELETE_VECTORS", "FAILED"),
1064
+ domain: error.ErrorDomain.STORAGE,
1065
+ category: error.ErrorCategory.THIRD_PARTY,
1066
+ details: {
1067
+ indexName,
1068
+ ...filter && { filter: JSON.stringify(filter) },
1069
+ ...ids && { idsCount: ids.length }
1070
+ }
1071
+ },
1072
+ error$1
1073
+ );
1074
+ }
1075
+ }
901
1076
  truncateIndex(args) {
902
1077
  try {
903
1078
  return this.executeWriteOperationWithRetry(() => this._doTruncateIndex(args));
904
1079
  } catch (error$1) {
905
1080
  throw new error.MastraError(
906
1081
  {
907
- id: "LIBSQL_VECTOR_TRUNCATE_INDEX_FAILED",
1082
+ id: storage.createVectorErrorId("LIBSQL", "TRUNCATE_INDEX", "FAILED"),
908
1083
  domain: error.ErrorDomain.STORAGE,
909
1084
  category: error.ErrorCategory.THIRD_PARTY,
910
1085
  details: { indexName: args.indexName }
@@ -920,1694 +1095,1989 @@ var LibSQLVector = class extends vector.MastraVector {
920
1095
  });
921
1096
  }
922
1097
  };
923
- function transformEvalRow(row) {
924
- const resultValue = JSON.parse(row.result);
925
- const testInfoValue = row.test_info ? JSON.parse(row.test_info) : void 0;
926
- if (!resultValue || typeof resultValue !== "object" || !("score" in resultValue)) {
927
- throw new Error(`Invalid MetricResult format: ${JSON.stringify(resultValue)}`);
928
- }
929
- return {
930
- input: row.input,
931
- output: row.output,
932
- result: resultValue,
933
- agentName: row.agent_name,
934
- metricName: row.metric_name,
935
- instructions: row.instructions,
936
- testInfo: testInfoValue,
937
- globalRunId: row.global_run_id,
938
- runId: row.run_id,
939
- createdAt: row.created_at
940
- };
1098
+ function isLockError(error) {
1099
+ 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");
941
1100
  }
942
- var LegacyEvalsLibSQL = class extends storage.LegacyEvalsStorage {
943
- client;
944
- constructor({ client }) {
945
- super();
946
- this.client = client;
947
- }
948
- /** @deprecated use getEvals instead */
949
- async getEvalsByAgentName(agentName, type) {
950
- try {
951
- const baseQuery = `SELECT * FROM ${storage.TABLE_EVALS} WHERE agent_name = ?`;
952
- const typeCondition = type === "test" ? " AND test_info IS NOT NULL AND test_info->>'testPath' IS NOT NULL" : type === "live" ? " AND (test_info IS NULL OR test_info->>'testPath' IS NULL)" : "";
953
- const result = await this.client.execute({
954
- sql: `${baseQuery}${typeCondition} ORDER BY created_at DESC`,
955
- args: [agentName]
956
- });
957
- return result.rows?.map((row) => transformEvalRow(row)) ?? [];
958
- } catch (error$1) {
959
- if (error$1 instanceof Error && error$1.message.includes("no such table")) {
960
- return [];
1101
+ function createExecuteWriteOperationWithRetry({
1102
+ logger,
1103
+ maxRetries,
1104
+ initialBackoffMs
1105
+ }) {
1106
+ return async function executeWriteOperationWithRetry(operationFn, operationDescription) {
1107
+ let attempts = 0;
1108
+ let backoff = initialBackoffMs;
1109
+ while (attempts < maxRetries) {
1110
+ try {
1111
+ return await operationFn();
1112
+ } catch (error) {
1113
+ logger.debug(`LibSQLStore: Error caught in retry loop for ${operationDescription}`, {
1114
+ errorType: error.constructor.name,
1115
+ errorCode: error.code,
1116
+ errorMessage: error.message,
1117
+ attempts,
1118
+ maxRetries
1119
+ });
1120
+ if (isLockError(error)) {
1121
+ attempts++;
1122
+ if (attempts >= maxRetries) {
1123
+ logger.error(
1124
+ `LibSQLStore: Operation failed after ${maxRetries} attempts due to database lock: ${error.message}`,
1125
+ { error, attempts, maxRetries }
1126
+ );
1127
+ throw error;
1128
+ }
1129
+ logger.warn(
1130
+ `LibSQLStore: Attempt ${attempts} failed due to database lock during ${operationDescription}. Retrying in ${backoff}ms...`,
1131
+ { errorMessage: error.message, attempts, backoff, maxRetries }
1132
+ );
1133
+ await new Promise((resolve) => setTimeout(resolve, backoff));
1134
+ backoff *= 2;
1135
+ } else {
1136
+ logger.error(`LibSQLStore: Non-lock error during ${operationDescription}, not retrying`, { error });
1137
+ throw error;
1138
+ }
961
1139
  }
962
- throw new error.MastraError(
963
- {
964
- id: "LIBSQL_STORE_GET_EVALS_BY_AGENT_NAME_FAILED",
965
- domain: error.ErrorDomain.STORAGE,
966
- category: error.ErrorCategory.THIRD_PARTY,
967
- details: { agentName }
968
- },
969
- error$1
970
- );
971
- }
972
- }
973
- async getEvals(options = {}) {
974
- const { agentName, type, page = 0, perPage = 100, dateRange } = options;
975
- const fromDate = dateRange?.start;
976
- const toDate = dateRange?.end;
977
- const conditions = [];
978
- const queryParams = [];
979
- if (agentName) {
980
- conditions.push(`agent_name = ?`);
981
- queryParams.push(agentName);
982
- }
983
- if (type === "test") {
984
- conditions.push(`(test_info IS NOT NULL AND json_extract(test_info, '$.testPath') IS NOT NULL)`);
985
- } else if (type === "live") {
986
- conditions.push(`(test_info IS NULL OR json_extract(test_info, '$.testPath') IS NULL)`);
987
1140
  }
988
- if (fromDate) {
989
- conditions.push(`created_at >= ?`);
990
- queryParams.push(fromDate.toISOString());
991
- }
992
- if (toDate) {
993
- conditions.push(`created_at <= ?`);
994
- queryParams.push(toDate.toISOString());
1141
+ throw new Error(`LibSQLStore: Unexpected exit from retry loop for ${operationDescription}`);
1142
+ };
1143
+ }
1144
+ function prepareStatement({ tableName, record }) {
1145
+ 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) => {
1148
+ if (typeof v === `undefined` || v === null) {
1149
+ return null;
995
1150
  }
996
- const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
997
- try {
998
- const countResult = await this.client.execute({
999
- sql: `SELECT COUNT(*) as count FROM ${storage.TABLE_EVALS} ${whereClause}`,
1000
- args: queryParams
1001
- });
1002
- const total = Number(countResult.rows?.[0]?.count ?? 0);
1003
- const currentOffset = page * perPage;
1004
- const hasMore = currentOffset + perPage < total;
1005
- if (total === 0) {
1006
- return {
1007
- evals: [],
1008
- total: 0,
1009
- page,
1010
- perPage,
1011
- hasMore: false
1012
- };
1013
- }
1014
- const dataResult = await this.client.execute({
1015
- sql: `SELECT * FROM ${storage.TABLE_EVALS} ${whereClause} ORDER BY created_at DESC LIMIT ? OFFSET ?`,
1016
- args: [...queryParams, perPage, currentOffset]
1017
- });
1018
- return {
1019
- evals: dataResult.rows?.map((row) => transformEvalRow(row)) ?? [],
1020
- total,
1021
- page,
1022
- perPage,
1023
- hasMore
1024
- };
1025
- } catch (error$1) {
1026
- throw new error.MastraError(
1027
- {
1028
- id: "LIBSQL_STORE_GET_EVALS_FAILED",
1029
- domain: error.ErrorDomain.STORAGE,
1030
- category: error.ErrorCategory.THIRD_PARTY
1031
- },
1032
- error$1
1033
- );
1151
+ if (v instanceof Date) {
1152
+ return v.toISOString();
1034
1153
  }
1154
+ return typeof v === "object" ? JSON.stringify(v) : v;
1155
+ });
1156
+ const placeholders = values.map(() => "?").join(", ");
1157
+ return {
1158
+ sql: `INSERT OR REPLACE INTO ${parsedTableName} (${columns.join(", ")}) VALUES (${placeholders})`,
1159
+ args: values
1160
+ };
1161
+ }
1162
+ function prepareUpdateStatement({
1163
+ tableName,
1164
+ updates,
1165
+ keys
1166
+ }) {
1167
+ const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
1168
+ 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(", ");
1172
+ const whereClause = prepareWhereClause(keys, schema);
1173
+ return {
1174
+ sql: `UPDATE ${parsedTableName} SET ${setClause}${whereClause.sql}`,
1175
+ args: [...updateValues, ...whereClause.args]
1176
+ };
1177
+ }
1178
+ function transformToSqlValue(value) {
1179
+ if (typeof value === "undefined" || value === null) {
1180
+ return null;
1035
1181
  }
1036
- };
1037
- var MemoryLibSQL = class extends storage.MemoryStorage {
1038
- client;
1039
- operations;
1040
- constructor({ client, operations }) {
1041
- super();
1042
- this.client = client;
1043
- this.operations = operations;
1182
+ if (value instanceof Date) {
1183
+ return value.toISOString();
1044
1184
  }
1045
- parseRow(row) {
1046
- let content = row.content;
1047
- try {
1048
- content = JSON.parse(row.content);
1049
- } catch {
1185
+ return typeof value === "object" ? JSON.stringify(value) : value;
1186
+ }
1187
+ function prepareDeleteStatement({ tableName, keys }) {
1188
+ const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
1189
+ const whereClause = prepareWhereClause(keys, storage.TABLE_SCHEMAS[tableName]);
1190
+ return {
1191
+ sql: `DELETE FROM ${parsedTableName}${whereClause.sql}`,
1192
+ args: whereClause.args
1193
+ };
1194
+ }
1195
+ function prepareWhereClause(filters, schema) {
1196
+ const conditions = [];
1197
+ const args = [];
1198
+ for (const [columnName, filterValue] of Object.entries(filters)) {
1199
+ const column = schema[columnName];
1200
+ if (!column) {
1201
+ throw new Error(`Unknown column: ${columnName}`);
1050
1202
  }
1051
- const result = {
1052
- id: row.id,
1053
- content,
1054
- role: row.role,
1055
- createdAt: new Date(row.createdAt),
1056
- threadId: row.thread_id,
1057
- resourceId: row.resourceId
1058
- };
1059
- if (row.type && row.type !== `v2`) result.type = row.type;
1060
- return result;
1203
+ const parsedColumn = utils.parseSqlIdentifier(columnName, "column name");
1204
+ const result = buildCondition2(parsedColumn, filterValue);
1205
+ conditions.push(result.condition);
1206
+ args.push(...result.args);
1061
1207
  }
1062
- async _getIncludedMessages({
1063
- threadId,
1064
- selectBy
1065
- }) {
1066
- if (!threadId.trim()) throw new Error("threadId must be a non-empty string");
1067
- const include = selectBy?.include;
1068
- if (!include) return null;
1069
- const unionQueries = [];
1070
- const params = [];
1071
- for (const inc of include) {
1072
- const { id, withPreviousMessages = 0, withNextMessages = 0 } = inc;
1073
- const searchId = inc.threadId || threadId;
1074
- unionQueries.push(
1075
- `
1076
- SELECT * FROM (
1077
- WITH numbered_messages AS (
1078
- SELECT
1079
- id, content, role, type, "createdAt", thread_id, "resourceId",
1080
- ROW_NUMBER() OVER (ORDER BY "createdAt" ASC) as row_num
1081
- FROM "${storage.TABLE_MESSAGES}"
1082
- WHERE thread_id = ?
1083
- ),
1084
- target_positions AS (
1085
- SELECT row_num as target_pos
1086
- FROM numbered_messages
1087
- WHERE id = ?
1088
- )
1089
- SELECT DISTINCT m.*
1090
- FROM numbered_messages m
1091
- CROSS JOIN target_positions t
1092
- WHERE m.row_num BETWEEN (t.target_pos - ?) AND (t.target_pos + ?)
1093
- )
1094
- `
1095
- // Keep ASC for final sorting after fetching context
1096
- );
1097
- params.push(searchId, id, withPreviousMessages, withNextMessages);
1208
+ return {
1209
+ sql: conditions.length > 0 ? ` WHERE ${conditions.join(" AND ")}` : "",
1210
+ args
1211
+ };
1212
+ }
1213
+ function buildCondition2(columnName, filterValue) {
1214
+ if (filterValue === null) {
1215
+ return { condition: `${columnName} IS NULL`, args: [] };
1216
+ }
1217
+ if (typeof filterValue === "object" && filterValue !== null && ("startAt" in filterValue || "endAt" in filterValue)) {
1218
+ return buildDateRangeCondition(columnName, filterValue);
1219
+ }
1220
+ return {
1221
+ condition: `${columnName} = ?`,
1222
+ args: [transformToSqlValue(filterValue)]
1223
+ };
1224
+ }
1225
+ function buildDateRangeCondition(columnName, range) {
1226
+ const conditions = [];
1227
+ const args = [];
1228
+ if (range.startAt !== void 0) {
1229
+ conditions.push(`${columnName} >= ?`);
1230
+ args.push(transformToSqlValue(range.startAt));
1231
+ }
1232
+ if (range.endAt !== void 0) {
1233
+ conditions.push(`${columnName} <= ?`);
1234
+ args.push(transformToSqlValue(range.endAt));
1235
+ }
1236
+ if (conditions.length === 0) {
1237
+ throw new Error("Date range must specify at least startAt or endAt");
1238
+ }
1239
+ return {
1240
+ condition: conditions.join(" AND "),
1241
+ args
1242
+ };
1243
+ }
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
+ function transformFromSqlRow({
1258
+ tableName,
1259
+ sqlRow
1260
+ }) {
1261
+ const result = {};
1262
+ const jsonColumns = new Set(
1263
+ Object.keys(storage.TABLE_SCHEMAS[tableName]).filter((key) => storage.TABLE_SCHEMAS[tableName][key].type === "jsonb").map((key) => key)
1264
+ );
1265
+ const dateColumns = new Set(
1266
+ Object.keys(storage.TABLE_SCHEMAS[tableName]).filter((key) => storage.TABLE_SCHEMAS[tableName][key].type === "timestamp").map((key) => key)
1267
+ );
1268
+ for (const [key, value] of Object.entries(sqlRow)) {
1269
+ if (value === null || value === void 0) {
1270
+ result[key] = value;
1271
+ continue;
1098
1272
  }
1099
- const finalQuery = unionQueries.join(" UNION ALL ") + ' ORDER BY "createdAt" ASC';
1100
- const includedResult = await this.client.execute({ sql: finalQuery, args: params });
1101
- const includedRows = includedResult.rows?.map((row) => this.parseRow(row));
1102
- const seen = /* @__PURE__ */ new Set();
1103
- const dedupedRows = includedRows.filter((row) => {
1104
- if (seen.has(row.id)) return false;
1105
- seen.add(row.id);
1106
- return true;
1273
+ if (dateColumns.has(key) && typeof value === "string") {
1274
+ result[key] = new Date(value);
1275
+ continue;
1276
+ }
1277
+ if (jsonColumns.has(key) && typeof value === "string") {
1278
+ result[key] = storage.safelyParseJSON(value);
1279
+ continue;
1280
+ }
1281
+ result[key] = value;
1282
+ }
1283
+ return result;
1284
+ }
1285
+
1286
+ // src/storage/db/index.ts
1287
+ function resolveClient(config) {
1288
+ if ("client" in config) {
1289
+ return config.client;
1290
+ }
1291
+ return client.createClient({
1292
+ url: config.url,
1293
+ ...config.authToken ? { authToken: config.authToken } : {}
1294
+ });
1295
+ }
1296
+ var LibSQLDB = class extends base.MastraBase {
1297
+ client;
1298
+ maxRetries;
1299
+ initialBackoffMs;
1300
+ executeWriteOperationWithRetry;
1301
+ constructor({
1302
+ client,
1303
+ maxRetries,
1304
+ initialBackoffMs
1305
+ }) {
1306
+ super({
1307
+ component: "STORAGE",
1308
+ name: "LIBSQL_DB_LAYER"
1309
+ });
1310
+ this.client = client;
1311
+ this.maxRetries = maxRetries ?? 5;
1312
+ this.initialBackoffMs = initialBackoffMs ?? 100;
1313
+ this.executeWriteOperationWithRetry = createExecuteWriteOperationWithRetry({
1314
+ logger: this.logger,
1315
+ maxRetries: this.maxRetries,
1316
+ initialBackoffMs: this.initialBackoffMs
1107
1317
  });
1108
- return dedupedRows;
1109
1318
  }
1110
- async getMessages({
1111
- threadId,
1112
- resourceId,
1113
- selectBy,
1114
- format
1319
+ /**
1320
+ * Checks if a column exists in the specified table.
1321
+ *
1322
+ * @param table - The name of the table to check
1323
+ * @param column - The name of the column to look for
1324
+ * @returns `true` if the column exists in the table, `false` otherwise
1325
+ */
1326
+ async hasColumn(table, column) {
1327
+ const sanitizedTable = utils.parseSqlIdentifier(table, "table name");
1328
+ const result = await this.client.execute({
1329
+ sql: `PRAGMA table_info("${sanitizedTable}")`
1330
+ });
1331
+ return result.rows?.some((row) => row.name === column);
1332
+ }
1333
+ /**
1334
+ * Internal insert implementation without retry logic.
1335
+ */
1336
+ async doInsert({
1337
+ tableName,
1338
+ record
1115
1339
  }) {
1116
- try {
1117
- if (!threadId.trim()) throw new Error("threadId must be a non-empty string");
1118
- const messages = [];
1119
- const limit = storage.resolveMessageLimit({ last: selectBy?.last, defaultLimit: 40 });
1120
- if (selectBy?.include?.length) {
1121
- const includeMessages = await this._getIncludedMessages({ threadId, selectBy });
1122
- if (includeMessages) {
1123
- messages.push(...includeMessages);
1124
- }
1125
- }
1126
- const excludeIds = messages.map((m) => m.id);
1127
- const remainingSql = `
1128
- SELECT
1129
- id,
1130
- content,
1131
- role,
1132
- type,
1133
- "createdAt",
1134
- thread_id,
1135
- "resourceId"
1136
- FROM "${storage.TABLE_MESSAGES}"
1137
- WHERE thread_id = ?
1138
- ${excludeIds.length ? `AND id NOT IN (${excludeIds.map(() => "?").join(", ")})` : ""}
1139
- ORDER BY "createdAt" DESC
1140
- LIMIT ?
1141
- `;
1142
- const remainingArgs = [threadId, ...excludeIds.length ? excludeIds : [], limit];
1143
- const remainingResult = await this.client.execute({ sql: remainingSql, args: remainingArgs });
1144
- if (remainingResult.rows) {
1145
- messages.push(...remainingResult.rows.map((row) => this.parseRow(row)));
1146
- }
1147
- messages.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
1148
- const list = new agent.MessageList().add(messages, "memory");
1149
- if (format === `v2`) return list.get.all.v2();
1150
- return list.get.all.v1();
1151
- } catch (error$1) {
1340
+ await this.client.execute(
1341
+ prepareStatement({
1342
+ tableName,
1343
+ record
1344
+ })
1345
+ );
1346
+ }
1347
+ /**
1348
+ * Inserts or replaces a record in the specified table with automatic retry on lock errors.
1349
+ *
1350
+ * @param args - The insert arguments
1351
+ * @param args.tableName - The name of the table to insert into
1352
+ * @param args.record - The record to insert (key-value pairs)
1353
+ */
1354
+ insert(args) {
1355
+ return this.executeWriteOperationWithRetry(() => this.doInsert(args), `insert into table ${args.tableName}`);
1356
+ }
1357
+ /**
1358
+ * Internal update implementation without retry logic.
1359
+ */
1360
+ async doUpdate({
1361
+ tableName,
1362
+ keys,
1363
+ data
1364
+ }) {
1365
+ await this.client.execute(prepareUpdateStatement({ tableName, updates: data, keys }));
1366
+ }
1367
+ /**
1368
+ * Updates a record in the specified table with automatic retry on lock errors.
1369
+ *
1370
+ * @param args - The update arguments
1371
+ * @param args.tableName - The name of the table to update
1372
+ * @param args.keys - The key(s) identifying the record to update
1373
+ * @param args.data - The fields to update (key-value pairs)
1374
+ */
1375
+ update(args) {
1376
+ return this.executeWriteOperationWithRetry(() => this.doUpdate(args), `update table ${args.tableName}`);
1377
+ }
1378
+ /**
1379
+ * Internal batch insert implementation without retry logic.
1380
+ */
1381
+ async doBatchInsert({
1382
+ tableName,
1383
+ records
1384
+ }) {
1385
+ if (records.length === 0) return;
1386
+ const batchStatements = records.map((r) => prepareStatement({ tableName, record: r }));
1387
+ await this.client.batch(batchStatements, "write");
1388
+ }
1389
+ /**
1390
+ * Inserts multiple records in a single batch transaction with automatic retry on lock errors.
1391
+ *
1392
+ * @param args - The batch insert arguments
1393
+ * @param args.tableName - The name of the table to insert into
1394
+ * @param args.records - Array of records to insert
1395
+ * @throws {MastraError} When the batch insert fails after retries
1396
+ */
1397
+ async batchInsert(args) {
1398
+ return this.executeWriteOperationWithRetry(
1399
+ () => this.doBatchInsert(args),
1400
+ `batch insert into table ${args.tableName}`
1401
+ ).catch((error$1) => {
1152
1402
  throw new error.MastraError(
1153
1403
  {
1154
- id: "LIBSQL_STORE_GET_MESSAGES_FAILED",
1404
+ id: storage.createStorageErrorId("LIBSQL", "BATCH_INSERT", "FAILED"),
1155
1405
  domain: error.ErrorDomain.STORAGE,
1156
1406
  category: error.ErrorCategory.THIRD_PARTY,
1157
- details: { threadId, resourceId: resourceId ?? "" }
1407
+ details: {
1408
+ tableName: args.tableName
1409
+ }
1158
1410
  },
1159
1411
  error$1
1160
1412
  );
1161
- }
1413
+ });
1162
1414
  }
1163
- async getMessagesById({
1164
- messageIds,
1165
- format
1415
+ /**
1416
+ * Internal batch update implementation without retry logic.
1417
+ * Each record can be updated based on single or composite keys.
1418
+ */
1419
+ async doBatchUpdate({
1420
+ tableName,
1421
+ updates
1166
1422
  }) {
1167
- if (messageIds.length === 0) return [];
1168
- try {
1169
- const sql = `
1170
- SELECT
1171
- id,
1172
- content,
1173
- role,
1174
- type,
1175
- "createdAt",
1176
- thread_id,
1177
- "resourceId"
1178
- FROM "${storage.TABLE_MESSAGES}"
1179
- WHERE id IN (${messageIds.map(() => "?").join(", ")})
1180
- ORDER BY "createdAt" DESC
1181
- `;
1182
- const result = await this.client.execute({ sql, args: messageIds });
1183
- if (!result.rows) return [];
1184
- const list = new agent.MessageList().add(result.rows.map(this.parseRow), "memory");
1185
- if (format === `v1`) return list.get.all.v1();
1186
- return list.get.all.v2();
1187
- } catch (error$1) {
1423
+ if (updates.length === 0) return;
1424
+ const batchStatements = updates.map(
1425
+ ({ keys, data }) => prepareUpdateStatement({
1426
+ tableName,
1427
+ updates: data,
1428
+ keys
1429
+ })
1430
+ );
1431
+ await this.client.batch(batchStatements, "write");
1432
+ }
1433
+ /**
1434
+ * Updates multiple records in a single batch transaction with automatic retry on lock errors.
1435
+ * Each record can be updated based on single or composite keys.
1436
+ *
1437
+ * @param args - The batch update arguments
1438
+ * @param args.tableName - The name of the table to update
1439
+ * @param args.updates - Array of update operations, each containing keys and data
1440
+ * @throws {MastraError} When the batch update fails after retries
1441
+ */
1442
+ async batchUpdate(args) {
1443
+ return this.executeWriteOperationWithRetry(
1444
+ () => this.doBatchUpdate(args),
1445
+ `batch update in table ${args.tableName}`
1446
+ ).catch((error$1) => {
1188
1447
  throw new error.MastraError(
1189
1448
  {
1190
- id: "LIBSQL_STORE_GET_MESSAGES_BY_ID_FAILED",
1449
+ id: storage.createStorageErrorId("LIBSQL", "BATCH_UPDATE", "FAILED"),
1191
1450
  domain: error.ErrorDomain.STORAGE,
1192
1451
  category: error.ErrorCategory.THIRD_PARTY,
1193
- details: { messageIds: JSON.stringify(messageIds) }
1452
+ details: {
1453
+ tableName: args.tableName
1454
+ }
1194
1455
  },
1195
1456
  error$1
1196
1457
  );
1197
- }
1458
+ });
1198
1459
  }
1199
- async getMessagesPaginated(args) {
1200
- const { threadId, format, selectBy } = args;
1201
- const { page = 0, perPage: perPageInput, dateRange } = selectBy?.pagination || {};
1202
- const perPage = perPageInput !== void 0 ? perPageInput : storage.resolveMessageLimit({ last: selectBy?.last, defaultLimit: 40 });
1203
- const fromDate = dateRange?.start;
1204
- const toDate = dateRange?.end;
1205
- const messages = [];
1206
- if (selectBy?.include?.length) {
1207
- try {
1208
- const includeMessages = await this._getIncludedMessages({ threadId, selectBy });
1209
- if (includeMessages) {
1210
- messages.push(...includeMessages);
1211
- }
1212
- } catch (error$1) {
1213
- throw new error.MastraError(
1214
- {
1215
- id: "LIBSQL_STORE_GET_MESSAGES_PAGINATED_GET_INCLUDE_MESSAGES_FAILED",
1216
- domain: error.ErrorDomain.STORAGE,
1217
- category: error.ErrorCategory.THIRD_PARTY,
1218
- details: { threadId }
1219
- },
1220
- error$1
1221
- );
1222
- }
1223
- }
1224
- try {
1225
- if (!threadId.trim()) throw new Error("threadId must be a non-empty string");
1226
- const currentOffset = page * perPage;
1227
- const conditions = [`thread_id = ?`];
1228
- const queryParams = [threadId];
1229
- if (fromDate) {
1230
- conditions.push(`"createdAt" >= ?`);
1231
- queryParams.push(fromDate.toISOString());
1232
- }
1233
- if (toDate) {
1234
- conditions.push(`"createdAt" <= ?`);
1235
- queryParams.push(toDate.toISOString());
1460
+ /**
1461
+ * Internal batch delete implementation without retry logic.
1462
+ * Each record can be deleted based on single or composite keys.
1463
+ */
1464
+ async doBatchDelete({
1465
+ tableName,
1466
+ keys
1467
+ }) {
1468
+ if (keys.length === 0) return;
1469
+ const batchStatements = keys.map(
1470
+ (keyObj) => prepareDeleteStatement({
1471
+ tableName,
1472
+ keys: keyObj
1473
+ })
1474
+ );
1475
+ await this.client.batch(batchStatements, "write");
1476
+ }
1477
+ /**
1478
+ * Deletes multiple records in a single batch transaction with automatic retry on lock errors.
1479
+ * Each record can be deleted based on single or composite keys.
1480
+ *
1481
+ * @param args - The batch delete arguments
1482
+ * @param args.tableName - The name of the table to delete from
1483
+ * @param args.keys - Array of key objects identifying records to delete
1484
+ * @throws {MastraError} When the batch delete fails after retries
1485
+ */
1486
+ async batchDelete({
1487
+ tableName,
1488
+ keys
1489
+ }) {
1490
+ return this.executeWriteOperationWithRetry(
1491
+ () => this.doBatchDelete({ tableName, keys }),
1492
+ `batch delete from table ${tableName}`
1493
+ ).catch((error$1) => {
1494
+ throw new error.MastraError(
1495
+ {
1496
+ id: storage.createStorageErrorId("LIBSQL", "BATCH_DELETE", "FAILED"),
1497
+ domain: error.ErrorDomain.STORAGE,
1498
+ category: error.ErrorCategory.THIRD_PARTY,
1499
+ details: {
1500
+ tableName
1501
+ }
1502
+ },
1503
+ error$1
1504
+ );
1505
+ });
1506
+ }
1507
+ /**
1508
+ * Internal single-record delete implementation without retry logic.
1509
+ */
1510
+ async doDelete({ tableName, keys }) {
1511
+ await this.client.execute(prepareDeleteStatement({ tableName, keys }));
1512
+ }
1513
+ /**
1514
+ * Deletes a single record from the specified table with automatic retry on lock errors.
1515
+ *
1516
+ * @param args - The delete arguments
1517
+ * @param args.tableName - The name of the table to delete from
1518
+ * @param args.keys - The key(s) identifying the record to delete
1519
+ * @throws {MastraError} When the delete fails after retries
1520
+ */
1521
+ async delete(args) {
1522
+ return this.executeWriteOperationWithRetry(() => this.doDelete(args), `delete from table ${args.tableName}`).catch(
1523
+ (error$1) => {
1524
+ throw new error.MastraError(
1525
+ {
1526
+ id: storage.createStorageErrorId("LIBSQL", "DELETE", "FAILED"),
1527
+ domain: error.ErrorDomain.STORAGE,
1528
+ category: error.ErrorCategory.THIRD_PARTY,
1529
+ details: {
1530
+ tableName: args.tableName
1531
+ }
1532
+ },
1533
+ error$1
1534
+ );
1236
1535
  }
1237
- const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
1238
- const countResult = await this.client.execute({
1239
- sql: `SELECT COUNT(*) as count FROM ${storage.TABLE_MESSAGES} ${whereClause}`,
1240
- args: queryParams
1536
+ );
1537
+ }
1538
+ /**
1539
+ * Selects a single record from the specified table by key(s).
1540
+ * Returns the most recently created record if multiple matches exist.
1541
+ * Automatically parses JSON string values back to objects/arrays.
1542
+ *
1543
+ * @typeParam R - The expected return type of the record
1544
+ * @param args - The select arguments
1545
+ * @param args.tableName - The name of the table to select from
1546
+ * @param args.keys - The key(s) identifying the record to select
1547
+ * @returns The matching record or `null` if not found
1548
+ */
1549
+ async select({ tableName, keys }) {
1550
+ const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
1551
+ const parsedKeys = Object.keys(keys).map((key) => utils.parseSqlIdentifier(key, "column name"));
1552
+ const conditions = parsedKeys.map((key) => `${key} = ?`).join(" AND ");
1553
+ const values = Object.values(keys);
1554
+ const result = await this.client.execute({
1555
+ sql: `SELECT * FROM ${parsedTableName} WHERE ${conditions} ORDER BY createdAt DESC LIMIT 1`,
1556
+ args: values
1557
+ });
1558
+ if (!result.rows || result.rows.length === 0) {
1559
+ return null;
1560
+ }
1561
+ const row = result.rows[0];
1562
+ const parsed = Object.fromEntries(
1563
+ Object.entries(row || {}).map(([k, v]) => {
1564
+ try {
1565
+ return [k, typeof v === "string" ? v.startsWith("{") || v.startsWith("[") ? JSON.parse(v) : v : v];
1566
+ } catch {
1567
+ return [k, v];
1568
+ }
1569
+ })
1570
+ );
1571
+ return parsed;
1572
+ }
1573
+ /**
1574
+ * Selects multiple records from the specified table with optional filtering, ordering, and pagination.
1575
+ *
1576
+ * @typeParam R - The expected return type of each record
1577
+ * @param args - The select arguments
1578
+ * @param args.tableName - The name of the table to select from
1579
+ * @param args.whereClause - Optional WHERE clause with SQL string and arguments
1580
+ * @param args.orderBy - Optional ORDER BY clause (e.g., "createdAt DESC")
1581
+ * @param args.offset - Optional offset for pagination
1582
+ * @param args.limit - Optional limit for pagination
1583
+ * @param args.args - Optional additional query arguments
1584
+ * @returns Array of matching records
1585
+ */
1586
+ async selectMany({
1587
+ tableName,
1588
+ whereClause,
1589
+ orderBy,
1590
+ offset,
1591
+ limit,
1592
+ args
1593
+ }) {
1594
+ const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
1595
+ let statement = `SELECT * FROM ${parsedTableName}`;
1596
+ if (whereClause?.sql) {
1597
+ statement += `${whereClause.sql}`;
1598
+ }
1599
+ if (orderBy) {
1600
+ statement += ` ORDER BY ${orderBy}`;
1601
+ }
1602
+ if (limit) {
1603
+ statement += ` LIMIT ${limit}`;
1604
+ }
1605
+ if (offset) {
1606
+ statement += ` OFFSET ${offset}`;
1607
+ }
1608
+ const result = await this.client.execute({
1609
+ sql: statement,
1610
+ args: [...whereClause?.args ?? [], ...args ?? []]
1611
+ });
1612
+ return result.rows;
1613
+ }
1614
+ /**
1615
+ * Returns the total count of records matching the optional WHERE clause.
1616
+ *
1617
+ * @param args - The count arguments
1618
+ * @param args.tableName - The name of the table to count from
1619
+ * @param args.whereClause - Optional WHERE clause with SQL string and arguments
1620
+ * @returns The total count of matching records
1621
+ */
1622
+ async selectTotalCount({
1623
+ tableName,
1624
+ whereClause
1625
+ }) {
1626
+ const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
1627
+ const statement = `SELECT COUNT(*) as count FROM ${parsedTableName} ${whereClause ? `${whereClause.sql}` : ""}`;
1628
+ const result = await this.client.execute({
1629
+ sql: statement,
1630
+ args: whereClause?.args ?? []
1631
+ });
1632
+ if (!result.rows || result.rows.length === 0) {
1633
+ return 0;
1634
+ }
1635
+ return result.rows[0]?.count ?? 0;
1636
+ }
1637
+ /**
1638
+ * Maps a storage column type to its SQLite equivalent.
1639
+ */
1640
+ getSqlType(type) {
1641
+ switch (type) {
1642
+ case "bigint":
1643
+ return "INTEGER";
1644
+ // SQLite uses INTEGER for all integer sizes
1645
+ case "timestamp":
1646
+ return "TEXT";
1647
+ // Store timestamps as ISO strings in SQLite
1648
+ case "float":
1649
+ return "REAL";
1650
+ // SQLite's floating point type
1651
+ case "boolean":
1652
+ return "INTEGER";
1653
+ // SQLite uses 0/1 for booleans
1654
+ case "jsonb":
1655
+ return "TEXT";
1656
+ // Store JSON as TEXT in SQLite
1657
+ default:
1658
+ return storage.getSqlType(type);
1659
+ }
1660
+ }
1661
+ /**
1662
+ * Creates a table if it doesn't exist based on the provided schema.
1663
+ *
1664
+ * @param args - The create table arguments
1665
+ * @param args.tableName - The name of the table to create
1666
+ * @param args.schema - The schema definition for the table columns
1667
+ */
1668
+ async createTable({
1669
+ tableName,
1670
+ schema
1671
+ }) {
1672
+ try {
1673
+ const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
1674
+ const columnDefinitions = Object.entries(schema).map(([colName, colDef]) => {
1675
+ const type = this.getSqlType(colDef.type);
1676
+ const nullable = colDef.nullable === false ? "NOT NULL" : "";
1677
+ const primaryKey = colDef.primaryKey ? "PRIMARY KEY" : "";
1678
+ return `"${colName}" ${type} ${nullable} ${primaryKey}`.trim();
1241
1679
  });
1242
- const total = Number(countResult.rows?.[0]?.count ?? 0);
1243
- if (total === 0 && messages.length === 0) {
1244
- return {
1245
- messages: [],
1246
- total: 0,
1247
- page,
1248
- perPage,
1249
- hasMore: false
1250
- };
1680
+ const tableConstraints = [];
1681
+ if (tableName === storage.TABLE_WORKFLOW_SNAPSHOT) {
1682
+ tableConstraints.push("UNIQUE (workflow_name, run_id)");
1251
1683
  }
1252
- const excludeIds = messages.map((m) => m.id);
1253
- const excludeIdsParam = excludeIds.map((_, idx) => `$${idx + queryParams.length + 1}`).join(", ");
1254
- const dataResult = await this.client.execute({
1255
- sql: `SELECT id, content, role, type, "createdAt", "resourceId", "thread_id" FROM ${storage.TABLE_MESSAGES} ${whereClause} ${excludeIds.length ? `AND id NOT IN (${excludeIdsParam})` : ""} ORDER BY "createdAt" DESC LIMIT ? OFFSET ?`,
1256
- args: [...queryParams, ...excludeIds, perPage, currentOffset]
1257
- });
1258
- messages.push(...(dataResult.rows || []).map((row) => this.parseRow(row)));
1259
- const messagesToReturn = format === "v1" ? new agent.MessageList().add(messages, "memory").get.all.v1() : new agent.MessageList().add(messages, "memory").get.all.v2();
1260
- return {
1261
- messages: messagesToReturn,
1262
- total,
1263
- page,
1264
- perPage,
1265
- hasMore: currentOffset + messages.length < total
1266
- };
1684
+ const allDefinitions = [...columnDefinitions, ...tableConstraints].join(",\n ");
1685
+ const sql = `CREATE TABLE IF NOT EXISTS ${parsedTableName} (
1686
+ ${allDefinitions}
1687
+ )`;
1688
+ await this.client.execute(sql);
1689
+ this.logger.debug(`LibSQLDB: Created table ${tableName}`);
1267
1690
  } catch (error$1) {
1268
- const mastraError = new error.MastraError(
1691
+ throw new error.MastraError(
1269
1692
  {
1270
- id: "LIBSQL_STORE_GET_MESSAGES_PAGINATED_FAILED",
1693
+ id: storage.createStorageErrorId("LIBSQL", "CREATE_TABLE", "FAILED"),
1271
1694
  domain: error.ErrorDomain.STORAGE,
1272
1695
  category: error.ErrorCategory.THIRD_PARTY,
1273
- details: { threadId }
1696
+ details: { tableName }
1274
1697
  },
1275
1698
  error$1
1276
1699
  );
1277
- this.logger?.trackException?.(mastraError);
1278
- this.logger?.error?.(mastraError.toString());
1279
- return { messages: [], total: 0, page, perPage, hasMore: false };
1280
1700
  }
1281
1701
  }
1282
- async saveMessages({
1283
- messages,
1284
- format
1702
+ /**
1703
+ * Gets a default value for a column type (used when adding NOT NULL columns).
1704
+ */
1705
+ getDefaultValue(type) {
1706
+ switch (type) {
1707
+ case "text":
1708
+ case "uuid":
1709
+ return "DEFAULT ''";
1710
+ case "integer":
1711
+ case "bigint":
1712
+ case "float":
1713
+ return "DEFAULT 0";
1714
+ case "boolean":
1715
+ return "DEFAULT 0";
1716
+ case "jsonb":
1717
+ return "DEFAULT '{}'";
1718
+ case "timestamp":
1719
+ return "DEFAULT CURRENT_TIMESTAMP";
1720
+ default:
1721
+ return "DEFAULT ''";
1722
+ }
1723
+ }
1724
+ /**
1725
+ * Alters an existing table to add missing columns.
1726
+ * Used for schema migrations when new columns are added.
1727
+ *
1728
+ * @param args - The alter table arguments
1729
+ * @param args.tableName - The name of the table to alter
1730
+ * @param args.schema - The full schema definition for the table
1731
+ * @param args.ifNotExists - Array of column names to add if they don't exist
1732
+ */
1733
+ async alterTable({
1734
+ tableName,
1735
+ schema,
1736
+ ifNotExists
1285
1737
  }) {
1286
- if (messages.length === 0) return messages;
1738
+ const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
1287
1739
  try {
1288
- const threadId = messages[0]?.threadId;
1289
- if (!threadId) {
1290
- throw new Error("Thread ID is required");
1291
- }
1292
- const batchStatements = messages.map((message) => {
1293
- const time = message.createdAt || /* @__PURE__ */ new Date();
1294
- if (!message.threadId) {
1295
- throw new Error(
1296
- `Expected to find a threadId for message, but couldn't find one. An unexpected error has occurred.`
1297
- );
1298
- }
1299
- if (!message.resourceId) {
1300
- throw new Error(
1301
- `Expected to find a resourceId for message, but couldn't find one. An unexpected error has occurred.`
1302
- );
1303
- }
1304
- return {
1305
- sql: `INSERT INTO "${storage.TABLE_MESSAGES}" (id, thread_id, content, role, type, "createdAt", "resourceId")
1306
- VALUES (?, ?, ?, ?, ?, ?, ?)
1307
- ON CONFLICT(id) DO UPDATE SET
1308
- thread_id=excluded.thread_id,
1309
- content=excluded.content,
1310
- role=excluded.role,
1311
- type=excluded.type,
1312
- "resourceId"=excluded."resourceId"
1313
- `,
1314
- args: [
1315
- message.id,
1316
- message.threadId,
1317
- typeof message.content === "object" ? JSON.stringify(message.content) : message.content,
1318
- message.role,
1319
- message.type || "v2",
1320
- time instanceof Date ? time.toISOString() : time,
1321
- message.resourceId
1322
- ]
1323
- };
1324
- });
1325
- const now = (/* @__PURE__ */ new Date()).toISOString();
1326
- batchStatements.push({
1327
- sql: `UPDATE "${storage.TABLE_THREADS}" SET "updatedAt" = ? WHERE id = ?`,
1328
- args: [now, threadId]
1740
+ const tableInfo = await this.client.execute({
1741
+ sql: `PRAGMA table_info("${parsedTableName}")`
1329
1742
  });
1330
- const BATCH_SIZE = 50;
1331
- const messageStatements = batchStatements.slice(0, -1);
1332
- const threadUpdateStatement = batchStatements[batchStatements.length - 1];
1333
- for (let i = 0; i < messageStatements.length; i += BATCH_SIZE) {
1334
- const batch = messageStatements.slice(i, i + BATCH_SIZE);
1335
- if (batch.length > 0) {
1336
- await this.client.batch(batch, "write");
1743
+ const existingColumns = new Set((tableInfo.rows || []).map((row) => row.name?.toLowerCase()));
1744
+ for (const columnName of ifNotExists) {
1745
+ if (!existingColumns.has(columnName.toLowerCase()) && schema[columnName]) {
1746
+ const columnDef = schema[columnName];
1747
+ const sqlType = this.getSqlType(columnDef.type);
1748
+ const defaultValue = this.getDefaultValue(columnDef.type);
1749
+ const alterSql = `ALTER TABLE ${parsedTableName} ADD COLUMN "${columnName}" ${sqlType} ${defaultValue}`;
1750
+ await this.client.execute(alterSql);
1751
+ this.logger.debug(`LibSQLDB: Added column ${columnName} to table ${tableName}`);
1337
1752
  }
1338
1753
  }
1339
- if (threadUpdateStatement) {
1340
- await this.client.execute(threadUpdateStatement);
1341
- }
1342
- const list = new agent.MessageList().add(messages, "memory");
1343
- if (format === `v2`) return list.get.all.v2();
1344
- return list.get.all.v1();
1345
1754
  } catch (error$1) {
1346
1755
  throw new error.MastraError(
1347
1756
  {
1348
- id: "LIBSQL_STORE_SAVE_MESSAGES_FAILED",
1757
+ id: storage.createStorageErrorId("LIBSQL", "ALTER_TABLE", "FAILED"),
1349
1758
  domain: error.ErrorDomain.STORAGE,
1350
- category: error.ErrorCategory.THIRD_PARTY
1759
+ category: error.ErrorCategory.THIRD_PARTY,
1760
+ details: { tableName }
1351
1761
  },
1352
1762
  error$1
1353
1763
  );
1354
1764
  }
1355
1765
  }
1356
- async updateMessages({
1357
- messages
1358
- }) {
1359
- if (messages.length === 0) {
1360
- return [];
1361
- }
1362
- const messageIds = messages.map((m) => m.id);
1363
- const placeholders = messageIds.map(() => "?").join(",");
1364
- const selectSql = `SELECT * FROM ${storage.TABLE_MESSAGES} WHERE id IN (${placeholders})`;
1365
- const existingResult = await this.client.execute({ sql: selectSql, args: messageIds });
1366
- const existingMessages = existingResult.rows.map((row) => this.parseRow(row));
1367
- if (existingMessages.length === 0) {
1368
- return [];
1369
- }
1370
- const batchStatements = [];
1371
- const threadIdsToUpdate = /* @__PURE__ */ new Set();
1372
- const columnMapping = {
1373
- threadId: "thread_id"
1374
- };
1375
- for (const existingMessage of existingMessages) {
1376
- const updatePayload = messages.find((m) => m.id === existingMessage.id);
1377
- if (!updatePayload) continue;
1378
- const { id, ...fieldsToUpdate } = updatePayload;
1379
- if (Object.keys(fieldsToUpdate).length === 0) continue;
1380
- threadIdsToUpdate.add(existingMessage.threadId);
1381
- if (updatePayload.threadId && updatePayload.threadId !== existingMessage.threadId) {
1382
- threadIdsToUpdate.add(updatePayload.threadId);
1383
- }
1384
- const setClauses = [];
1385
- const args = [];
1386
- const updatableFields = { ...fieldsToUpdate };
1387
- if (updatableFields.content) {
1388
- const newContent = {
1389
- ...existingMessage.content,
1390
- ...updatableFields.content,
1391
- // Deep merge metadata if it exists on both
1392
- ...existingMessage.content?.metadata && updatableFields.content.metadata ? {
1393
- metadata: {
1394
- ...existingMessage.content.metadata,
1395
- ...updatableFields.content.metadata
1396
- }
1397
- } : {}
1398
- };
1399
- setClauses.push(`${utils.parseSqlIdentifier("content", "column name")} = ?`);
1400
- args.push(JSON.stringify(newContent));
1401
- delete updatableFields.content;
1402
- }
1403
- for (const key in updatableFields) {
1404
- if (Object.prototype.hasOwnProperty.call(updatableFields, key)) {
1405
- const dbKey = columnMapping[key] || key;
1406
- setClauses.push(`${utils.parseSqlIdentifier(dbKey, "column name")} = ?`);
1407
- let value = updatableFields[key];
1408
- if (typeof value === "object" && value !== null) {
1409
- value = JSON.stringify(value);
1766
+ /**
1767
+ * Deletes all records from the specified table.
1768
+ * Errors are logged but not thrown.
1769
+ *
1770
+ * @param args - The delete arguments
1771
+ * @param args.tableName - The name of the table to clear
1772
+ */
1773
+ async deleteData({ tableName }) {
1774
+ const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
1775
+ try {
1776
+ await this.client.execute(`DELETE FROM ${parsedTableName}`);
1777
+ } catch (e) {
1778
+ const mastraError = new error.MastraError(
1779
+ {
1780
+ id: storage.createStorageErrorId("LIBSQL", "CLEAR_TABLE", "FAILED"),
1781
+ domain: error.ErrorDomain.STORAGE,
1782
+ category: error.ErrorCategory.THIRD_PARTY,
1783
+ details: {
1784
+ tableName
1410
1785
  }
1411
- args.push(value);
1412
- }
1413
- }
1414
- if (setClauses.length === 0) continue;
1415
- args.push(id);
1416
- const sql = `UPDATE ${storage.TABLE_MESSAGES} SET ${setClauses.join(", ")} WHERE id = ?`;
1417
- batchStatements.push({ sql, args });
1418
- }
1419
- if (batchStatements.length === 0) {
1420
- return existingMessages;
1421
- }
1422
- const now = (/* @__PURE__ */ new Date()).toISOString();
1423
- for (const threadId of threadIdsToUpdate) {
1424
- if (threadId) {
1425
- batchStatements.push({
1426
- sql: `UPDATE ${storage.TABLE_THREADS} SET updatedAt = ? WHERE id = ?`,
1427
- args: [now, threadId]
1428
- });
1429
- }
1786
+ },
1787
+ e
1788
+ );
1789
+ this.logger?.trackException?.(mastraError);
1790
+ this.logger?.error?.(mastraError.toString());
1430
1791
  }
1431
- await this.client.batch(batchStatements, "write");
1432
- const updatedResult = await this.client.execute({ sql: selectSql, args: messageIds });
1433
- return updatedResult.rows.map((row) => this.parseRow(row));
1434
1792
  }
1435
- async deleteMessages(messageIds) {
1436
- if (!messageIds || messageIds.length === 0) {
1437
- return;
1438
- }
1793
+ };
1794
+
1795
+ // src/storage/domains/agents/index.ts
1796
+ var AgentsLibSQL = class extends storage.AgentsStorage {
1797
+ #db;
1798
+ constructor(config) {
1799
+ super();
1800
+ const client = resolveClient(config);
1801
+ this.#db = new LibSQLDB({ client, maxRetries: config.maxRetries, initialBackoffMs: config.initialBackoffMs });
1802
+ }
1803
+ async init() {
1804
+ await this.#db.createTable({ tableName: storage.TABLE_AGENTS, schema: storage.AGENTS_SCHEMA });
1805
+ }
1806
+ async dangerouslyClearAll() {
1807
+ await this.#db.deleteData({ tableName: storage.TABLE_AGENTS });
1808
+ }
1809
+ parseJson(value, fieldName) {
1810
+ if (!value) return void 0;
1811
+ if (typeof value !== "string") return value;
1439
1812
  try {
1440
- const BATCH_SIZE = 100;
1441
- const threadIds = /* @__PURE__ */ new Set();
1442
- const tx = await this.client.transaction("write");
1443
- try {
1444
- for (let i = 0; i < messageIds.length; i += BATCH_SIZE) {
1445
- const batch = messageIds.slice(i, i + BATCH_SIZE);
1446
- const placeholders = batch.map(() => "?").join(",");
1447
- const result = await tx.execute({
1448
- sql: `SELECT DISTINCT thread_id FROM "${storage.TABLE_MESSAGES}" WHERE id IN (${placeholders})`,
1449
- args: batch
1450
- });
1451
- result.rows?.forEach((row) => {
1452
- if (row.thread_id) threadIds.add(row.thread_id);
1453
- });
1454
- await tx.execute({
1455
- sql: `DELETE FROM "${storage.TABLE_MESSAGES}" WHERE id IN (${placeholders})`,
1456
- args: batch
1457
- });
1458
- }
1459
- if (threadIds.size > 0) {
1460
- const now = (/* @__PURE__ */ new Date()).toISOString();
1461
- for (const threadId of threadIds) {
1462
- await tx.execute({
1463
- sql: `UPDATE "${storage.TABLE_THREADS}" SET "updatedAt" = ? WHERE id = ?`,
1464
- args: [now, threadId]
1465
- });
1466
- }
1467
- }
1468
- await tx.commit();
1469
- } catch (error) {
1470
- await tx.rollback();
1471
- throw error;
1472
- }
1813
+ return JSON.parse(value);
1473
1814
  } catch (error$1) {
1815
+ const details = {
1816
+ value: value.length > 100 ? value.substring(0, 100) + "..." : value
1817
+ };
1818
+ if (fieldName) {
1819
+ details.field = fieldName;
1820
+ }
1474
1821
  throw new error.MastraError(
1475
1822
  {
1476
- id: "LIBSQL_STORE_DELETE_MESSAGES_FAILED",
1823
+ id: storage.createStorageErrorId("LIBSQL", "PARSE_JSON", "INVALID_JSON"),
1477
1824
  domain: error.ErrorDomain.STORAGE,
1478
- category: error.ErrorCategory.THIRD_PARTY,
1479
- details: { messageIds: messageIds.join(", ") }
1825
+ category: error.ErrorCategory.SYSTEM,
1826
+ text: `Failed to parse JSON${fieldName ? ` for field "${fieldName}"` : ""}: ${error$1 instanceof Error ? error$1.message : "Unknown error"}`,
1827
+ details
1480
1828
  },
1481
1829
  error$1
1482
1830
  );
1483
1831
  }
1484
1832
  }
1485
- async getResourceById({ resourceId }) {
1486
- const result = await this.operations.load({
1487
- tableName: storage.TABLE_RESOURCES,
1488
- keys: { id: resourceId }
1489
- });
1490
- if (!result) {
1491
- return null;
1492
- }
1833
+ parseRow(row) {
1493
1834
  return {
1494
- ...result,
1495
- // Ensure workingMemory is always returned as a string, even if auto-parsed as JSON
1496
- workingMemory: result.workingMemory && typeof result.workingMemory === "object" ? JSON.stringify(result.workingMemory) : result.workingMemory,
1497
- metadata: typeof result.metadata === "string" ? JSON.parse(result.metadata) : result.metadata,
1498
- createdAt: new Date(result.createdAt),
1499
- updatedAt: new Date(result.updatedAt)
1835
+ id: row.id,
1836
+ name: row.name,
1837
+ description: row.description,
1838
+ instructions: row.instructions,
1839
+ model: this.parseJson(row.model, "model"),
1840
+ tools: this.parseJson(row.tools, "tools"),
1841
+ defaultOptions: this.parseJson(row.defaultOptions, "defaultOptions"),
1842
+ workflows: this.parseJson(row.workflows, "workflows"),
1843
+ agents: this.parseJson(row.agents, "agents"),
1844
+ inputProcessors: this.parseJson(row.inputProcessors, "inputProcessors"),
1845
+ outputProcessors: this.parseJson(row.outputProcessors, "outputProcessors"),
1846
+ memory: this.parseJson(row.memory, "memory"),
1847
+ scorers: this.parseJson(row.scorers, "scorers"),
1848
+ metadata: this.parseJson(row.metadata, "metadata"),
1849
+ createdAt: new Date(row.createdAt),
1850
+ updatedAt: new Date(row.updatedAt)
1500
1851
  };
1501
1852
  }
1502
- async saveResource({ resource }) {
1503
- await this.operations.insert({
1504
- tableName: storage.TABLE_RESOURCES,
1505
- record: {
1506
- ...resource,
1507
- metadata: JSON.stringify(resource.metadata)
1508
- }
1509
- });
1510
- return resource;
1511
- }
1512
- async updateResource({
1513
- resourceId,
1514
- workingMemory,
1515
- metadata
1516
- }) {
1517
- const existingResource = await this.getResourceById({ resourceId });
1518
- if (!existingResource) {
1519
- const newResource = {
1520
- id: resourceId,
1521
- workingMemory,
1522
- metadata: metadata || {},
1523
- createdAt: /* @__PURE__ */ new Date(),
1524
- updatedAt: /* @__PURE__ */ new Date()
1525
- };
1526
- return this.saveResource({ resource: newResource });
1527
- }
1528
- const updatedResource = {
1529
- ...existingResource,
1530
- workingMemory: workingMemory !== void 0 ? workingMemory : existingResource.workingMemory,
1531
- metadata: {
1532
- ...existingResource.metadata,
1533
- ...metadata
1534
- },
1535
- updatedAt: /* @__PURE__ */ new Date()
1536
- };
1537
- const updates = [];
1538
- const values = [];
1539
- if (workingMemory !== void 0) {
1540
- updates.push("workingMemory = ?");
1541
- values.push(workingMemory);
1542
- }
1543
- if (metadata) {
1544
- updates.push("metadata = ?");
1545
- values.push(JSON.stringify(updatedResource.metadata));
1853
+ async getAgentById({ id }) {
1854
+ try {
1855
+ const result = await this.#db.select({
1856
+ tableName: storage.TABLE_AGENTS,
1857
+ keys: { id }
1858
+ });
1859
+ return result ? this.parseRow(result) : null;
1860
+ } catch (error$1) {
1861
+ throw new error.MastraError(
1862
+ {
1863
+ id: storage.createStorageErrorId("LIBSQL", "GET_AGENT_BY_ID", "FAILED"),
1864
+ domain: error.ErrorDomain.STORAGE,
1865
+ category: error.ErrorCategory.THIRD_PARTY,
1866
+ details: { agentId: id }
1867
+ },
1868
+ error$1
1869
+ );
1546
1870
  }
1547
- updates.push("updatedAt = ?");
1548
- values.push(updatedResource.updatedAt.toISOString());
1549
- values.push(resourceId);
1550
- await this.client.execute({
1551
- sql: `UPDATE ${storage.TABLE_RESOURCES} SET ${updates.join(", ")} WHERE id = ?`,
1552
- args: values
1553
- });
1554
- return updatedResource;
1555
1871
  }
1556
- async getThreadById({ threadId }) {
1872
+ async createAgent({ agent }) {
1557
1873
  try {
1558
- const result = await this.operations.load({
1559
- tableName: storage.TABLE_THREADS,
1560
- keys: { id: threadId }
1874
+ const now = /* @__PURE__ */ new Date();
1875
+ await this.#db.insert({
1876
+ tableName: storage.TABLE_AGENTS,
1877
+ record: {
1878
+ id: agent.id,
1879
+ name: agent.name,
1880
+ description: agent.description ?? null,
1881
+ instructions: agent.instructions,
1882
+ model: agent.model,
1883
+ tools: agent.tools ?? null,
1884
+ defaultOptions: agent.defaultOptions ?? null,
1885
+ workflows: agent.workflows ?? null,
1886
+ agents: agent.agents ?? null,
1887
+ inputProcessors: agent.inputProcessors ?? null,
1888
+ outputProcessors: agent.outputProcessors ?? null,
1889
+ memory: agent.memory ?? null,
1890
+ scorers: agent.scorers ?? null,
1891
+ metadata: agent.metadata ?? null,
1892
+ createdAt: now,
1893
+ updatedAt: now
1894
+ }
1561
1895
  });
1562
- if (!result) {
1563
- return null;
1564
- }
1565
1896
  return {
1566
- ...result,
1567
- metadata: typeof result.metadata === "string" ? JSON.parse(result.metadata) : result.metadata,
1568
- createdAt: new Date(result.createdAt),
1569
- updatedAt: new Date(result.updatedAt)
1897
+ ...agent,
1898
+ createdAt: now,
1899
+ updatedAt: now
1570
1900
  };
1571
1901
  } catch (error$1) {
1572
1902
  throw new error.MastraError(
1573
1903
  {
1574
- id: "LIBSQL_STORE_GET_THREAD_BY_ID_FAILED",
1904
+ id: storage.createStorageErrorId("LIBSQL", "CREATE_AGENT", "FAILED"),
1575
1905
  domain: error.ErrorDomain.STORAGE,
1576
1906
  category: error.ErrorCategory.THIRD_PARTY,
1577
- details: { threadId }
1907
+ details: { agentId: agent.id }
1578
1908
  },
1579
1909
  error$1
1580
1910
  );
1581
1911
  }
1582
1912
  }
1583
- /**
1584
- * @deprecated use getThreadsByResourceIdPaginated instead for paginated results.
1585
- */
1586
- async getThreadsByResourceId(args) {
1587
- const resourceId = args.resourceId;
1588
- const orderBy = this.castThreadOrderBy(args.orderBy);
1589
- const sortDirection = this.castThreadSortDirection(args.sortDirection);
1913
+ async updateAgent({ id, ...updates }) {
1590
1914
  try {
1591
- const baseQuery = `FROM ${storage.TABLE_THREADS} WHERE resourceId = ?`;
1592
- const queryParams = [resourceId];
1593
- const mapRowToStorageThreadType = (row) => ({
1594
- id: row.id,
1595
- resourceId: row.resourceId,
1596
- title: row.title,
1597
- createdAt: new Date(row.createdAt),
1598
- // Convert string to Date
1599
- updatedAt: new Date(row.updatedAt),
1600
- // Convert string to Date
1601
- metadata: typeof row.metadata === "string" ? JSON.parse(row.metadata) : row.metadata
1602
- });
1603
- const result = await this.client.execute({
1604
- sql: `SELECT * ${baseQuery} ORDER BY ${orderBy} ${sortDirection}`,
1605
- args: queryParams
1606
- });
1607
- if (!result.rows) {
1608
- return [];
1915
+ const existingAgent = await this.getAgentById({ id });
1916
+ if (!existingAgent) {
1917
+ throw new error.MastraError({
1918
+ id: storage.createStorageErrorId("LIBSQL", "UPDATE_AGENT", "NOT_FOUND"),
1919
+ domain: error.ErrorDomain.STORAGE,
1920
+ category: error.ErrorCategory.USER,
1921
+ text: `Agent ${id} not found`,
1922
+ details: { agentId: id }
1923
+ });
1924
+ }
1925
+ const data = {
1926
+ updatedAt: /* @__PURE__ */ new Date()
1927
+ };
1928
+ if (updates.name !== void 0) data.name = updates.name;
1929
+ if (updates.description !== void 0) data.description = updates.description;
1930
+ if (updates.instructions !== void 0) data.instructions = updates.instructions;
1931
+ if (updates.model !== void 0) data.model = updates.model;
1932
+ if (updates.tools !== void 0) data.tools = updates.tools;
1933
+ if (updates.defaultOptions !== void 0) data.defaultOptions = updates.defaultOptions;
1934
+ if (updates.workflows !== void 0) data.workflows = updates.workflows;
1935
+ if (updates.agents !== void 0) data.agents = updates.agents;
1936
+ if (updates.inputProcessors !== void 0) data.inputProcessors = updates.inputProcessors;
1937
+ if (updates.outputProcessors !== void 0) data.outputProcessors = updates.outputProcessors;
1938
+ if (updates.memory !== void 0) data.memory = updates.memory;
1939
+ if (updates.scorers !== void 0) data.scorers = updates.scorers;
1940
+ if (updates.metadata !== void 0) {
1941
+ data.metadata = { ...existingAgent.metadata, ...updates.metadata };
1942
+ }
1943
+ if (Object.keys(data).length > 1) {
1944
+ await this.#db.update({
1945
+ tableName: storage.TABLE_AGENTS,
1946
+ keys: { id },
1947
+ data
1948
+ });
1949
+ }
1950
+ const updatedAgent = await this.getAgentById({ id });
1951
+ if (!updatedAgent) {
1952
+ throw new error.MastraError({
1953
+ id: storage.createStorageErrorId("LIBSQL", "UPDATE_AGENT", "NOT_FOUND_AFTER_UPDATE"),
1954
+ domain: error.ErrorDomain.STORAGE,
1955
+ category: error.ErrorCategory.SYSTEM,
1956
+ text: `Agent ${id} not found after update`,
1957
+ details: { agentId: id }
1958
+ });
1609
1959
  }
1610
- return result.rows.map(mapRowToStorageThreadType);
1960
+ return updatedAgent;
1611
1961
  } catch (error$1) {
1612
- const mastraError = new error.MastraError(
1962
+ if (error$1 instanceof error.MastraError) {
1963
+ throw error$1;
1964
+ }
1965
+ throw new error.MastraError(
1613
1966
  {
1614
- id: "LIBSQL_STORE_GET_THREADS_BY_RESOURCE_ID_FAILED",
1967
+ id: storage.createStorageErrorId("LIBSQL", "UPDATE_AGENT", "FAILED"),
1615
1968
  domain: error.ErrorDomain.STORAGE,
1616
1969
  category: error.ErrorCategory.THIRD_PARTY,
1617
- details: { resourceId }
1970
+ details: { agentId: id }
1618
1971
  },
1619
1972
  error$1
1620
1973
  );
1621
- this.logger?.trackException?.(mastraError);
1622
- this.logger?.error?.(mastraError.toString());
1623
- return [];
1624
1974
  }
1625
1975
  }
1626
- async getThreadsByResourceIdPaginated(args) {
1627
- const { resourceId, page = 0, perPage = 100 } = args;
1628
- const orderBy = this.castThreadOrderBy(args.orderBy);
1629
- const sortDirection = this.castThreadSortDirection(args.sortDirection);
1976
+ async deleteAgent({ id }) {
1630
1977
  try {
1631
- const baseQuery = `FROM ${storage.TABLE_THREADS} WHERE resourceId = ?`;
1632
- const queryParams = [resourceId];
1633
- const mapRowToStorageThreadType = (row) => ({
1634
- id: row.id,
1635
- resourceId: row.resourceId,
1636
- title: row.title,
1637
- createdAt: new Date(row.createdAt),
1638
- // Convert string to Date
1639
- updatedAt: new Date(row.updatedAt),
1640
- // Convert string to Date
1641
- metadata: typeof row.metadata === "string" ? JSON.parse(row.metadata) : row.metadata
1642
- });
1643
- const currentOffset = page * perPage;
1644
- const countResult = await this.client.execute({
1645
- sql: `SELECT COUNT(*) as count ${baseQuery}`,
1646
- args: queryParams
1978
+ await this.#db.delete({
1979
+ tableName: storage.TABLE_AGENTS,
1980
+ keys: { id }
1647
1981
  });
1648
- const total = Number(countResult.rows?.[0]?.count ?? 0);
1982
+ } catch (error$1) {
1983
+ throw new error.MastraError(
1984
+ {
1985
+ id: storage.createStorageErrorId("LIBSQL", "DELETE_AGENT", "FAILED"),
1986
+ domain: error.ErrorDomain.STORAGE,
1987
+ category: error.ErrorCategory.THIRD_PARTY,
1988
+ details: { agentId: id }
1989
+ },
1990
+ error$1
1991
+ );
1992
+ }
1993
+ }
1994
+ async listAgents(args) {
1995
+ const { page = 0, perPage: perPageInput, orderBy } = args || {};
1996
+ const { field, direction } = this.parseOrderBy(orderBy);
1997
+ if (page < 0) {
1998
+ throw new error.MastraError(
1999
+ {
2000
+ id: storage.createStorageErrorId("LIBSQL", "LIST_AGENTS", "INVALID_PAGE"),
2001
+ domain: error.ErrorDomain.STORAGE,
2002
+ category: error.ErrorCategory.USER,
2003
+ details: { page }
2004
+ },
2005
+ new Error("page must be >= 0")
2006
+ );
2007
+ }
2008
+ const perPage = storage.normalizePerPage(perPageInput, 100);
2009
+ const { offset, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
2010
+ try {
2011
+ const total = await this.#db.selectTotalCount({ tableName: storage.TABLE_AGENTS });
1649
2012
  if (total === 0) {
1650
2013
  return {
1651
- threads: [],
2014
+ agents: [],
1652
2015
  total: 0,
1653
2016
  page,
1654
- perPage,
2017
+ perPage: perPageForResponse,
1655
2018
  hasMore: false
1656
2019
  };
1657
2020
  }
1658
- const dataResult = await this.client.execute({
1659
- sql: `SELECT * ${baseQuery} ORDER BY ${orderBy} ${sortDirection} LIMIT ? OFFSET ?`,
1660
- args: [...queryParams, perPage, currentOffset]
2021
+ const limitValue = perPageInput === false ? total : perPage;
2022
+ const rows = await this.#db.selectMany({
2023
+ tableName: storage.TABLE_AGENTS,
2024
+ orderBy: `"${field}" ${direction}`,
2025
+ limit: limitValue,
2026
+ offset
1661
2027
  });
1662
- const threads = (dataResult.rows || []).map(mapRowToStorageThreadType);
2028
+ const agents = rows.map((row) => this.parseRow(row));
1663
2029
  return {
1664
- threads,
2030
+ agents,
1665
2031
  total,
1666
2032
  page,
1667
- perPage,
1668
- hasMore: currentOffset + threads.length < total
2033
+ perPage: perPageForResponse,
2034
+ hasMore: perPageInput === false ? false : offset + perPage < total
1669
2035
  };
1670
2036
  } catch (error$1) {
1671
- const mastraError = new error.MastraError(
2037
+ throw new error.MastraError(
1672
2038
  {
1673
- id: "LIBSQL_STORE_GET_THREADS_BY_RESOURCE_ID_FAILED",
2039
+ id: storage.createStorageErrorId("LIBSQL", "LIST_AGENTS", "FAILED"),
1674
2040
  domain: error.ErrorDomain.STORAGE,
1675
- category: error.ErrorCategory.THIRD_PARTY,
1676
- details: { resourceId }
2041
+ category: error.ErrorCategory.THIRD_PARTY
1677
2042
  },
1678
2043
  error$1
1679
2044
  );
1680
- this.logger?.trackException?.(mastraError);
1681
- this.logger?.error?.(mastraError.toString());
1682
- return { threads: [], total: 0, page, perPage, hasMore: false };
1683
2045
  }
1684
2046
  }
1685
- async saveThread({ thread }) {
2047
+ };
2048
+ var MemoryLibSQL = class extends storage.MemoryStorage {
2049
+ #client;
2050
+ #db;
2051
+ constructor(config) {
2052
+ super();
2053
+ const client = resolveClient(config);
2054
+ this.#client = client;
2055
+ this.#db = new LibSQLDB({ client, maxRetries: config.maxRetries, initialBackoffMs: config.initialBackoffMs });
2056
+ }
2057
+ async init() {
2058
+ await this.#db.createTable({ tableName: storage.TABLE_THREADS, schema: storage.TABLE_SCHEMAS[storage.TABLE_THREADS] });
2059
+ await this.#db.createTable({ tableName: storage.TABLE_MESSAGES, schema: storage.TABLE_SCHEMAS[storage.TABLE_MESSAGES] });
2060
+ await this.#db.createTable({ tableName: storage.TABLE_RESOURCES, schema: storage.TABLE_SCHEMAS[storage.TABLE_RESOURCES] });
2061
+ await this.#db.alterTable({
2062
+ tableName: storage.TABLE_MESSAGES,
2063
+ schema: storage.TABLE_SCHEMAS[storage.TABLE_MESSAGES],
2064
+ ifNotExists: ["resourceId"]
2065
+ });
2066
+ }
2067
+ async dangerouslyClearAll() {
2068
+ await this.#db.deleteData({ tableName: storage.TABLE_MESSAGES });
2069
+ await this.#db.deleteData({ tableName: storage.TABLE_THREADS });
2070
+ await this.#db.deleteData({ tableName: storage.TABLE_RESOURCES });
2071
+ }
2072
+ parseRow(row) {
2073
+ let content = row.content;
1686
2074
  try {
1687
- await this.operations.insert({
1688
- tableName: storage.TABLE_THREADS,
1689
- record: {
1690
- ...thread,
1691
- metadata: JSON.stringify(thread.metadata)
1692
- }
1693
- });
1694
- return thread;
2075
+ content = JSON.parse(row.content);
2076
+ } catch {
2077
+ }
2078
+ const result = {
2079
+ id: row.id,
2080
+ content,
2081
+ role: row.role,
2082
+ createdAt: new Date(row.createdAt),
2083
+ threadId: row.thread_id,
2084
+ resourceId: row.resourceId
2085
+ };
2086
+ if (row.type && row.type !== `v2`) result.type = row.type;
2087
+ return result;
2088
+ }
2089
+ async _getIncludedMessages({ include }) {
2090
+ if (!include || include.length === 0) return null;
2091
+ const unionQueries = [];
2092
+ const params = [];
2093
+ for (const inc of include) {
2094
+ const { id, withPreviousMessages = 0, withNextMessages = 0 } = inc;
2095
+ unionQueries.push(
2096
+ `
2097
+ SELECT * FROM (
2098
+ WITH target_thread AS (
2099
+ SELECT thread_id FROM "${storage.TABLE_MESSAGES}" WHERE id = ?
2100
+ ),
2101
+ numbered_messages AS (
2102
+ SELECT
2103
+ id, content, role, type, "createdAt", thread_id, "resourceId",
2104
+ ROW_NUMBER() OVER (ORDER BY "createdAt" ASC) as row_num
2105
+ FROM "${storage.TABLE_MESSAGES}"
2106
+ WHERE thread_id = (SELECT thread_id FROM target_thread)
2107
+ ),
2108
+ target_positions AS (
2109
+ SELECT row_num as target_pos
2110
+ FROM numbered_messages
2111
+ WHERE id = ?
2112
+ )
2113
+ SELECT DISTINCT m.*
2114
+ FROM numbered_messages m
2115
+ CROSS JOIN target_positions t
2116
+ WHERE m.row_num BETWEEN (t.target_pos - ?) AND (t.target_pos + ?)
2117
+ )
2118
+ `
2119
+ // Keep ASC for final sorting after fetching context
2120
+ );
2121
+ params.push(id, id, withPreviousMessages, withNextMessages);
2122
+ }
2123
+ const finalQuery = unionQueries.join(" UNION ALL ") + ' ORDER BY "createdAt" ASC';
2124
+ const includedResult = await this.#client.execute({ sql: finalQuery, args: params });
2125
+ const includedRows = includedResult.rows?.map((row) => this.parseRow(row));
2126
+ const seen = /* @__PURE__ */ new Set();
2127
+ const dedupedRows = includedRows.filter((row) => {
2128
+ if (seen.has(row.id)) return false;
2129
+ seen.add(row.id);
2130
+ return true;
2131
+ });
2132
+ return dedupedRows;
2133
+ }
2134
+ async listMessagesById({ messageIds }) {
2135
+ if (messageIds.length === 0) return { messages: [] };
2136
+ try {
2137
+ const sql = `
2138
+ SELECT
2139
+ id,
2140
+ content,
2141
+ role,
2142
+ type,
2143
+ "createdAt",
2144
+ thread_id,
2145
+ "resourceId"
2146
+ FROM "${storage.TABLE_MESSAGES}"
2147
+ WHERE id IN (${messageIds.map(() => "?").join(", ")})
2148
+ ORDER BY "createdAt" DESC
2149
+ `;
2150
+ const result = await this.#client.execute({ sql, args: messageIds });
2151
+ if (!result.rows) return { messages: [] };
2152
+ const list = new agent.MessageList().add(result.rows.map(this.parseRow), "memory");
2153
+ return { messages: list.get.all.db() };
1695
2154
  } catch (error$1) {
1696
- const mastraError = new error.MastraError(
2155
+ throw new error.MastraError(
1697
2156
  {
1698
- id: "LIBSQL_STORE_SAVE_THREAD_FAILED",
2157
+ id: storage.createStorageErrorId("LIBSQL", "LIST_MESSAGES_BY_ID", "FAILED"),
1699
2158
  domain: error.ErrorDomain.STORAGE,
1700
2159
  category: error.ErrorCategory.THIRD_PARTY,
1701
- details: { threadId: thread.id }
2160
+ details: { messageIds: JSON.stringify(messageIds) }
1702
2161
  },
1703
2162
  error$1
1704
2163
  );
1705
- this.logger?.trackException?.(mastraError);
1706
- this.logger?.error?.(mastraError.toString());
1707
- throw mastraError;
1708
2164
  }
1709
2165
  }
1710
- async updateThread({
1711
- id,
1712
- title,
1713
- metadata
1714
- }) {
1715
- const thread = await this.getThreadById({ threadId: id });
1716
- if (!thread) {
1717
- throw new error.MastraError({
1718
- id: "LIBSQL_STORE_UPDATE_THREAD_FAILED_THREAD_NOT_FOUND",
1719
- domain: error.ErrorDomain.STORAGE,
1720
- category: error.ErrorCategory.USER,
1721
- text: `Thread ${id} not found`,
1722
- details: {
1723
- status: 404,
1724
- threadId: id
1725
- }
1726
- });
1727
- }
1728
- const updatedThread = {
1729
- ...thread,
1730
- title,
1731
- metadata: {
1732
- ...thread.metadata,
1733
- ...metadata
1734
- }
1735
- };
1736
- try {
1737
- await this.client.execute({
1738
- sql: `UPDATE ${storage.TABLE_THREADS} SET title = ?, metadata = ? WHERE id = ?`,
1739
- args: [title, JSON.stringify(updatedThread.metadata), id]
1740
- });
1741
- return updatedThread;
1742
- } catch (error$1) {
2166
+ async listMessages(args) {
2167
+ const { threadId, resourceId, include, filter, perPage: perPageInput, page = 0, orderBy } = args;
2168
+ const threadIds = Array.isArray(threadId) ? threadId : [threadId];
2169
+ if (threadIds.length === 0 || threadIds.some((id) => !id.trim())) {
1743
2170
  throw new error.MastraError(
1744
2171
  {
1745
- id: "LIBSQL_STORE_UPDATE_THREAD_FAILED",
2172
+ id: storage.createStorageErrorId("LIBSQL", "LIST_MESSAGES", "INVALID_THREAD_ID"),
1746
2173
  domain: error.ErrorDomain.STORAGE,
1747
2174
  category: error.ErrorCategory.THIRD_PARTY,
1748
- text: `Failed to update thread ${id}`,
1749
- details: { threadId: id }
2175
+ details: { threadId: Array.isArray(threadId) ? threadId.join(",") : threadId }
1750
2176
  },
1751
- error$1
2177
+ new Error("threadId must be a non-empty string or array of non-empty strings")
1752
2178
  );
1753
2179
  }
1754
- }
1755
- async deleteThread({ threadId }) {
2180
+ if (page < 0) {
2181
+ throw new error.MastraError(
2182
+ {
2183
+ id: storage.createStorageErrorId("LIBSQL", "LIST_MESSAGES", "INVALID_PAGE"),
2184
+ domain: error.ErrorDomain.STORAGE,
2185
+ category: error.ErrorCategory.USER,
2186
+ details: { page }
2187
+ },
2188
+ new Error("page must be >= 0")
2189
+ );
2190
+ }
2191
+ const perPage = storage.normalizePerPage(perPageInput, 40);
2192
+ const { offset, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
1756
2193
  try {
1757
- await this.client.execute({
1758
- sql: `DELETE FROM ${storage.TABLE_MESSAGES} WHERE thread_id = ?`,
1759
- args: [threadId]
2194
+ const { field, direction } = this.parseOrderBy(orderBy, "ASC");
2195
+ const orderByStatement = `ORDER BY "${field}" ${direction}`;
2196
+ const threadPlaceholders = threadIds.map(() => "?").join(", ");
2197
+ const conditions = [`thread_id IN (${threadPlaceholders})`];
2198
+ const queryParams = [...threadIds];
2199
+ if (resourceId) {
2200
+ conditions.push(`"resourceId" = ?`);
2201
+ queryParams.push(resourceId);
2202
+ }
2203
+ if (filter?.dateRange?.start) {
2204
+ conditions.push(`"createdAt" >= ?`);
2205
+ queryParams.push(
2206
+ filter.dateRange.start instanceof Date ? filter.dateRange.start.toISOString() : filter.dateRange.start
2207
+ );
2208
+ }
2209
+ if (filter?.dateRange?.end) {
2210
+ conditions.push(`"createdAt" <= ?`);
2211
+ queryParams.push(
2212
+ filter.dateRange.end instanceof Date ? filter.dateRange.end.toISOString() : filter.dateRange.end
2213
+ );
2214
+ }
2215
+ const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
2216
+ const countResult = await this.#client.execute({
2217
+ sql: `SELECT COUNT(*) as count FROM ${storage.TABLE_MESSAGES} ${whereClause}`,
2218
+ args: queryParams
1760
2219
  });
1761
- await this.client.execute({
1762
- sql: `DELETE FROM ${storage.TABLE_THREADS} WHERE id = ?`,
1763
- args: [threadId]
2220
+ const total = Number(countResult.rows?.[0]?.count ?? 0);
2221
+ const limitValue = perPageInput === false ? total : perPage;
2222
+ const dataResult = await this.#client.execute({
2223
+ sql: `SELECT id, content, role, type, "createdAt", "resourceId", "thread_id" FROM ${storage.TABLE_MESSAGES} ${whereClause} ${orderByStatement} LIMIT ? OFFSET ?`,
2224
+ args: [...queryParams, limitValue, offset]
2225
+ });
2226
+ const messages = (dataResult.rows || []).map((row) => this.parseRow(row));
2227
+ if (total === 0 && messages.length === 0 && (!include || include.length === 0)) {
2228
+ return {
2229
+ messages: [],
2230
+ total: 0,
2231
+ page,
2232
+ perPage: perPageForResponse,
2233
+ hasMore: false
2234
+ };
2235
+ }
2236
+ const messageIds = new Set(messages.map((m) => m.id));
2237
+ if (include && include.length > 0) {
2238
+ const includeMessages = await this._getIncludedMessages({ include });
2239
+ if (includeMessages) {
2240
+ for (const includeMsg of includeMessages) {
2241
+ if (!messageIds.has(includeMsg.id)) {
2242
+ messages.push(includeMsg);
2243
+ messageIds.add(includeMsg.id);
2244
+ }
2245
+ }
2246
+ }
2247
+ }
2248
+ const list = new agent.MessageList().add(messages, "memory");
2249
+ let finalMessages = list.get.all.db();
2250
+ finalMessages = finalMessages.sort((a, b) => {
2251
+ const isDateField = field === "createdAt" || field === "updatedAt";
2252
+ const aValue = isDateField ? new Date(a[field]).getTime() : a[field];
2253
+ const bValue = isDateField ? new Date(b[field]).getTime() : b[field];
2254
+ if (typeof aValue === "number" && typeof bValue === "number") {
2255
+ return direction === "ASC" ? aValue - bValue : bValue - aValue;
2256
+ }
2257
+ return direction === "ASC" ? String(aValue).localeCompare(String(bValue)) : String(bValue).localeCompare(String(aValue));
1764
2258
  });
2259
+ const threadIdSet = new Set(threadIds);
2260
+ const returnedThreadMessageIds = new Set(
2261
+ finalMessages.filter((m) => m.threadId && threadIdSet.has(m.threadId)).map((m) => m.id)
2262
+ );
2263
+ const allThreadMessagesReturned = returnedThreadMessageIds.size >= total;
2264
+ const hasMore = perPageInput !== false && !allThreadMessagesReturned && offset + perPage < total;
2265
+ return {
2266
+ messages: finalMessages,
2267
+ total,
2268
+ page,
2269
+ perPage: perPageForResponse,
2270
+ hasMore
2271
+ };
1765
2272
  } catch (error$1) {
1766
- throw new error.MastraError(
2273
+ const mastraError = new error.MastraError(
1767
2274
  {
1768
- id: "LIBSQL_STORE_DELETE_THREAD_FAILED",
2275
+ id: storage.createStorageErrorId("LIBSQL", "LIST_MESSAGES", "FAILED"),
1769
2276
  domain: error.ErrorDomain.STORAGE,
1770
2277
  category: error.ErrorCategory.THIRD_PARTY,
1771
- details: { threadId }
2278
+ details: {
2279
+ threadId: Array.isArray(threadId) ? threadId.join(",") : threadId,
2280
+ resourceId: resourceId ?? ""
2281
+ }
1772
2282
  },
1773
2283
  error$1
1774
2284
  );
2285
+ this.logger?.error?.(mastraError.toString());
2286
+ this.logger?.trackException?.(mastraError);
2287
+ return {
2288
+ messages: [],
2289
+ total: 0,
2290
+ page,
2291
+ perPage: perPageForResponse,
2292
+ hasMore: false
2293
+ };
1775
2294
  }
1776
2295
  }
1777
- };
1778
- function createExecuteWriteOperationWithRetry({
1779
- logger,
1780
- maxRetries,
1781
- initialBackoffMs
1782
- }) {
1783
- return async function executeWriteOperationWithRetry(operationFn, operationDescription) {
1784
- let retries = 0;
1785
- while (true) {
1786
- try {
1787
- return await operationFn();
1788
- } catch (error) {
1789
- if (error.message && (error.message.includes("SQLITE_BUSY") || error.message.includes("database is locked")) && retries < maxRetries) {
1790
- retries++;
1791
- const backoffTime = initialBackoffMs * Math.pow(2, retries - 1);
1792
- logger.warn(
1793
- `LibSQLStore: Encountered SQLITE_BUSY during ${operationDescription}. Retrying (${retries}/${maxRetries}) in ${backoffTime}ms...`
2296
+ async saveMessages({ messages }) {
2297
+ if (messages.length === 0) return { messages };
2298
+ try {
2299
+ const threadId = messages[0]?.threadId;
2300
+ if (!threadId) {
2301
+ throw new Error("Thread ID is required");
2302
+ }
2303
+ const batchStatements = messages.map((message) => {
2304
+ const time = message.createdAt || /* @__PURE__ */ new Date();
2305
+ if (!message.threadId) {
2306
+ throw new Error(
2307
+ `Expected to find a threadId for message, but couldn't find one. An unexpected error has occurred.`
1794
2308
  );
1795
- await new Promise((resolve) => setTimeout(resolve, backoffTime));
1796
- } else {
1797
- logger.error(`LibSQLStore: Error during ${operationDescription} after ${retries} retries: ${error}`);
1798
- throw error;
1799
2309
  }
1800
- }
1801
- }
1802
- };
1803
- }
1804
- function prepareStatement({ tableName, record }) {
1805
- const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
1806
- const columns = Object.keys(record).map((col) => utils.parseSqlIdentifier(col, "column name"));
1807
- const values = Object.values(record).map((v) => {
1808
- if (typeof v === `undefined` || v === null) {
1809
- return null;
1810
- }
1811
- if (v instanceof Date) {
1812
- return v.toISOString();
2310
+ if (!message.resourceId) {
2311
+ throw new Error(
2312
+ `Expected to find a resourceId for message, but couldn't find one. An unexpected error has occurred.`
2313
+ );
2314
+ }
2315
+ return {
2316
+ sql: `INSERT INTO "${storage.TABLE_MESSAGES}" (id, thread_id, content, role, type, "createdAt", "resourceId")
2317
+ VALUES (?, ?, ?, ?, ?, ?, ?)
2318
+ ON CONFLICT(id) DO UPDATE SET
2319
+ thread_id=excluded.thread_id,
2320
+ content=excluded.content,
2321
+ role=excluded.role,
2322
+ type=excluded.type,
2323
+ "resourceId"=excluded."resourceId"
2324
+ `,
2325
+ args: [
2326
+ message.id,
2327
+ message.threadId,
2328
+ typeof message.content === "object" ? JSON.stringify(message.content) : message.content,
2329
+ message.role,
2330
+ message.type || "v2",
2331
+ time instanceof Date ? time.toISOString() : time,
2332
+ message.resourceId
2333
+ ]
2334
+ };
2335
+ });
2336
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2337
+ batchStatements.push({
2338
+ sql: `UPDATE "${storage.TABLE_THREADS}" SET "updatedAt" = ? WHERE id = ?`,
2339
+ args: [now, threadId]
2340
+ });
2341
+ const BATCH_SIZE = 50;
2342
+ const messageStatements = batchStatements.slice(0, -1);
2343
+ const threadUpdateStatement = batchStatements[batchStatements.length - 1];
2344
+ for (let i = 0; i < messageStatements.length; i += BATCH_SIZE) {
2345
+ const batch = messageStatements.slice(i, i + BATCH_SIZE);
2346
+ if (batch.length > 0) {
2347
+ await this.#client.batch(batch, "write");
2348
+ }
2349
+ }
2350
+ if (threadUpdateStatement) {
2351
+ await this.#client.execute(threadUpdateStatement);
2352
+ }
2353
+ const list = new agent.MessageList().add(messages, "memory");
2354
+ return { messages: list.get.all.db() };
2355
+ } catch (error$1) {
2356
+ throw new error.MastraError(
2357
+ {
2358
+ id: storage.createStorageErrorId("LIBSQL", "SAVE_MESSAGES", "FAILED"),
2359
+ domain: error.ErrorDomain.STORAGE,
2360
+ category: error.ErrorCategory.THIRD_PARTY
2361
+ },
2362
+ error$1
2363
+ );
1813
2364
  }
1814
- return typeof v === "object" ? JSON.stringify(v) : v;
1815
- });
1816
- const placeholders = values.map(() => "?").join(", ");
1817
- return {
1818
- sql: `INSERT OR REPLACE INTO ${parsedTableName} (${columns.join(", ")}) VALUES (${placeholders})`,
1819
- args: values
1820
- };
1821
- }
1822
- function prepareUpdateStatement({
1823
- tableName,
1824
- updates,
1825
- keys
1826
- }) {
1827
- const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
1828
- const schema = storage.TABLE_SCHEMAS[tableName];
1829
- const updateColumns = Object.keys(updates).map((col) => utils.parseSqlIdentifier(col, "column name"));
1830
- const updateValues = Object.values(updates).map(transformToSqlValue);
1831
- const setClause = updateColumns.map((col) => `${col} = ?`).join(", ");
1832
- const whereClause = prepareWhereClause(keys, schema);
1833
- return {
1834
- sql: `UPDATE ${parsedTableName} SET ${setClause}${whereClause.sql}`,
1835
- args: [...updateValues, ...whereClause.args]
1836
- };
1837
- }
1838
- function transformToSqlValue(value) {
1839
- if (typeof value === "undefined" || value === null) {
1840
- return null;
1841
- }
1842
- if (value instanceof Date) {
1843
- return value.toISOString();
1844
2365
  }
1845
- return typeof value === "object" ? JSON.stringify(value) : value;
1846
- }
1847
- function prepareDeleteStatement({ tableName, keys }) {
1848
- const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
1849
- const whereClause = prepareWhereClause(keys, storage.TABLE_SCHEMAS[tableName]);
1850
- return {
1851
- sql: `DELETE FROM ${parsedTableName}${whereClause.sql}`,
1852
- args: whereClause.args
1853
- };
1854
- }
1855
- function prepareWhereClause(filters, schema) {
1856
- const conditions = [];
1857
- const args = [];
1858
- for (const [columnName, filterValue] of Object.entries(filters)) {
1859
- const column = schema[columnName];
1860
- if (!column) {
1861
- throw new Error(`Unknown column: ${columnName}`);
2366
+ async updateMessages({
2367
+ messages
2368
+ }) {
2369
+ if (messages.length === 0) {
2370
+ return [];
1862
2371
  }
1863
- const parsedColumn = utils.parseSqlIdentifier(columnName, "column name");
1864
- const result = buildCondition2(parsedColumn, filterValue);
1865
- conditions.push(result.condition);
1866
- args.push(...result.args);
1867
- }
1868
- return {
1869
- sql: conditions.length > 0 ? ` WHERE ${conditions.join(" AND ")}` : "",
1870
- args
1871
- };
1872
- }
1873
- function buildCondition2(columnName, filterValue) {
1874
- if (filterValue === null) {
1875
- return { condition: `${columnName} IS NULL`, args: [] };
1876
- }
1877
- if (typeof filterValue === "object" && filterValue !== null && ("startAt" in filterValue || "endAt" in filterValue)) {
1878
- return buildDateRangeCondition(columnName, filterValue);
1879
- }
1880
- return {
1881
- condition: `${columnName} = ?`,
1882
- args: [transformToSqlValue(filterValue)]
1883
- };
1884
- }
1885
- function buildDateRangeCondition(columnName, range) {
1886
- const conditions = [];
1887
- const args = [];
1888
- if (range.startAt !== void 0) {
1889
- conditions.push(`${columnName} >= ?`);
1890
- args.push(transformToSqlValue(range.startAt));
1891
- }
1892
- if (range.endAt !== void 0) {
1893
- conditions.push(`${columnName} <= ?`);
1894
- args.push(transformToSqlValue(range.endAt));
1895
- }
1896
- if (conditions.length === 0) {
1897
- throw new Error("Date range must specify at least startAt or endAt");
1898
- }
1899
- return {
1900
- condition: conditions.join(" AND "),
1901
- args
1902
- };
1903
- }
1904
- function buildDateRangeFilter(dateRange, columnName = "createdAt") {
1905
- if (!dateRange?.start && !dateRange?.end) {
1906
- return {};
1907
- }
1908
- const filter = {};
1909
- if (dateRange.start) {
1910
- filter.startAt = new Date(dateRange.start).toISOString();
1911
- }
1912
- if (dateRange.end) {
1913
- filter.endAt = new Date(dateRange.end).toISOString();
1914
- }
1915
- return { [columnName]: filter };
1916
- }
1917
- function transformFromSqlRow({
1918
- tableName,
1919
- sqlRow
1920
- }) {
1921
- const result = {};
1922
- const jsonColumns = new Set(
1923
- Object.keys(storage.TABLE_SCHEMAS[tableName]).filter((key) => storage.TABLE_SCHEMAS[tableName][key].type === "jsonb").map((key) => key)
1924
- );
1925
- const dateColumns = new Set(
1926
- Object.keys(storage.TABLE_SCHEMAS[tableName]).filter((key) => storage.TABLE_SCHEMAS[tableName][key].type === "timestamp").map((key) => key)
1927
- );
1928
- for (const [key, value] of Object.entries(sqlRow)) {
1929
- if (value === null || value === void 0) {
1930
- result[key] = value;
1931
- continue;
2372
+ const messageIds = messages.map((m) => m.id);
2373
+ const placeholders = messageIds.map(() => "?").join(",");
2374
+ const selectSql = `SELECT * FROM ${storage.TABLE_MESSAGES} WHERE id IN (${placeholders})`;
2375
+ const existingResult = await this.#client.execute({ sql: selectSql, args: messageIds });
2376
+ const existingMessages = existingResult.rows.map((row) => this.parseRow(row));
2377
+ if (existingMessages.length === 0) {
2378
+ return [];
1932
2379
  }
1933
- if (dateColumns.has(key) && typeof value === "string") {
1934
- result[key] = new Date(value);
1935
- continue;
2380
+ const batchStatements = [];
2381
+ const threadIdsToUpdate = /* @__PURE__ */ new Set();
2382
+ const columnMapping = {
2383
+ threadId: "thread_id"
2384
+ };
2385
+ for (const existingMessage of existingMessages) {
2386
+ const updatePayload = messages.find((m) => m.id === existingMessage.id);
2387
+ if (!updatePayload) continue;
2388
+ const { id, ...fieldsToUpdate } = updatePayload;
2389
+ if (Object.keys(fieldsToUpdate).length === 0) continue;
2390
+ threadIdsToUpdate.add(existingMessage.threadId);
2391
+ if (updatePayload.threadId && updatePayload.threadId !== existingMessage.threadId) {
2392
+ threadIdsToUpdate.add(updatePayload.threadId);
2393
+ }
2394
+ const setClauses = [];
2395
+ const args = [];
2396
+ const updatableFields = { ...fieldsToUpdate };
2397
+ if (updatableFields.content) {
2398
+ const newContent = {
2399
+ ...existingMessage.content,
2400
+ ...updatableFields.content,
2401
+ // Deep merge metadata if it exists on both
2402
+ ...existingMessage.content?.metadata && updatableFields.content.metadata ? {
2403
+ metadata: {
2404
+ ...existingMessage.content.metadata,
2405
+ ...updatableFields.content.metadata
2406
+ }
2407
+ } : {}
2408
+ };
2409
+ setClauses.push(`${utils.parseSqlIdentifier("content", "column name")} = ?`);
2410
+ args.push(JSON.stringify(newContent));
2411
+ delete updatableFields.content;
2412
+ }
2413
+ for (const key in updatableFields) {
2414
+ if (Object.prototype.hasOwnProperty.call(updatableFields, key)) {
2415
+ const dbKey = columnMapping[key] || key;
2416
+ setClauses.push(`${utils.parseSqlIdentifier(dbKey, "column name")} = ?`);
2417
+ let value = updatableFields[key];
2418
+ if (typeof value === "object" && value !== null) {
2419
+ value = JSON.stringify(value);
2420
+ }
2421
+ args.push(value);
2422
+ }
2423
+ }
2424
+ if (setClauses.length === 0) continue;
2425
+ args.push(id);
2426
+ const sql = `UPDATE ${storage.TABLE_MESSAGES} SET ${setClauses.join(", ")} WHERE id = ?`;
2427
+ batchStatements.push({ sql, args });
1936
2428
  }
1937
- if (jsonColumns.has(key) && typeof value === "string") {
1938
- result[key] = storage.safelyParseJSON(value);
1939
- continue;
2429
+ if (batchStatements.length === 0) {
2430
+ return existingMessages;
1940
2431
  }
1941
- result[key] = value;
1942
- }
1943
- return result;
1944
- }
1945
-
1946
- // src/storage/domains/observability/index.ts
1947
- var ObservabilityLibSQL = class extends storage.ObservabilityStorage {
1948
- operations;
1949
- constructor({ operations }) {
1950
- super();
1951
- this.operations = operations;
2432
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2433
+ for (const threadId of threadIdsToUpdate) {
2434
+ if (threadId) {
2435
+ batchStatements.push({
2436
+ sql: `UPDATE ${storage.TABLE_THREADS} SET updatedAt = ? WHERE id = ?`,
2437
+ args: [now, threadId]
2438
+ });
2439
+ }
2440
+ }
2441
+ await this.#client.batch(batchStatements, "write");
2442
+ const updatedResult = await this.#client.execute({ sql: selectSql, args: messageIds });
2443
+ return updatedResult.rows.map((row) => this.parseRow(row));
1952
2444
  }
1953
- async createAISpan(span) {
2445
+ async deleteMessages(messageIds) {
2446
+ if (!messageIds || messageIds.length === 0) {
2447
+ return;
2448
+ }
1954
2449
  try {
1955
- const now = (/* @__PURE__ */ new Date()).toISOString();
1956
- const record = {
1957
- ...span,
1958
- createdAt: now,
1959
- updatedAt: now
1960
- };
1961
- return this.operations.insert({ tableName: storage.TABLE_AI_SPANS, record });
2450
+ const BATCH_SIZE = 100;
2451
+ const threadIds = /* @__PURE__ */ new Set();
2452
+ const tx = await this.#client.transaction("write");
2453
+ try {
2454
+ for (let i = 0; i < messageIds.length; i += BATCH_SIZE) {
2455
+ const batch = messageIds.slice(i, i + BATCH_SIZE);
2456
+ const placeholders = batch.map(() => "?").join(",");
2457
+ const result = await tx.execute({
2458
+ sql: `SELECT DISTINCT thread_id FROM "${storage.TABLE_MESSAGES}" WHERE id IN (${placeholders})`,
2459
+ args: batch
2460
+ });
2461
+ result.rows?.forEach((row) => {
2462
+ if (row.thread_id) threadIds.add(row.thread_id);
2463
+ });
2464
+ await tx.execute({
2465
+ sql: `DELETE FROM "${storage.TABLE_MESSAGES}" WHERE id IN (${placeholders})`,
2466
+ args: batch
2467
+ });
2468
+ }
2469
+ if (threadIds.size > 0) {
2470
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2471
+ for (const threadId of threadIds) {
2472
+ await tx.execute({
2473
+ sql: `UPDATE "${storage.TABLE_THREADS}" SET "updatedAt" = ? WHERE id = ?`,
2474
+ args: [now, threadId]
2475
+ });
2476
+ }
2477
+ }
2478
+ await tx.commit();
2479
+ } catch (error) {
2480
+ await tx.rollback();
2481
+ throw error;
2482
+ }
1962
2483
  } catch (error$1) {
1963
2484
  throw new error.MastraError(
1964
2485
  {
1965
- id: "LIBSQL_STORE_CREATE_AI_SPAN_FAILED",
2486
+ id: storage.createStorageErrorId("LIBSQL", "DELETE_MESSAGES", "FAILED"),
1966
2487
  domain: error.ErrorDomain.STORAGE,
1967
- category: error.ErrorCategory.USER,
1968
- details: {
1969
- spanId: span.spanId,
1970
- traceId: span.traceId,
1971
- spanType: span.spanType,
1972
- spanName: span.name
1973
- }
2488
+ category: error.ErrorCategory.THIRD_PARTY,
2489
+ details: { messageIds: messageIds.join(", ") }
1974
2490
  },
1975
2491
  error$1
1976
2492
  );
1977
2493
  }
1978
2494
  }
1979
- async getAITrace(traceId) {
2495
+ async getResourceById({ resourceId }) {
2496
+ const result = await this.#db.select({
2497
+ tableName: storage.TABLE_RESOURCES,
2498
+ keys: { id: resourceId }
2499
+ });
2500
+ if (!result) {
2501
+ return null;
2502
+ }
2503
+ return {
2504
+ ...result,
2505
+ // Ensure workingMemory is always returned as a string, even if auto-parsed as JSON
2506
+ workingMemory: result.workingMemory && typeof result.workingMemory === "object" ? JSON.stringify(result.workingMemory) : result.workingMemory,
2507
+ metadata: typeof result.metadata === "string" ? JSON.parse(result.metadata) : result.metadata,
2508
+ createdAt: new Date(result.createdAt),
2509
+ updatedAt: new Date(result.updatedAt)
2510
+ };
2511
+ }
2512
+ async saveResource({ resource }) {
2513
+ await this.#db.insert({
2514
+ tableName: storage.TABLE_RESOURCES,
2515
+ record: {
2516
+ ...resource,
2517
+ metadata: JSON.stringify(resource.metadata)
2518
+ }
2519
+ });
2520
+ return resource;
2521
+ }
2522
+ async updateResource({
2523
+ resourceId,
2524
+ workingMemory,
2525
+ metadata
2526
+ }) {
2527
+ const existingResource = await this.getResourceById({ resourceId });
2528
+ if (!existingResource) {
2529
+ const newResource = {
2530
+ id: resourceId,
2531
+ workingMemory,
2532
+ metadata: metadata || {},
2533
+ createdAt: /* @__PURE__ */ new Date(),
2534
+ updatedAt: /* @__PURE__ */ new Date()
2535
+ };
2536
+ return this.saveResource({ resource: newResource });
2537
+ }
2538
+ const updatedResource = {
2539
+ ...existingResource,
2540
+ workingMemory: workingMemory !== void 0 ? workingMemory : existingResource.workingMemory,
2541
+ metadata: {
2542
+ ...existingResource.metadata,
2543
+ ...metadata
2544
+ },
2545
+ updatedAt: /* @__PURE__ */ new Date()
2546
+ };
2547
+ const updates = [];
2548
+ const values = [];
2549
+ if (workingMemory !== void 0) {
2550
+ updates.push("workingMemory = ?");
2551
+ values.push(workingMemory);
2552
+ }
2553
+ if (metadata) {
2554
+ updates.push("metadata = ?");
2555
+ values.push(JSON.stringify(updatedResource.metadata));
2556
+ }
2557
+ updates.push("updatedAt = ?");
2558
+ values.push(updatedResource.updatedAt.toISOString());
2559
+ values.push(resourceId);
2560
+ await this.#client.execute({
2561
+ sql: `UPDATE ${storage.TABLE_RESOURCES} SET ${updates.join(", ")} WHERE id = ?`,
2562
+ args: values
2563
+ });
2564
+ return updatedResource;
2565
+ }
2566
+ async getThreadById({ threadId }) {
1980
2567
  try {
1981
- const spans = await this.operations.loadMany({
1982
- tableName: storage.TABLE_AI_SPANS,
1983
- whereClause: { sql: " WHERE traceId = ?", args: [traceId] },
1984
- orderBy: "startedAt DESC"
2568
+ const result = await this.#db.select({
2569
+ tableName: storage.TABLE_THREADS,
2570
+ keys: { id: threadId }
1985
2571
  });
1986
- if (!spans || spans.length === 0) {
2572
+ if (!result) {
1987
2573
  return null;
1988
2574
  }
1989
2575
  return {
1990
- traceId,
1991
- spans: spans.map((span) => transformFromSqlRow({ tableName: storage.TABLE_AI_SPANS, sqlRow: span }))
2576
+ ...result,
2577
+ metadata: typeof result.metadata === "string" ? JSON.parse(result.metadata) : result.metadata,
2578
+ createdAt: new Date(result.createdAt),
2579
+ updatedAt: new Date(result.updatedAt)
1992
2580
  };
1993
2581
  } catch (error$1) {
1994
2582
  throw new error.MastraError(
1995
2583
  {
1996
- id: "LIBSQL_STORE_GET_AI_TRACE_FAILED",
2584
+ id: storage.createStorageErrorId("LIBSQL", "GET_THREAD_BY_ID", "FAILED"),
1997
2585
  domain: error.ErrorDomain.STORAGE,
1998
- category: error.ErrorCategory.USER,
1999
- details: {
2000
- traceId
2001
- }
2586
+ category: error.ErrorCategory.THIRD_PARTY,
2587
+ details: { threadId }
2002
2588
  },
2003
2589
  error$1
2004
2590
  );
2005
2591
  }
2006
2592
  }
2007
- async updateAISpan({
2008
- spanId,
2009
- traceId,
2010
- updates
2011
- }) {
2012
- try {
2013
- await this.operations.update({
2014
- tableName: storage.TABLE_AI_SPANS,
2015
- keys: { spanId, traceId },
2016
- data: { ...updates, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }
2017
- });
2018
- } catch (error$1) {
2593
+ async listThreadsByResourceId(args) {
2594
+ const { resourceId, page = 0, perPage: perPageInput, orderBy } = args;
2595
+ if (page < 0) {
2019
2596
  throw new error.MastraError(
2020
2597
  {
2021
- id: "LIBSQL_STORE_UPDATE_AI_SPAN_FAILED",
2598
+ id: storage.createStorageErrorId("LIBSQL", "LIST_THREADS_BY_RESOURCE_ID", "INVALID_PAGE"),
2022
2599
  domain: error.ErrorDomain.STORAGE,
2023
2600
  category: error.ErrorCategory.USER,
2024
- details: {
2025
- spanId,
2026
- traceId
2027
- }
2601
+ details: { page }
2028
2602
  },
2029
- error$1
2603
+ new Error("page must be >= 0")
2030
2604
  );
2031
2605
  }
2032
- }
2033
- async getAITracesPaginated({
2034
- filters,
2035
- pagination
2036
- }) {
2037
- const page = pagination?.page ?? 0;
2038
- const perPage = pagination?.perPage ?? 10;
2039
- const { entityId, entityType, ...actualFilters } = filters || {};
2040
- const filtersWithDateRange = {
2041
- ...actualFilters,
2042
- ...buildDateRangeFilter(pagination?.dateRange, "startedAt"),
2043
- parentSpanId: null
2044
- };
2045
- const whereClause = prepareWhereClause(filtersWithDateRange, storage.AI_SPAN_SCHEMA);
2046
- let actualWhereClause = whereClause.sql || "";
2047
- if (entityId && entityType) {
2048
- const statement = `name = ?`;
2049
- let name = "";
2050
- if (entityType === "workflow") {
2051
- name = `workflow run: '${entityId}'`;
2052
- } else if (entityType === "agent") {
2053
- name = `agent run: '${entityId}'`;
2054
- } else {
2055
- const error$1 = new error.MastraError({
2056
- id: "LIBSQL_STORE_GET_AI_TRACES_PAGINATED_FAILED",
2057
- domain: error.ErrorDomain.STORAGE,
2058
- category: error.ErrorCategory.USER,
2059
- details: {
2060
- entityType
2061
- },
2062
- text: `Cannot filter by entity type: ${entityType}`
2063
- });
2064
- this.logger?.trackException(error$1);
2065
- throw error$1;
2066
- }
2067
- whereClause.args.push(name);
2068
- if (actualWhereClause) {
2069
- actualWhereClause += ` AND ${statement}`;
2070
- } else {
2071
- actualWhereClause += `WHERE ${statement}`;
2072
- }
2073
- }
2074
- const orderBy = "startedAt DESC";
2075
- let count = 0;
2606
+ const perPage = storage.normalizePerPage(perPageInput, 100);
2607
+ const { offset, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
2608
+ const { field, direction } = this.parseOrderBy(orderBy);
2076
2609
  try {
2077
- count = await this.operations.loadTotalCount({
2078
- tableName: storage.TABLE_AI_SPANS,
2079
- whereClause: { sql: actualWhereClause, args: whereClause.args }
2610
+ const baseQuery = `FROM ${storage.TABLE_THREADS} WHERE resourceId = ?`;
2611
+ const queryParams = [resourceId];
2612
+ const mapRowToStorageThreadType = (row) => ({
2613
+ id: row.id,
2614
+ resourceId: row.resourceId,
2615
+ title: row.title,
2616
+ createdAt: new Date(row.createdAt),
2617
+ // Convert string to Date
2618
+ updatedAt: new Date(row.updatedAt),
2619
+ // Convert string to Date
2620
+ metadata: typeof row.metadata === "string" ? JSON.parse(row.metadata) : row.metadata
2080
2621
  });
2081
- } catch (error$1) {
2082
- throw new error.MastraError(
2083
- {
2084
- id: "LIBSQL_STORE_GET_AI_TRACES_PAGINATED_COUNT_FAILED",
2085
- domain: error.ErrorDomain.STORAGE,
2086
- category: error.ErrorCategory.USER
2087
- },
2088
- error$1
2089
- );
2090
- }
2091
- if (count === 0) {
2092
- return {
2093
- pagination: {
2622
+ const countResult = await this.#client.execute({
2623
+ sql: `SELECT COUNT(*) as count ${baseQuery}`,
2624
+ args: queryParams
2625
+ });
2626
+ const total = Number(countResult.rows?.[0]?.count ?? 0);
2627
+ if (total === 0) {
2628
+ return {
2629
+ threads: [],
2094
2630
  total: 0,
2095
2631
  page,
2096
- perPage,
2632
+ perPage: perPageForResponse,
2097
2633
  hasMore: false
2098
- },
2099
- spans: []
2100
- };
2101
- }
2102
- try {
2103
- const spans = await this.operations.loadMany({
2104
- tableName: storage.TABLE_AI_SPANS,
2105
- whereClause: {
2106
- sql: actualWhereClause,
2107
- args: whereClause.args
2108
- },
2109
- orderBy,
2110
- offset: page * perPage,
2111
- limit: perPage
2634
+ };
2635
+ }
2636
+ const limitValue = perPageInput === false ? total : perPage;
2637
+ const dataResult = await this.#client.execute({
2638
+ sql: `SELECT * ${baseQuery} ORDER BY "${field}" ${direction} LIMIT ? OFFSET ?`,
2639
+ args: [...queryParams, limitValue, offset]
2112
2640
  });
2641
+ const threads = (dataResult.rows || []).map(mapRowToStorageThreadType);
2113
2642
  return {
2114
- pagination: {
2115
- total: count,
2116
- page,
2117
- perPage,
2118
- hasMore: spans.length === perPage
2119
- },
2120
- spans: spans.map((span) => transformFromSqlRow({ tableName: storage.TABLE_AI_SPANS, sqlRow: span }))
2643
+ threads,
2644
+ total,
2645
+ page,
2646
+ perPage: perPageForResponse,
2647
+ hasMore: perPageInput === false ? false : offset + perPage < total
2121
2648
  };
2122
2649
  } catch (error$1) {
2123
- throw new error.MastraError(
2650
+ const mastraError = new error.MastraError(
2124
2651
  {
2125
- id: "LIBSQL_STORE_GET_AI_TRACES_PAGINATED_FAILED",
2652
+ id: storage.createStorageErrorId("LIBSQL", "LIST_THREADS_BY_RESOURCE_ID", "FAILED"),
2126
2653
  domain: error.ErrorDomain.STORAGE,
2127
- category: error.ErrorCategory.USER
2654
+ category: error.ErrorCategory.THIRD_PARTY,
2655
+ details: { resourceId }
2128
2656
  },
2129
2657
  error$1
2130
2658
  );
2659
+ this.logger?.trackException?.(mastraError);
2660
+ this.logger?.error?.(mastraError.toString());
2661
+ return {
2662
+ threads: [],
2663
+ total: 0,
2664
+ page,
2665
+ perPage: perPageForResponse,
2666
+ hasMore: false
2667
+ };
2131
2668
  }
2132
2669
  }
2133
- async batchCreateAISpans(args) {
2670
+ async saveThread({ thread }) {
2134
2671
  try {
2135
- const now = (/* @__PURE__ */ new Date()).toISOString();
2136
- return this.operations.batchInsert({
2137
- tableName: storage.TABLE_AI_SPANS,
2138
- records: args.records.map((record) => ({
2139
- ...record,
2140
- createdAt: now,
2141
- updatedAt: now
2142
- }))
2672
+ await this.#db.insert({
2673
+ tableName: storage.TABLE_THREADS,
2674
+ record: {
2675
+ ...thread,
2676
+ metadata: JSON.stringify(thread.metadata)
2677
+ }
2143
2678
  });
2679
+ return thread;
2144
2680
  } catch (error$1) {
2145
- throw new error.MastraError(
2681
+ const mastraError = new error.MastraError(
2146
2682
  {
2147
- id: "LIBSQL_STORE_BATCH_CREATE_AI_SPANS_FAILED",
2683
+ id: storage.createStorageErrorId("LIBSQL", "SAVE_THREAD", "FAILED"),
2148
2684
  domain: error.ErrorDomain.STORAGE,
2149
- category: error.ErrorCategory.USER
2685
+ category: error.ErrorCategory.THIRD_PARTY,
2686
+ details: { threadId: thread.id }
2150
2687
  },
2151
2688
  error$1
2152
2689
  );
2690
+ this.logger?.trackException?.(mastraError);
2691
+ this.logger?.error?.(mastraError.toString());
2692
+ throw mastraError;
2153
2693
  }
2154
2694
  }
2155
- async batchUpdateAISpans(args) {
2695
+ async updateThread({
2696
+ id,
2697
+ title,
2698
+ metadata
2699
+ }) {
2700
+ const thread = await this.getThreadById({ threadId: id });
2701
+ if (!thread) {
2702
+ throw new error.MastraError({
2703
+ id: storage.createStorageErrorId("LIBSQL", "UPDATE_THREAD", "NOT_FOUND"),
2704
+ domain: error.ErrorDomain.STORAGE,
2705
+ category: error.ErrorCategory.USER,
2706
+ text: `Thread ${id} not found`,
2707
+ details: {
2708
+ status: 404,
2709
+ threadId: id
2710
+ }
2711
+ });
2712
+ }
2713
+ const updatedThread = {
2714
+ ...thread,
2715
+ title,
2716
+ metadata: {
2717
+ ...thread.metadata,
2718
+ ...metadata
2719
+ }
2720
+ };
2156
2721
  try {
2157
- return this.operations.batchUpdate({
2158
- tableName: storage.TABLE_AI_SPANS,
2159
- updates: args.records.map((record) => ({
2160
- keys: { spanId: record.spanId, traceId: record.traceId },
2161
- data: { ...record.updates, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }
2162
- }))
2722
+ await this.#client.execute({
2723
+ sql: `UPDATE ${storage.TABLE_THREADS} SET title = ?, metadata = ? WHERE id = ?`,
2724
+ args: [title, JSON.stringify(updatedThread.metadata), id]
2163
2725
  });
2726
+ return updatedThread;
2164
2727
  } catch (error$1) {
2165
2728
  throw new error.MastraError(
2166
2729
  {
2167
- id: "LIBSQL_STORE_BATCH_UPDATE_AI_SPANS_FAILED",
2730
+ id: storage.createStorageErrorId("LIBSQL", "UPDATE_THREAD", "FAILED"),
2168
2731
  domain: error.ErrorDomain.STORAGE,
2169
- category: error.ErrorCategory.USER
2732
+ category: error.ErrorCategory.THIRD_PARTY,
2733
+ text: `Failed to update thread ${id}`,
2734
+ details: { threadId: id }
2170
2735
  },
2171
2736
  error$1
2172
2737
  );
2173
2738
  }
2174
2739
  }
2175
- async batchDeleteAITraces(args) {
2740
+ async deleteThread({ threadId }) {
2176
2741
  try {
2177
- const keys = args.traceIds.map((traceId) => ({ traceId }));
2178
- return this.operations.batchDelete({
2179
- tableName: storage.TABLE_AI_SPANS,
2180
- keys
2742
+ await this.#client.execute({
2743
+ sql: `DELETE FROM ${storage.TABLE_MESSAGES} WHERE thread_id = ?`,
2744
+ args: [threadId]
2745
+ });
2746
+ await this.#client.execute({
2747
+ sql: `DELETE FROM ${storage.TABLE_THREADS} WHERE id = ?`,
2748
+ args: [threadId]
2181
2749
  });
2182
2750
  } catch (error$1) {
2183
2751
  throw new error.MastraError(
2184
2752
  {
2185
- id: "LIBSQL_STORE_BATCH_DELETE_AI_TRACES_FAILED",
2753
+ id: storage.createStorageErrorId("LIBSQL", "DELETE_THREAD", "FAILED"),
2186
2754
  domain: error.ErrorDomain.STORAGE,
2187
- category: error.ErrorCategory.USER
2755
+ category: error.ErrorCategory.THIRD_PARTY,
2756
+ details: { threadId }
2188
2757
  },
2189
2758
  error$1
2190
2759
  );
2191
2760
  }
2192
2761
  }
2193
2762
  };
2194
- var StoreOperationsLibSQL = class extends storage.StoreOperations {
2195
- client;
2196
- /**
2197
- * Maximum number of retries for write operations if an SQLITE_BUSY error occurs.
2198
- * @default 5
2199
- */
2200
- maxRetries;
2201
- /**
2202
- * Initial backoff time in milliseconds for retrying write operations on SQLITE_BUSY.
2203
- * The backoff time will double with each retry (exponential backoff).
2204
- * @default 100
2205
- */
2206
- initialBackoffMs;
2207
- constructor({
2208
- client,
2209
- maxRetries,
2210
- initialBackoffMs
2211
- }) {
2763
+ var ObservabilityLibSQL = class extends storage.ObservabilityStorage {
2764
+ #db;
2765
+ constructor(config) {
2212
2766
  super();
2213
- this.client = client;
2214
- this.maxRetries = maxRetries ?? 5;
2215
- this.initialBackoffMs = initialBackoffMs ?? 100;
2216
- }
2217
- async hasColumn(table, column) {
2218
- const result = await this.client.execute({
2219
- sql: `PRAGMA table_info(${table})`
2220
- });
2221
- return (await result.rows)?.some((row) => row.name === column);
2767
+ const client = resolveClient(config);
2768
+ this.#db = new LibSQLDB({ client, maxRetries: config.maxRetries, initialBackoffMs: config.initialBackoffMs });
2222
2769
  }
2223
- getCreateTableSQL(tableName, schema) {
2224
- const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
2225
- const columns = Object.entries(schema).map(([name, col]) => {
2226
- const parsedColumnName = utils.parseSqlIdentifier(name, "column name");
2227
- let type = col.type.toUpperCase();
2228
- if (type === "TEXT") type = "TEXT";
2229
- if (type === "TIMESTAMP") type = "TEXT";
2230
- const nullable = col.nullable ? "" : "NOT NULL";
2231
- const primaryKey = col.primaryKey ? "PRIMARY KEY" : "";
2232
- return `${parsedColumnName} ${type} ${nullable} ${primaryKey}`.trim();
2233
- });
2234
- if (tableName === storage.TABLE_WORKFLOW_SNAPSHOT) {
2235
- const stmnt = `CREATE TABLE IF NOT EXISTS ${parsedTableName} (
2236
- ${columns.join(",\n")},
2237
- PRIMARY KEY (workflow_name, run_id)
2238
- )`;
2239
- return stmnt;
2240
- }
2241
- if (tableName === storage.TABLE_AI_SPANS) {
2242
- const stmnt = `CREATE TABLE IF NOT EXISTS ${parsedTableName} (
2243
- ${columns.join(",\n")},
2244
- PRIMARY KEY (traceId, spanId)
2245
- )`;
2246
- return stmnt;
2247
- }
2248
- return `CREATE TABLE IF NOT EXISTS ${parsedTableName} (${columns.join(", ")})`;
2770
+ async init() {
2771
+ await this.#db.createTable({ tableName: storage.TABLE_SPANS, schema: storage.SPAN_SCHEMA });
2249
2772
  }
2250
- async createTable({
2251
- tableName,
2252
- schema
2253
- }) {
2773
+ async dangerouslyClearAll() {
2774
+ await this.#db.deleteData({ tableName: storage.TABLE_SPANS });
2775
+ }
2776
+ async createSpan(span) {
2254
2777
  try {
2255
- this.logger.debug(`Creating database table`, { tableName, operation: "schema init" });
2256
- const sql = this.getCreateTableSQL(tableName, schema);
2257
- await this.client.execute(sql);
2778
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2779
+ const record = {
2780
+ ...span,
2781
+ createdAt: now,
2782
+ updatedAt: now
2783
+ };
2784
+ return this.#db.insert({ tableName: storage.TABLE_SPANS, record });
2258
2785
  } catch (error$1) {
2259
2786
  throw new error.MastraError(
2260
2787
  {
2261
- id: "LIBSQL_STORE_CREATE_TABLE_FAILED",
2788
+ id: storage.createStorageErrorId("LIBSQL", "CREATE_SPAN", "FAILED"),
2262
2789
  domain: error.ErrorDomain.STORAGE,
2263
- category: error.ErrorCategory.THIRD_PARTY,
2790
+ category: error.ErrorCategory.USER,
2264
2791
  details: {
2265
- tableName
2792
+ spanId: span.spanId,
2793
+ traceId: span.traceId,
2794
+ spanType: span.spanType,
2795
+ spanName: span.name
2266
2796
  }
2267
2797
  },
2268
2798
  error$1
2269
2799
  );
2270
2800
  }
2271
2801
  }
2272
- getSqlType(type) {
2273
- switch (type) {
2274
- case "bigint":
2275
- return "INTEGER";
2276
- // SQLite uses INTEGER for all integer sizes
2277
- case "jsonb":
2278
- return "TEXT";
2279
- // Store JSON as TEXT in SQLite
2280
- default:
2281
- return super.getSqlType(type);
2282
- }
2283
- }
2284
- async doInsert({
2285
- tableName,
2286
- record
2287
- }) {
2288
- await this.client.execute(
2289
- prepareStatement({
2290
- tableName,
2291
- record
2292
- })
2293
- );
2294
- }
2295
- insert(args) {
2296
- const executeWriteOperationWithRetry = createExecuteWriteOperationWithRetry({
2297
- logger: this.logger,
2298
- maxRetries: this.maxRetries,
2299
- initialBackoffMs: this.initialBackoffMs
2300
- });
2301
- return executeWriteOperationWithRetry(() => this.doInsert(args), `insert into table ${args.tableName}`);
2302
- }
2303
- async load({ tableName, keys }) {
2304
- const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
2305
- const parsedKeys = Object.keys(keys).map((key) => utils.parseSqlIdentifier(key, "column name"));
2306
- const conditions = parsedKeys.map((key) => `${key} = ?`).join(" AND ");
2307
- const values = Object.values(keys);
2308
- const result = await this.client.execute({
2309
- sql: `SELECT * FROM ${parsedTableName} WHERE ${conditions} ORDER BY createdAt DESC LIMIT 1`,
2310
- args: values
2311
- });
2312
- if (!result.rows || result.rows.length === 0) {
2313
- return null;
2314
- }
2315
- const row = result.rows[0];
2316
- const parsed = Object.fromEntries(
2317
- Object.entries(row || {}).map(([k, v]) => {
2318
- try {
2319
- return [k, typeof v === "string" ? v.startsWith("{") || v.startsWith("[") ? JSON.parse(v) : v : v];
2320
- } catch {
2321
- return [k, v];
2322
- }
2323
- })
2324
- );
2325
- return parsed;
2326
- }
2327
- async loadMany({
2328
- tableName,
2329
- whereClause,
2330
- orderBy,
2331
- offset,
2332
- limit,
2333
- args
2334
- }) {
2335
- const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
2336
- let statement = `SELECT * FROM ${parsedTableName}`;
2337
- if (whereClause?.sql) {
2338
- statement += `${whereClause.sql}`;
2339
- }
2340
- if (orderBy) {
2341
- statement += ` ORDER BY ${orderBy}`;
2342
- }
2343
- if (limit) {
2344
- statement += ` LIMIT ${limit}`;
2345
- }
2346
- if (offset) {
2347
- statement += ` OFFSET ${offset}`;
2348
- }
2349
- const result = await this.client.execute({
2350
- sql: statement,
2351
- args: [...whereClause?.args ?? [], ...args ?? []]
2352
- });
2353
- return result.rows;
2354
- }
2355
- async loadTotalCount({
2356
- tableName,
2357
- whereClause
2358
- }) {
2359
- const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
2360
- const statement = `SELECT COUNT(*) as count FROM ${parsedTableName} ${whereClause ? `${whereClause.sql}` : ""}`;
2361
- const result = await this.client.execute({
2362
- sql: statement,
2363
- args: whereClause?.args ?? []
2364
- });
2365
- if (!result.rows || result.rows.length === 0) {
2366
- return 0;
2367
- }
2368
- return result.rows[0]?.count ?? 0;
2369
- }
2370
- update(args) {
2371
- const executeWriteOperationWithRetry = createExecuteWriteOperationWithRetry({
2372
- logger: this.logger,
2373
- maxRetries: this.maxRetries,
2374
- initialBackoffMs: this.initialBackoffMs
2375
- });
2376
- return executeWriteOperationWithRetry(() => this.executeUpdate(args), `update table ${args.tableName}`);
2377
- }
2378
- async executeUpdate({
2379
- tableName,
2380
- keys,
2381
- data
2382
- }) {
2383
- await this.client.execute(prepareUpdateStatement({ tableName, updates: data, keys }));
2384
- }
2385
- async doBatchInsert({
2386
- tableName,
2387
- records
2388
- }) {
2389
- if (records.length === 0) return;
2390
- const batchStatements = records.map((r) => prepareStatement({ tableName, record: r }));
2391
- await this.client.batch(batchStatements, "write");
2392
- }
2393
- batchInsert(args) {
2394
- const executeWriteOperationWithRetry = createExecuteWriteOperationWithRetry({
2395
- logger: this.logger,
2396
- maxRetries: this.maxRetries,
2397
- initialBackoffMs: this.initialBackoffMs
2398
- });
2399
- return executeWriteOperationWithRetry(
2400
- () => this.doBatchInsert(args),
2401
- `batch insert into table ${args.tableName}`
2402
- ).catch((error$1) => {
2802
+ async getTrace(traceId) {
2803
+ try {
2804
+ const spans = await this.#db.selectMany({
2805
+ tableName: storage.TABLE_SPANS,
2806
+ whereClause: { sql: " WHERE traceId = ?", args: [traceId] },
2807
+ orderBy: "startedAt DESC"
2808
+ });
2809
+ if (!spans || spans.length === 0) {
2810
+ return null;
2811
+ }
2812
+ return {
2813
+ traceId,
2814
+ spans: spans.map((span) => transformFromSqlRow({ tableName: storage.TABLE_SPANS, sqlRow: span }))
2815
+ };
2816
+ } catch (error$1) {
2403
2817
  throw new error.MastraError(
2404
2818
  {
2405
- id: "LIBSQL_STORE_BATCH_INSERT_FAILED",
2819
+ id: storage.createStorageErrorId("LIBSQL", "GET_TRACE", "FAILED"),
2406
2820
  domain: error.ErrorDomain.STORAGE,
2407
- category: error.ErrorCategory.THIRD_PARTY,
2821
+ category: error.ErrorCategory.USER,
2408
2822
  details: {
2409
- tableName: args.tableName
2823
+ traceId
2410
2824
  }
2411
2825
  },
2412
2826
  error$1
2413
2827
  );
2414
- });
2828
+ }
2415
2829
  }
2416
- /**
2417
- * Public batch update method with retry logic
2418
- */
2419
- batchUpdate(args) {
2420
- const executeWriteOperationWithRetry = createExecuteWriteOperationWithRetry({
2421
- logger: this.logger,
2422
- maxRetries: this.maxRetries,
2423
- initialBackoffMs: this.initialBackoffMs
2424
- });
2425
- return executeWriteOperationWithRetry(
2426
- () => this.executeBatchUpdate(args),
2427
- `batch update in table ${args.tableName}`
2428
- ).catch((error$1) => {
2830
+ async updateSpan({
2831
+ spanId,
2832
+ traceId,
2833
+ updates
2834
+ }) {
2835
+ try {
2836
+ await this.#db.update({
2837
+ tableName: storage.TABLE_SPANS,
2838
+ keys: { spanId, traceId },
2839
+ data: { ...updates, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }
2840
+ });
2841
+ } catch (error$1) {
2429
2842
  throw new error.MastraError(
2430
2843
  {
2431
- id: "LIBSQL_STORE_BATCH_UPDATE_FAILED",
2844
+ id: storage.createStorageErrorId("LIBSQL", "UPDATE_SPAN", "FAILED"),
2432
2845
  domain: error.ErrorDomain.STORAGE,
2433
- category: error.ErrorCategory.THIRD_PARTY,
2846
+ category: error.ErrorCategory.USER,
2434
2847
  details: {
2435
- tableName: args.tableName
2848
+ spanId,
2849
+ traceId
2436
2850
  }
2437
2851
  },
2438
2852
  error$1
2439
2853
  );
2440
- });
2854
+ }
2441
2855
  }
2442
- /**
2443
- * Updates multiple records in batch. Each record can be updated based on single or composite keys.
2444
- */
2445
- async executeBatchUpdate({
2446
- tableName,
2447
- updates
2856
+ async getTracesPaginated({
2857
+ filters,
2858
+ pagination
2448
2859
  }) {
2449
- if (updates.length === 0) return;
2450
- const batchStatements = updates.map(
2451
- ({ keys, data }) => prepareUpdateStatement({
2452
- tableName,
2453
- updates: data,
2454
- keys
2455
- })
2456
- );
2457
- await this.client.batch(batchStatements, "write");
2458
- }
2459
- /**
2460
- * Public batch delete method with retry logic
2461
- */
2462
- batchDelete({ tableName, keys }) {
2463
- const executeWriteOperationWithRetry = createExecuteWriteOperationWithRetry({
2464
- logger: this.logger,
2465
- maxRetries: this.maxRetries,
2466
- initialBackoffMs: this.initialBackoffMs
2467
- });
2468
- return executeWriteOperationWithRetry(
2469
- () => this.executeBatchDelete({ tableName, keys }),
2470
- `batch delete from table ${tableName}`
2471
- ).catch((error$1) => {
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;
2889
+ }
2890
+ whereClause.args.push(name);
2891
+ if (actualWhereClause) {
2892
+ actualWhereClause += ` AND ${statement}`;
2893
+ } else {
2894
+ actualWhereClause += `WHERE ${statement}`;
2895
+ }
2896
+ }
2897
+ const orderBy = "startedAt DESC";
2898
+ let count = 0;
2899
+ try {
2900
+ count = await this.#db.selectTotalCount({
2901
+ tableName: storage.TABLE_SPANS,
2902
+ whereClause: { sql: actualWhereClause, args: whereClause.args }
2903
+ });
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 {
2926
+ const spans = await this.#db.selectMany({
2927
+ tableName: storage.TABLE_SPANS,
2928
+ whereClause: {
2929
+ sql: actualWhereClause,
2930
+ args: whereClause.args
2931
+ },
2932
+ orderBy,
2933
+ offset: page * perPage,
2934
+ limit: perPage
2935
+ });
2936
+ return {
2937
+ pagination: {
2938
+ total: count,
2939
+ page,
2940
+ perPage,
2941
+ hasMore: spans.length === perPage
2942
+ },
2943
+ spans: spans.map((span) => transformFromSqlRow({ tableName: storage.TABLE_SPANS, sqlRow: span }))
2944
+ };
2945
+ } catch (error$1) {
2472
2946
  throw new error.MastraError(
2473
2947
  {
2474
- id: "LIBSQL_STORE_BATCH_DELETE_FAILED",
2948
+ id: storage.createStorageErrorId("LIBSQL", "GET_TRACES_PAGINATED", "FAILED"),
2475
2949
  domain: error.ErrorDomain.STORAGE,
2476
- category: error.ErrorCategory.THIRD_PARTY,
2477
- details: {
2478
- tableName
2479
- }
2950
+ category: error.ErrorCategory.USER
2480
2951
  },
2481
2952
  error$1
2482
2953
  );
2483
- });
2484
- }
2485
- /**
2486
- * Deletes multiple records in batch. Each record can be deleted based on single or composite keys.
2487
- */
2488
- async executeBatchDelete({
2489
- tableName,
2490
- keys
2491
- }) {
2492
- if (keys.length === 0) return;
2493
- const batchStatements = keys.map(
2494
- (keyObj) => prepareDeleteStatement({
2495
- tableName,
2496
- keys: keyObj
2497
- })
2498
- );
2499
- await this.client.batch(batchStatements, "write");
2954
+ }
2500
2955
  }
2501
- /**
2502
- * Alters table schema to add columns if they don't exist
2503
- * @param tableName Name of the table
2504
- * @param schema Schema of the table
2505
- * @param ifNotExists Array of column names to add if they don't exist
2506
- */
2507
- async alterTable({
2508
- tableName,
2509
- schema,
2510
- ifNotExists
2511
- }) {
2512
- const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
2956
+ async batchCreateSpans(args) {
2513
2957
  try {
2514
- const pragmaQuery = `PRAGMA table_info(${parsedTableName})`;
2515
- const result = await this.client.execute(pragmaQuery);
2516
- const existingColumnNames = new Set(result.rows.map((row) => row.name.toLowerCase()));
2517
- for (const columnName of ifNotExists) {
2518
- if (!existingColumnNames.has(columnName.toLowerCase()) && schema[columnName]) {
2519
- const columnDef = schema[columnName];
2520
- const sqlType = this.getSqlType(columnDef.type);
2521
- const nullable = columnDef.nullable === false ? "NOT NULL" : "";
2522
- const defaultValue = columnDef.nullable === false ? this.getDefaultValue(columnDef.type) : "";
2523
- const alterSql = `ALTER TABLE ${parsedTableName} ADD COLUMN "${columnName}" ${sqlType} ${nullable} ${defaultValue}`.trim();
2524
- await this.client.execute(alterSql);
2525
- this.logger?.debug?.(`Added column ${columnName} to table ${parsedTableName}`);
2526
- }
2527
- }
2958
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2959
+ return this.#db.batchInsert({
2960
+ tableName: storage.TABLE_SPANS,
2961
+ records: args.records.map((record) => ({
2962
+ ...record,
2963
+ createdAt: now,
2964
+ updatedAt: now
2965
+ }))
2966
+ });
2528
2967
  } catch (error$1) {
2529
2968
  throw new error.MastraError(
2530
2969
  {
2531
- id: "LIBSQL_STORE_ALTER_TABLE_FAILED",
2970
+ id: storage.createStorageErrorId("LIBSQL", "BATCH_CREATE_SPANS", "FAILED"),
2532
2971
  domain: error.ErrorDomain.STORAGE,
2533
- category: error.ErrorCategory.THIRD_PARTY,
2534
- details: {
2535
- tableName
2536
- }
2972
+ category: error.ErrorCategory.USER
2537
2973
  },
2538
2974
  error$1
2539
2975
  );
2540
2976
  }
2541
2977
  }
2542
- async clearTable({ tableName }) {
2543
- const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
2978
+ async batchUpdateSpans(args) {
2544
2979
  try {
2545
- await this.client.execute(`DELETE FROM ${parsedTableName}`);
2546
- } catch (e) {
2547
- const mastraError = new error.MastraError(
2980
+ return this.#db.batchUpdate({
2981
+ 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
+ }))
2986
+ });
2987
+ } catch (error$1) {
2988
+ throw new error.MastraError(
2548
2989
  {
2549
- id: "LIBSQL_STORE_CLEAR_TABLE_FAILED",
2990
+ id: storage.createStorageErrorId("LIBSQL", "BATCH_UPDATE_SPANS", "FAILED"),
2550
2991
  domain: error.ErrorDomain.STORAGE,
2551
- category: error.ErrorCategory.THIRD_PARTY,
2552
- details: {
2553
- tableName
2554
- }
2992
+ category: error.ErrorCategory.USER
2555
2993
  },
2556
- e
2994
+ error$1
2557
2995
  );
2558
- this.logger?.trackException?.(mastraError);
2559
- this.logger?.error?.(mastraError.toString());
2560
2996
  }
2561
2997
  }
2562
- async dropTable({ tableName }) {
2563
- const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
2998
+ async batchDeleteTraces(args) {
2564
2999
  try {
2565
- await this.client.execute(`DROP TABLE IF EXISTS ${parsedTableName}`);
2566
- } catch (e) {
3000
+ const keys = args.traceIds.map((traceId) => ({ traceId }));
3001
+ return this.#db.batchDelete({
3002
+ tableName: storage.TABLE_SPANS,
3003
+ keys
3004
+ });
3005
+ } catch (error$1) {
2567
3006
  throw new error.MastraError(
2568
3007
  {
2569
- id: "LIBSQL_STORE_DROP_TABLE_FAILED",
3008
+ id: storage.createStorageErrorId("LIBSQL", "BATCH_DELETE_TRACES", "FAILED"),
2570
3009
  domain: error.ErrorDomain.STORAGE,
2571
- category: error.ErrorCategory.THIRD_PARTY,
2572
- details: {
2573
- tableName
2574
- }
3010
+ category: error.ErrorCategory.USER
2575
3011
  },
2576
- e
3012
+ error$1
2577
3013
  );
2578
3014
  }
2579
3015
  }
2580
3016
  };
2581
3017
  var ScoresLibSQL = class extends storage.ScoresStorage {
2582
- operations;
2583
- client;
2584
- constructor({ client, operations }) {
3018
+ #db;
3019
+ #client;
3020
+ constructor(config) {
2585
3021
  super();
2586
- this.operations = operations;
2587
- this.client = client;
3022
+ const client = resolveClient(config);
3023
+ this.#client = client;
3024
+ this.#db = new LibSQLDB({ client, maxRetries: config.maxRetries, initialBackoffMs: config.initialBackoffMs });
3025
+ }
3026
+ async init() {
3027
+ await this.#db.createTable({ tableName: storage.TABLE_SCORERS, schema: storage.SCORERS_SCHEMA });
3028
+ await this.#db.alterTable({
3029
+ tableName: storage.TABLE_SCORERS,
3030
+ schema: storage.SCORERS_SCHEMA,
3031
+ ifNotExists: ["spanId", "requestContext"]
3032
+ });
3033
+ }
3034
+ async dangerouslyClearAll() {
3035
+ await this.#db.deleteData({ tableName: storage.TABLE_SCORERS });
2588
3036
  }
2589
- async getScoresByRunId({
3037
+ async listScoresByRunId({
2590
3038
  runId,
2591
3039
  pagination
2592
3040
  }) {
2593
3041
  try {
2594
- const result = await this.client.execute({
3042
+ const { page, perPage: perPageInput } = pagination;
3043
+ const countResult = await this.#client.execute({
3044
+ sql: `SELECT COUNT(*) as count FROM ${storage.TABLE_SCORERS} WHERE runId = ?`,
3045
+ args: [runId]
3046
+ });
3047
+ const total = Number(countResult.rows?.[0]?.count ?? 0);
3048
+ if (total === 0) {
3049
+ return {
3050
+ pagination: {
3051
+ total: 0,
3052
+ page,
3053
+ perPage: perPageInput,
3054
+ hasMore: false
3055
+ },
3056
+ scores: []
3057
+ };
3058
+ }
3059
+ const perPage = storage.normalizePerPage(perPageInput, 100);
3060
+ const { offset: start, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
3061
+ const limitValue = perPageInput === false ? total : perPage;
3062
+ const end = perPageInput === false ? total : start + perPage;
3063
+ const result = await this.#client.execute({
2595
3064
  sql: `SELECT * FROM ${storage.TABLE_SCORERS} WHERE runId = ? ORDER BY createdAt DESC LIMIT ? OFFSET ?`,
2596
- args: [runId, pagination.perPage + 1, pagination.page * pagination.perPage]
3065
+ args: [runId, limitValue, start]
2597
3066
  });
3067
+ const scores = result.rows?.map((row) => this.transformScoreRow(row)) ?? [];
2598
3068
  return {
2599
- scores: result.rows?.slice(0, pagination.perPage).map((row) => this.transformScoreRow(row)) ?? [],
3069
+ scores,
2600
3070
  pagination: {
2601
- total: result.rows?.length ?? 0,
2602
- page: pagination.page,
2603
- perPage: pagination.perPage,
2604
- hasMore: result.rows?.length > pagination.perPage
3071
+ total,
3072
+ page,
3073
+ perPage: perPageForResponse,
3074
+ hasMore: end < total
2605
3075
  }
2606
3076
  };
2607
3077
  } catch (error$1) {
2608
3078
  throw new error.MastraError(
2609
3079
  {
2610
- id: "LIBSQL_STORE_GET_SCORES_BY_RUN_ID_FAILED",
3080
+ id: storage.createStorageErrorId("LIBSQL", "LIST_SCORES_BY_RUN_ID", "FAILED"),
2611
3081
  domain: error.ErrorDomain.STORAGE,
2612
3082
  category: error.ErrorCategory.THIRD_PARTY
2613
3083
  },
@@ -2615,7 +3085,7 @@ var ScoresLibSQL = class extends storage.ScoresStorage {
2615
3085
  );
2616
3086
  }
2617
3087
  }
2618
- async getScoresByScorerId({
3088
+ async listScoresByScorerId({
2619
3089
  scorerId,
2620
3090
  entityId,
2621
3091
  entityType,
@@ -2623,6 +3093,7 @@ var ScoresLibSQL = class extends storage.ScoresStorage {
2623
3093
  pagination
2624
3094
  }) {
2625
3095
  try {
3096
+ const { page, perPage: perPageInput } = pagination;
2626
3097
  const conditions = [];
2627
3098
  const queryParams = [];
2628
3099
  if (scorerId) {
@@ -2642,23 +3113,44 @@ var ScoresLibSQL = class extends storage.ScoresStorage {
2642
3113
  queryParams.push(source);
2643
3114
  }
2644
3115
  const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
2645
- const result = await this.client.execute({
3116
+ const countResult = await this.#client.execute({
3117
+ sql: `SELECT COUNT(*) as count FROM ${storage.TABLE_SCORERS} ${whereClause}`,
3118
+ args: queryParams
3119
+ });
3120
+ const total = Number(countResult.rows?.[0]?.count ?? 0);
3121
+ if (total === 0) {
3122
+ return {
3123
+ pagination: {
3124
+ total: 0,
3125
+ page,
3126
+ perPage: perPageInput,
3127
+ hasMore: false
3128
+ },
3129
+ scores: []
3130
+ };
3131
+ }
3132
+ const perPage = storage.normalizePerPage(perPageInput, 100);
3133
+ const { offset: start, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
3134
+ const limitValue = perPageInput === false ? total : perPage;
3135
+ const end = perPageInput === false ? total : start + perPage;
3136
+ const result = await this.#client.execute({
2646
3137
  sql: `SELECT * FROM ${storage.TABLE_SCORERS} ${whereClause} ORDER BY createdAt DESC LIMIT ? OFFSET ?`,
2647
- args: [...queryParams, pagination.perPage + 1, pagination.page * pagination.perPage]
3138
+ args: [...queryParams, limitValue, start]
2648
3139
  });
3140
+ const scores = result.rows?.map((row) => this.transformScoreRow(row)) ?? [];
2649
3141
  return {
2650
- scores: result.rows?.slice(0, pagination.perPage).map((row) => this.transformScoreRow(row)) ?? [],
3142
+ scores,
2651
3143
  pagination: {
2652
- total: result.rows?.length ?? 0,
2653
- page: pagination.page,
2654
- perPage: pagination.perPage,
2655
- hasMore: result.rows?.length > pagination.perPage
3144
+ total,
3145
+ page,
3146
+ perPage: perPageForResponse,
3147
+ hasMore: end < total
2656
3148
  }
2657
3149
  };
2658
3150
  } catch (error$1) {
2659
3151
  throw new error.MastraError(
2660
3152
  {
2661
- id: "LIBSQL_STORE_GET_SCORES_BY_SCORER_ID_FAILED",
3153
+ id: storage.createStorageErrorId("LIBSQL", "LIST_SCORES_BY_SCORER_ID", "FAILED"),
2662
3154
  domain: error.ErrorDomain.STORAGE,
2663
3155
  category: error.ErrorCategory.THIRD_PARTY
2664
3156
  },
@@ -2666,48 +3158,17 @@ var ScoresLibSQL = class extends storage.ScoresStorage {
2666
3158
  );
2667
3159
  }
2668
3160
  }
3161
+ /**
3162
+ * LibSQL-specific score row transformation.
3163
+ * Maps additionalLLMContext column to additionalContext field.
3164
+ */
2669
3165
  transformScoreRow(row) {
2670
- const scorerValue = storage.safelyParseJSON(row.scorer);
2671
- const inputValue = storage.safelyParseJSON(row.input ?? "{}");
2672
- const outputValue = storage.safelyParseJSON(row.output ?? "{}");
2673
- const additionalLLMContextValue = row.additionalLLMContext ? storage.safelyParseJSON(row.additionalLLMContext) : null;
2674
- const runtimeContextValue = row.runtimeContext ? storage.safelyParseJSON(row.runtimeContext) : null;
2675
- const metadataValue = row.metadata ? storage.safelyParseJSON(row.metadata) : null;
2676
- const entityValue = row.entity ? storage.safelyParseJSON(row.entity) : null;
2677
- const preprocessStepResultValue = row.preprocessStepResult ? storage.safelyParseJSON(row.preprocessStepResult) : null;
2678
- const analyzeStepResultValue = row.analyzeStepResult ? storage.safelyParseJSON(row.analyzeStepResult) : null;
2679
- return {
2680
- id: row.id,
2681
- traceId: row.traceId,
2682
- spanId: row.spanId,
2683
- runId: row.runId,
2684
- scorer: scorerValue,
2685
- score: row.score,
2686
- reason: row.reason,
2687
- preprocessStepResult: preprocessStepResultValue,
2688
- analyzeStepResult: analyzeStepResultValue,
2689
- analyzePrompt: row.analyzePrompt,
2690
- preprocessPrompt: row.preprocessPrompt,
2691
- generateScorePrompt: row.generateScorePrompt,
2692
- generateReasonPrompt: row.generateReasonPrompt,
2693
- metadata: metadataValue,
2694
- input: inputValue,
2695
- output: outputValue,
2696
- additionalContext: additionalLLMContextValue,
2697
- runtimeContext: runtimeContextValue,
2698
- entityType: row.entityType,
2699
- entity: entityValue,
2700
- entityId: row.entityId,
2701
- scorerId: row.scorerId,
2702
- source: row.source,
2703
- resourceId: row.resourceId,
2704
- threadId: row.threadId,
2705
- createdAt: row.createdAt,
2706
- updatedAt: row.updatedAt
2707
- };
3166
+ return storage.transformScoreRow(row, {
3167
+ fieldMappings: { additionalContext: "additionalLLMContext" }
3168
+ });
2708
3169
  }
2709
3170
  async getScoreById({ id }) {
2710
- const result = await this.client.execute({
3171
+ const result = await this.#client.execute({
2711
3172
  sql: `SELECT * FROM ${storage.TABLE_SCORERS} WHERE id = ?`,
2712
3173
  args: [id]
2713
3174
  });
@@ -2716,19 +3177,19 @@ var ScoresLibSQL = class extends storage.ScoresStorage {
2716
3177
  async saveScore(score) {
2717
3178
  let parsedScore;
2718
3179
  try {
2719
- parsedScore = scores.saveScorePayloadSchema.parse(score);
3180
+ parsedScore = evals.saveScorePayloadSchema.parse(score);
2720
3181
  } catch (error$1) {
2721
3182
  throw new error.MastraError(
2722
3183
  {
2723
- id: "LIBSQL_STORE_SAVE_SCORE_FAILED_INVALID_SCORE_PAYLOAD",
3184
+ id: storage.createStorageErrorId("LIBSQL", "SAVE_SCORE", "VALIDATION_FAILED"),
2724
3185
  domain: error.ErrorDomain.STORAGE,
2725
3186
  category: error.ErrorCategory.USER,
2726
3187
  details: {
2727
- scorer: score.scorer.name,
2728
- entityId: score.entityId,
2729
- entityType: score.entityType,
2730
- traceId: score.traceId || "",
2731
- spanId: score.spanId || ""
3188
+ scorer: score.scorer?.id ?? "unknown",
3189
+ entityId: score.entityId ?? "unknown",
3190
+ entityType: score.entityType ?? "unknown",
3191
+ traceId: score.traceId ?? "",
3192
+ spanId: score.spanId ?? ""
2732
3193
  }
2733
3194
  },
2734
3195
  error$1
@@ -2736,21 +3197,21 @@ var ScoresLibSQL = class extends storage.ScoresStorage {
2736
3197
  }
2737
3198
  try {
2738
3199
  const id = crypto.randomUUID();
2739
- await this.operations.insert({
3200
+ const now = /* @__PURE__ */ new Date();
3201
+ await this.#db.insert({
2740
3202
  tableName: storage.TABLE_SCORERS,
2741
3203
  record: {
3204
+ ...parsedScore,
2742
3205
  id,
2743
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
2744
- updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
2745
- ...parsedScore
3206
+ createdAt: now.toISOString(),
3207
+ updatedAt: now.toISOString()
2746
3208
  }
2747
3209
  });
2748
- const scoreFromDb = await this.getScoreById({ id });
2749
- return { score: scoreFromDb };
3210
+ return { score: { ...parsedScore, id, createdAt: now, updatedAt: now } };
2750
3211
  } catch (error$1) {
2751
3212
  throw new error.MastraError(
2752
3213
  {
2753
- id: "LIBSQL_STORE_SAVE_SCORE_FAILED",
3214
+ id: storage.createStorageErrorId("LIBSQL", "SAVE_SCORE", "FAILED"),
2754
3215
  domain: error.ErrorDomain.STORAGE,
2755
3216
  category: error.ErrorCategory.THIRD_PARTY
2756
3217
  },
@@ -2758,29 +3219,51 @@ var ScoresLibSQL = class extends storage.ScoresStorage {
2758
3219
  );
2759
3220
  }
2760
3221
  }
2761
- async getScoresByEntityId({
3222
+ async listScoresByEntityId({
2762
3223
  entityId,
2763
3224
  entityType,
2764
3225
  pagination
2765
3226
  }) {
2766
3227
  try {
2767
- const result = await this.client.execute({
3228
+ const { page, perPage: perPageInput } = pagination;
3229
+ const countResult = await this.#client.execute({
3230
+ sql: `SELECT COUNT(*) as count FROM ${storage.TABLE_SCORERS} WHERE entityId = ? AND entityType = ?`,
3231
+ args: [entityId, entityType]
3232
+ });
3233
+ const total = Number(countResult.rows?.[0]?.count ?? 0);
3234
+ if (total === 0) {
3235
+ return {
3236
+ pagination: {
3237
+ total: 0,
3238
+ page,
3239
+ perPage: perPageInput,
3240
+ hasMore: false
3241
+ },
3242
+ scores: []
3243
+ };
3244
+ }
3245
+ const perPage = storage.normalizePerPage(perPageInput, 100);
3246
+ const { offset: start, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
3247
+ const limitValue = perPageInput === false ? total : perPage;
3248
+ const end = perPageInput === false ? total : start + perPage;
3249
+ const result = await this.#client.execute({
2768
3250
  sql: `SELECT * FROM ${storage.TABLE_SCORERS} WHERE entityId = ? AND entityType = ? ORDER BY createdAt DESC LIMIT ? OFFSET ?`,
2769
- args: [entityId, entityType, pagination.perPage + 1, pagination.page * pagination.perPage]
3251
+ args: [entityId, entityType, limitValue, start]
2770
3252
  });
3253
+ const scores = result.rows?.map((row) => this.transformScoreRow(row)) ?? [];
2771
3254
  return {
2772
- scores: result.rows?.slice(0, pagination.perPage).map((row) => this.transformScoreRow(row)) ?? [],
3255
+ scores,
2773
3256
  pagination: {
2774
- total: result.rows?.length ?? 0,
2775
- page: pagination.page,
2776
- perPage: pagination.perPage,
2777
- hasMore: result.rows?.length > pagination.perPage
3257
+ total,
3258
+ page,
3259
+ perPage: perPageForResponse,
3260
+ hasMore: end < total
2778
3261
  }
2779
3262
  };
2780
3263
  } catch (error$1) {
2781
3264
  throw new error.MastraError(
2782
3265
  {
2783
- id: "LIBSQL_STORE_GET_SCORES_BY_ENTITY_ID_FAILED",
3266
+ id: storage.createStorageErrorId("LIBSQL", "LIST_SCORES_BY_ENTITY_ID", "FAILED"),
2784
3267
  domain: error.ErrorDomain.STORAGE,
2785
3268
  category: error.ErrorCategory.THIRD_PARTY
2786
3269
  },
@@ -2788,36 +3271,40 @@ var ScoresLibSQL = class extends storage.ScoresStorage {
2788
3271
  );
2789
3272
  }
2790
3273
  }
2791
- async getScoresBySpan({
3274
+ async listScoresBySpan({
2792
3275
  traceId,
2793
3276
  spanId,
2794
3277
  pagination
2795
3278
  }) {
2796
3279
  try {
2797
- const countSQLResult = await this.client.execute({
3280
+ const { page, perPage: perPageInput } = pagination;
3281
+ const perPage = storage.normalizePerPage(perPageInput, 100);
3282
+ const { offset: start, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
3283
+ const countSQLResult = await this.#client.execute({
2798
3284
  sql: `SELECT COUNT(*) as count FROM ${storage.TABLE_SCORERS} WHERE traceId = ? AND spanId = ?`,
2799
3285
  args: [traceId, spanId]
2800
3286
  });
2801
3287
  const total = Number(countSQLResult.rows?.[0]?.count ?? 0);
2802
- const result = await this.client.execute({
3288
+ const limitValue = perPageInput === false ? total : perPage;
3289
+ const end = perPageInput === false ? total : start + perPage;
3290
+ const result = await this.#client.execute({
2803
3291
  sql: `SELECT * FROM ${storage.TABLE_SCORERS} WHERE traceId = ? AND spanId = ? ORDER BY createdAt DESC LIMIT ? OFFSET ?`,
2804
- args: [traceId, spanId, pagination.perPage + 1, pagination.page * pagination.perPage]
3292
+ args: [traceId, spanId, limitValue, start]
2805
3293
  });
2806
- const hasMore = result.rows?.length > pagination.perPage;
2807
- const scores = result.rows?.slice(0, pagination.perPage).map((row) => this.transformScoreRow(row)) ?? [];
3294
+ const scores = result.rows?.map((row) => this.transformScoreRow(row)) ?? [];
2808
3295
  return {
2809
3296
  scores,
2810
3297
  pagination: {
2811
3298
  total,
2812
- page: pagination.page,
2813
- perPage: pagination.perPage,
2814
- hasMore
3299
+ page,
3300
+ perPage: perPageForResponse,
3301
+ hasMore: end < total
2815
3302
  }
2816
3303
  };
2817
3304
  } catch (error$1) {
2818
3305
  throw new error.MastraError(
2819
3306
  {
2820
- id: "LIBSQL_STORE_GET_SCORES_BY_SPAN_FAILED",
3307
+ id: storage.createStorageErrorId("LIBSQL", "LIST_SCORES_BY_SPAN", "FAILED"),
2821
3308
  domain: error.ErrorDomain.STORAGE,
2822
3309
  category: error.ErrorCategory.THIRD_PARTY
2823
3310
  },
@@ -2845,37 +3332,49 @@ function parseWorkflowRun(row) {
2845
3332
  };
2846
3333
  }
2847
3334
  var WorkflowsLibSQL = class extends storage.WorkflowsStorage {
2848
- operations;
2849
- client;
2850
- maxRetries;
2851
- initialBackoffMs;
2852
- constructor({
2853
- operations,
2854
- client,
2855
- maxRetries = 5,
2856
- initialBackoffMs = 500
2857
- }) {
3335
+ #db;
3336
+ #client;
3337
+ executeWithRetry;
3338
+ constructor(config) {
2858
3339
  super();
2859
- this.operations = operations;
2860
- this.client = client;
2861
- this.maxRetries = maxRetries;
2862
- this.initialBackoffMs = initialBackoffMs;
3340
+ const client = resolveClient(config);
3341
+ const maxRetries = config.maxRetries ?? 5;
3342
+ const initialBackoffMs = config.initialBackoffMs ?? 500;
3343
+ this.#client = client;
3344
+ this.#db = new LibSQLDB({ client, maxRetries, initialBackoffMs });
3345
+ this.executeWithRetry = createExecuteWriteOperationWithRetry({
3346
+ logger: this.logger,
3347
+ maxRetries,
3348
+ initialBackoffMs
3349
+ });
2863
3350
  this.setupPragmaSettings().catch(
2864
3351
  (err) => this.logger.warn("LibSQL Workflows: Failed to setup PRAGMA settings.", err)
2865
3352
  );
2866
3353
  }
3354
+ async init() {
3355
+ const schema = storage.TABLE_SCHEMAS[storage.TABLE_WORKFLOW_SNAPSHOT];
3356
+ await this.#db.createTable({ tableName: storage.TABLE_WORKFLOW_SNAPSHOT, schema });
3357
+ await this.#db.alterTable({
3358
+ tableName: storage.TABLE_WORKFLOW_SNAPSHOT,
3359
+ schema,
3360
+ ifNotExists: ["resourceId"]
3361
+ });
3362
+ }
3363
+ async dangerouslyClearAll() {
3364
+ await this.#db.deleteData({ tableName: storage.TABLE_WORKFLOW_SNAPSHOT });
3365
+ }
2867
3366
  async setupPragmaSettings() {
2868
3367
  try {
2869
- await this.client.execute("PRAGMA busy_timeout = 10000;");
3368
+ await this.#client.execute("PRAGMA busy_timeout = 10000;");
2870
3369
  this.logger.debug("LibSQL Workflows: PRAGMA busy_timeout=10000 set.");
2871
3370
  try {
2872
- await this.client.execute("PRAGMA journal_mode = WAL;");
3371
+ await this.#client.execute("PRAGMA journal_mode = WAL;");
2873
3372
  this.logger.debug("LibSQL Workflows: PRAGMA journal_mode=WAL set.");
2874
3373
  } catch {
2875
3374
  this.logger.debug("LibSQL Workflows: WAL mode not supported, using default journal mode.");
2876
3375
  }
2877
3376
  try {
2878
- await this.client.execute("PRAGMA synchronous = NORMAL;");
3377
+ await this.#client.execute("PRAGMA synchronous = NORMAL;");
2879
3378
  this.logger.debug("LibSQL Workflows: PRAGMA synchronous=NORMAL set.");
2880
3379
  } catch {
2881
3380
  this.logger.debug("LibSQL Workflows: Failed to set synchronous mode.");
@@ -2884,53 +3383,15 @@ var WorkflowsLibSQL = class extends storage.WorkflowsStorage {
2884
3383
  this.logger.warn("LibSQL Workflows: Failed to set PRAGMA settings.", err);
2885
3384
  }
2886
3385
  }
2887
- async executeWithRetry(operation) {
2888
- let attempts = 0;
2889
- let backoff = this.initialBackoffMs;
2890
- while (attempts < this.maxRetries) {
2891
- try {
2892
- return await operation();
2893
- } catch (error) {
2894
- this.logger.debug("LibSQL Workflows: Error caught in retry loop", {
2895
- errorType: error.constructor.name,
2896
- errorCode: error.code,
2897
- errorMessage: error.message,
2898
- attempts,
2899
- maxRetries: this.maxRetries
2900
- });
2901
- const isLockError = 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");
2902
- if (isLockError) {
2903
- attempts++;
2904
- if (attempts >= this.maxRetries) {
2905
- this.logger.error(
2906
- `LibSQL Workflows: Operation failed after ${this.maxRetries} attempts due to database lock: ${error.message}`,
2907
- { error, attempts, maxRetries: this.maxRetries }
2908
- );
2909
- throw error;
2910
- }
2911
- this.logger.warn(
2912
- `LibSQL Workflows: Attempt ${attempts} failed due to database lock. Retrying in ${backoff}ms...`,
2913
- { errorMessage: error.message, attempts, backoff, maxRetries: this.maxRetries }
2914
- );
2915
- await new Promise((resolve) => setTimeout(resolve, backoff));
2916
- backoff *= 2;
2917
- } else {
2918
- this.logger.error("LibSQL Workflows: Non-lock error occurred, not retrying", { error });
2919
- throw error;
2920
- }
2921
- }
2922
- }
2923
- throw new Error("LibSQL Workflows: Max retries reached, but no error was re-thrown from the loop.");
2924
- }
2925
3386
  async updateWorkflowResults({
2926
3387
  workflowName,
2927
3388
  runId,
2928
3389
  stepId,
2929
3390
  result,
2930
- runtimeContext
3391
+ requestContext
2931
3392
  }) {
2932
3393
  return this.executeWithRetry(async () => {
2933
- const tx = await this.client.transaction("write");
3394
+ const tx = await this.#client.transaction("write");
2934
3395
  try {
2935
3396
  const existingSnapshotResult = await tx.execute({
2936
3397
  sql: `SELECT snapshot FROM ${storage.TABLE_WORKFLOW_SNAPSHOT} WHERE workflow_name = ? AND run_id = ?`,
@@ -2943,20 +3404,21 @@ var WorkflowsLibSQL = class extends storage.WorkflowsStorage {
2943
3404
  activePaths: [],
2944
3405
  timestamp: Date.now(),
2945
3406
  suspendedPaths: {},
3407
+ activeStepsPath: {},
2946
3408
  resumeLabels: {},
2947
3409
  serializedStepGraph: [],
3410
+ status: "pending",
2948
3411
  value: {},
2949
3412
  waitingPaths: {},
2950
- status: "pending",
2951
3413
  runId,
2952
- runtimeContext: {}
3414
+ requestContext: {}
2953
3415
  };
2954
3416
  } else {
2955
3417
  const existingSnapshot = existingSnapshotResult.rows[0].snapshot;
2956
3418
  snapshot = typeof existingSnapshot === "string" ? JSON.parse(existingSnapshot) : existingSnapshot;
2957
3419
  }
2958
3420
  snapshot.context[stepId] = result;
2959
- snapshot.runtimeContext = { ...snapshot.runtimeContext, ...runtimeContext };
3421
+ snapshot.requestContext = { ...snapshot.requestContext, ...requestContext };
2960
3422
  await tx.execute({
2961
3423
  sql: `UPDATE ${storage.TABLE_WORKFLOW_SNAPSHOT} SET snapshot = ? WHERE workflow_name = ? AND run_id = ?`,
2962
3424
  args: [JSON.stringify(snapshot), workflowName, runId]
@@ -2969,7 +3431,7 @@ var WorkflowsLibSQL = class extends storage.WorkflowsStorage {
2969
3431
  }
2970
3432
  throw error;
2971
3433
  }
2972
- });
3434
+ }, "updateWorkflowResults");
2973
3435
  }
2974
3436
  async updateWorkflowState({
2975
3437
  workflowName,
@@ -2977,7 +3439,7 @@ var WorkflowsLibSQL = class extends storage.WorkflowsStorage {
2977
3439
  opts
2978
3440
  }) {
2979
3441
  return this.executeWithRetry(async () => {
2980
- const tx = await this.client.transaction("write");
3442
+ const tx = await this.#client.transaction("write");
2981
3443
  try {
2982
3444
  const existingSnapshotResult = await tx.execute({
2983
3445
  sql: `SELECT snapshot FROM ${storage.TABLE_WORKFLOW_SNAPSHOT} WHERE workflow_name = ? AND run_id = ?`,
@@ -3006,24 +3468,27 @@ var WorkflowsLibSQL = class extends storage.WorkflowsStorage {
3006
3468
  }
3007
3469
  throw error;
3008
3470
  }
3009
- });
3471
+ }, "updateWorkflowState");
3010
3472
  }
3011
3473
  async persistWorkflowSnapshot({
3012
3474
  workflowName,
3013
3475
  runId,
3014
3476
  resourceId,
3015
- snapshot
3477
+ snapshot,
3478
+ createdAt,
3479
+ updatedAt
3016
3480
  }) {
3481
+ const now = /* @__PURE__ */ new Date();
3017
3482
  const data = {
3018
3483
  workflow_name: workflowName,
3019
3484
  run_id: runId,
3020
3485
  resourceId,
3021
3486
  snapshot,
3022
- createdAt: /* @__PURE__ */ new Date(),
3023
- updatedAt: /* @__PURE__ */ new Date()
3487
+ createdAt: createdAt ?? now,
3488
+ updatedAt: updatedAt ?? now
3024
3489
  };
3025
3490
  this.logger.debug("Persisting workflow snapshot", { workflowName, runId, data });
3026
- await this.operations.insert({
3491
+ await this.#db.insert({
3027
3492
  tableName: storage.TABLE_WORKFLOW_SNAPSHOT,
3028
3493
  record: data
3029
3494
  });
@@ -3033,7 +3498,7 @@ var WorkflowsLibSQL = class extends storage.WorkflowsStorage {
3033
3498
  runId
3034
3499
  }) {
3035
3500
  this.logger.debug("Loading workflow snapshot", { workflowName, runId });
3036
- const d = await this.operations.load({
3501
+ const d = await this.#db.select({
3037
3502
  tableName: storage.TABLE_WORKFLOW_SNAPSHOT,
3038
3503
  keys: { workflow_name: workflowName, run_id: runId }
3039
3504
  });
@@ -3055,7 +3520,7 @@ var WorkflowsLibSQL = class extends storage.WorkflowsStorage {
3055
3520
  }
3056
3521
  const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
3057
3522
  try {
3058
- const result = await this.client.execute({
3523
+ const result = await this.#client.execute({
3059
3524
  sql: `SELECT * FROM ${storage.TABLE_WORKFLOW_SNAPSHOT} ${whereClause} ORDER BY createdAt DESC LIMIT 1`,
3060
3525
  args
3061
3526
  });
@@ -3066,7 +3531,7 @@ var WorkflowsLibSQL = class extends storage.WorkflowsStorage {
3066
3531
  } catch (error$1) {
3067
3532
  throw new error.MastraError(
3068
3533
  {
3069
- id: "LIBSQL_STORE_GET_WORKFLOW_RUN_BY_ID_FAILED",
3534
+ id: storage.createStorageErrorId("LIBSQL", "GET_WORKFLOW_RUN_BY_ID", "FAILED"),
3070
3535
  domain: error.ErrorDomain.STORAGE,
3071
3536
  category: error.ErrorCategory.THIRD_PARTY
3072
3537
  },
@@ -3074,13 +3539,34 @@ var WorkflowsLibSQL = class extends storage.WorkflowsStorage {
3074
3539
  );
3075
3540
  }
3076
3541
  }
3077
- async getWorkflowRuns({
3542
+ async deleteWorkflowRunById({ runId, workflowName }) {
3543
+ return this.executeWithRetry(async () => {
3544
+ try {
3545
+ await this.#client.execute({
3546
+ sql: `DELETE FROM ${storage.TABLE_WORKFLOW_SNAPSHOT} WHERE workflow_name = ? AND run_id = ?`,
3547
+ args: [workflowName, runId]
3548
+ });
3549
+ } catch (error$1) {
3550
+ throw new error.MastraError(
3551
+ {
3552
+ id: storage.createStorageErrorId("LIBSQL", "DELETE_WORKFLOW_RUN_BY_ID", "FAILED"),
3553
+ domain: error.ErrorDomain.STORAGE,
3554
+ category: error.ErrorCategory.THIRD_PARTY,
3555
+ details: { runId, workflowName }
3556
+ },
3557
+ error$1
3558
+ );
3559
+ }
3560
+ }, "deleteWorkflowRunById");
3561
+ }
3562
+ async listWorkflowRuns({
3078
3563
  workflowName,
3079
3564
  fromDate,
3080
3565
  toDate,
3081
- limit,
3082
- offset,
3083
- resourceId
3566
+ page,
3567
+ perPage,
3568
+ resourceId,
3569
+ status
3084
3570
  } = {}) {
3085
3571
  try {
3086
3572
  const conditions = [];
@@ -3089,6 +3575,10 @@ var WorkflowsLibSQL = class extends storage.WorkflowsStorage {
3089
3575
  conditions.push("workflow_name = ?");
3090
3576
  args.push(workflowName);
3091
3577
  }
3578
+ if (status) {
3579
+ conditions.push("json_extract(snapshot, '$.status') = ?");
3580
+ args.push(status);
3581
+ }
3092
3582
  if (fromDate) {
3093
3583
  conditions.push("createdAt >= ?");
3094
3584
  args.push(fromDate.toISOString());
@@ -3098,7 +3588,7 @@ var WorkflowsLibSQL = class extends storage.WorkflowsStorage {
3098
3588
  args.push(toDate.toISOString());
3099
3589
  }
3100
3590
  if (resourceId) {
3101
- const hasResourceId = await this.operations.hasColumn(storage.TABLE_WORKFLOW_SNAPSHOT, "resourceId");
3591
+ const hasResourceId = await this.#db.hasColumn(storage.TABLE_WORKFLOW_SNAPSHOT, "resourceId");
3102
3592
  if (hasResourceId) {
3103
3593
  conditions.push("resourceId = ?");
3104
3594
  args.push(resourceId);
@@ -3108,23 +3598,26 @@ var WorkflowsLibSQL = class extends storage.WorkflowsStorage {
3108
3598
  }
3109
3599
  const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
3110
3600
  let total = 0;
3111
- if (limit !== void 0 && offset !== void 0) {
3112
- const countResult = await this.client.execute({
3601
+ const usePagination = typeof perPage === "number" && typeof page === "number";
3602
+ if (usePagination) {
3603
+ const countResult = await this.#client.execute({
3113
3604
  sql: `SELECT COUNT(*) as count FROM ${storage.TABLE_WORKFLOW_SNAPSHOT} ${whereClause}`,
3114
3605
  args
3115
3606
  });
3116
3607
  total = Number(countResult.rows?.[0]?.count ?? 0);
3117
3608
  }
3118
- const result = await this.client.execute({
3119
- sql: `SELECT * FROM ${storage.TABLE_WORKFLOW_SNAPSHOT} ${whereClause} ORDER BY createdAt DESC${limit !== void 0 && offset !== void 0 ? ` LIMIT ? OFFSET ?` : ""}`,
3120
- args: limit !== void 0 && offset !== void 0 ? [...args, limit, offset] : args
3609
+ const normalizedPerPage = usePagination ? storage.normalizePerPage(perPage, Number.MAX_SAFE_INTEGER) : 0;
3610
+ const offset = usePagination ? page * normalizedPerPage : 0;
3611
+ const result = await this.#client.execute({
3612
+ sql: `SELECT * FROM ${storage.TABLE_WORKFLOW_SNAPSHOT} ${whereClause} ORDER BY createdAt DESC${usePagination ? ` LIMIT ? OFFSET ?` : ""}`,
3613
+ args: usePagination ? [...args, normalizedPerPage, offset] : args
3121
3614
  });
3122
3615
  const runs = (result.rows || []).map((row) => parseWorkflowRun(row));
3123
3616
  return { runs, total: total || runs.length };
3124
3617
  } catch (error$1) {
3125
3618
  throw new error.MastraError(
3126
3619
  {
3127
- id: "LIBSQL_STORE_GET_WORKFLOW_RUNS_FAILED",
3620
+ id: storage.createStorageErrorId("LIBSQL", "LIST_WORKFLOW_RUNS", "FAILED"),
3128
3621
  domain: error.ErrorDomain.STORAGE,
3129
3622
  category: error.ErrorCategory.THIRD_PARTY
3130
3623
  },
@@ -3141,7 +3634,10 @@ var LibSQLStore = class extends storage.MastraStorage {
3141
3634
  initialBackoffMs;
3142
3635
  stores;
3143
3636
  constructor(config) {
3144
- super({ name: `LibSQLStore` });
3637
+ if (!config.id || typeof config.id !== "string" || config.id.trim() === "") {
3638
+ throw new Error("LibSQLStore: id must be provided and cannot be empty.");
3639
+ }
3640
+ super({ id: config.id, name: `LibSQLStore`, disableInit: config.disableInit });
3145
3641
  this.maxRetries = config.maxRetries ?? 5;
3146
3642
  this.initialBackoffMs = config.initialBackoffMs ?? 100;
3147
3643
  if ("url" in config) {
@@ -3159,23 +3655,22 @@ var LibSQLStore = class extends storage.MastraStorage {
3159
3655
  } else {
3160
3656
  this.client = config.client;
3161
3657
  }
3162
- const operations = new StoreOperationsLibSQL({
3658
+ const domainConfig = {
3163
3659
  client: this.client,
3164
3660
  maxRetries: this.maxRetries,
3165
3661
  initialBackoffMs: this.initialBackoffMs
3166
- });
3167
- const scores = new ScoresLibSQL({ client: this.client, operations });
3168
- const workflows = new WorkflowsLibSQL({ client: this.client, operations });
3169
- const memory = new MemoryLibSQL({ client: this.client, operations });
3170
- const legacyEvals = new LegacyEvalsLibSQL({ client: this.client });
3171
- const observability = new ObservabilityLibSQL({ operations });
3662
+ };
3663
+ const scores = new ScoresLibSQL(domainConfig);
3664
+ const workflows = new WorkflowsLibSQL(domainConfig);
3665
+ const memory = new MemoryLibSQL(domainConfig);
3666
+ const observability = new ObservabilityLibSQL(domainConfig);
3667
+ const agents = new AgentsLibSQL(domainConfig);
3172
3668
  this.stores = {
3173
- operations,
3174
3669
  scores,
3175
3670
  workflows,
3176
3671
  memory,
3177
- legacyEvals,
3178
- observability
3672
+ observability,
3673
+ agents
3179
3674
  };
3180
3675
  }
3181
3676
  get supports() {
@@ -3185,56 +3680,14 @@ var LibSQLStore = class extends storage.MastraStorage {
3185
3680
  hasColumn: true,
3186
3681
  createTable: true,
3187
3682
  deleteMessages: true,
3188
- aiTracing: true,
3189
- getScoresBySpan: true
3683
+ observabilityInstance: true,
3684
+ listScoresBySpan: true,
3685
+ agents: true
3190
3686
  };
3191
3687
  }
3192
- async createTable({
3193
- tableName,
3194
- schema
3195
- }) {
3196
- await this.stores.operations.createTable({ tableName, schema });
3197
- }
3198
- /**
3199
- * Alters table schema to add columns if they don't exist
3200
- * @param tableName Name of the table
3201
- * @param schema Schema of the table
3202
- * @param ifNotExists Array of column names to add if they don't exist
3203
- */
3204
- async alterTable({
3205
- tableName,
3206
- schema,
3207
- ifNotExists
3208
- }) {
3209
- await this.stores.operations.alterTable({ tableName, schema, ifNotExists });
3210
- }
3211
- async clearTable({ tableName }) {
3212
- await this.stores.operations.clearTable({ tableName });
3213
- }
3214
- async dropTable({ tableName }) {
3215
- await this.stores.operations.dropTable({ tableName });
3216
- }
3217
- insert(args) {
3218
- return this.stores.operations.insert(args);
3219
- }
3220
- batchInsert(args) {
3221
- return this.stores.operations.batchInsert(args);
3222
- }
3223
- async load({ tableName, keys }) {
3224
- return this.stores.operations.load({ tableName, keys });
3225
- }
3226
3688
  async getThreadById({ threadId }) {
3227
3689
  return this.stores.memory.getThreadById({ threadId });
3228
3690
  }
3229
- /**
3230
- * @deprecated use getThreadsByResourceIdPaginated instead for paginated results.
3231
- */
3232
- async getThreadsByResourceId(args) {
3233
- return this.stores.memory.getThreadsByResourceId(args);
3234
- }
3235
- async getThreadsByResourceIdPaginated(args) {
3236
- return this.stores.memory.getThreadsByResourceIdPaginated(args);
3237
- }
3238
3691
  async saveThread({ thread }) {
3239
3692
  return this.stores.memory.saveThread({ thread });
3240
3693
  }
@@ -3248,24 +3701,12 @@ var LibSQLStore = class extends storage.MastraStorage {
3248
3701
  async deleteThread({ threadId }) {
3249
3702
  return this.stores.memory.deleteThread({ threadId });
3250
3703
  }
3251
- async getMessages({
3252
- threadId,
3253
- selectBy,
3254
- format
3255
- }) {
3256
- return this.stores.memory.getMessages({ threadId, selectBy, format });
3257
- }
3258
- async getMessagesById({
3259
- messageIds,
3260
- format
3261
- }) {
3262
- return this.stores.memory.getMessagesById({ messageIds, format });
3263
- }
3264
- async getMessagesPaginated(args) {
3265
- return this.stores.memory.getMessagesPaginated(args);
3704
+ async listMessagesById({ messageIds }) {
3705
+ return this.stores.memory.listMessagesById({ messageIds });
3266
3706
  }
3267
3707
  async saveMessages(args) {
3268
- return this.stores.memory.saveMessages(args);
3708
+ const result = await this.stores.memory.saveMessages({ messages: args.messages });
3709
+ return { messages: result.messages };
3269
3710
  }
3270
3711
  async updateMessages({
3271
3712
  messages
@@ -3275,40 +3716,33 @@ var LibSQLStore = class extends storage.MastraStorage {
3275
3716
  async deleteMessages(messageIds) {
3276
3717
  return this.stores.memory.deleteMessages(messageIds);
3277
3718
  }
3278
- /** @deprecated use getEvals instead */
3279
- async getEvalsByAgentName(agentName, type) {
3280
- return this.stores.legacyEvals.getEvalsByAgentName(agentName, type);
3281
- }
3282
- async getEvals(options = {}) {
3283
- return this.stores.legacyEvals.getEvals(options);
3284
- }
3285
3719
  async getScoreById({ id }) {
3286
3720
  return this.stores.scores.getScoreById({ id });
3287
3721
  }
3288
3722
  async saveScore(score) {
3289
3723
  return this.stores.scores.saveScore(score);
3290
3724
  }
3291
- async getScoresByScorerId({
3725
+ async listScoresByScorerId({
3292
3726
  scorerId,
3293
3727
  entityId,
3294
3728
  entityType,
3295
3729
  source,
3296
3730
  pagination
3297
3731
  }) {
3298
- return this.stores.scores.getScoresByScorerId({ scorerId, entityId, entityType, source, pagination });
3732
+ return this.stores.scores.listScoresByScorerId({ scorerId, entityId, entityType, source, pagination });
3299
3733
  }
3300
- async getScoresByRunId({
3734
+ async listScoresByRunId({
3301
3735
  runId,
3302
3736
  pagination
3303
3737
  }) {
3304
- return this.stores.scores.getScoresByRunId({ runId, pagination });
3738
+ return this.stores.scores.listScoresByRunId({ runId, pagination });
3305
3739
  }
3306
- async getScoresByEntityId({
3740
+ async listScoresByEntityId({
3307
3741
  entityId,
3308
3742
  entityType,
3309
3743
  pagination
3310
3744
  }) {
3311
- return this.stores.scores.getScoresByEntityId({ entityId, entityType, pagination });
3745
+ return this.stores.scores.listScoresByEntityId({ entityId, entityType, pagination });
3312
3746
  }
3313
3747
  /**
3314
3748
  * WORKFLOWS
@@ -3318,9 +3752,9 @@ var LibSQLStore = class extends storage.MastraStorage {
3318
3752
  runId,
3319
3753
  stepId,
3320
3754
  result,
3321
- runtimeContext
3755
+ requestContext
3322
3756
  }) {
3323
- return this.stores.workflows.updateWorkflowResults({ workflowName, runId, stepId, result, runtimeContext });
3757
+ return this.stores.workflows.updateWorkflowResults({ workflowName, runId, stepId, result, requestContext });
3324
3758
  }
3325
3759
  async updateWorkflowState({
3326
3760
  workflowName,
@@ -3343,15 +3777,8 @@ var LibSQLStore = class extends storage.MastraStorage {
3343
3777
  }) {
3344
3778
  return this.stores.workflows.loadWorkflowSnapshot({ workflowName, runId });
3345
3779
  }
3346
- async getWorkflowRuns({
3347
- workflowName,
3348
- fromDate,
3349
- toDate,
3350
- limit,
3351
- offset,
3352
- resourceId
3353
- } = {}) {
3354
- return this.stores.workflows.getWorkflowRuns({ workflowName, fromDate, toDate, limit, offset, resourceId });
3780
+ async listWorkflowRuns(args = {}) {
3781
+ return this.stores.workflows.listWorkflowRuns(args);
3355
3782
  }
3356
3783
  async getWorkflowRunById({
3357
3784
  runId,
@@ -3359,6 +3786,9 @@ var LibSQLStore = class extends storage.MastraStorage {
3359
3786
  }) {
3360
3787
  return this.stores.workflows.getWorkflowRunById({ runId, workflowName });
3361
3788
  }
3789
+ async deleteWorkflowRunById({ runId, workflowName }) {
3790
+ return this.stores.workflows.deleteWorkflowRunById({ runId, workflowName });
3791
+ }
3362
3792
  async getResourceById({ resourceId }) {
3363
3793
  return this.stores.memory.getResourceById({ resourceId });
3364
3794
  }
@@ -3372,30 +3802,30 @@ var LibSQLStore = class extends storage.MastraStorage {
3372
3802
  }) {
3373
3803
  return this.stores.memory.updateResource({ resourceId, workingMemory, metadata });
3374
3804
  }
3375
- async createAISpan(span) {
3376
- return this.stores.observability.createAISpan(span);
3805
+ async createSpan(span) {
3806
+ return this.stores.observability.createSpan(span);
3377
3807
  }
3378
- async updateAISpan(params) {
3379
- return this.stores.observability.updateAISpan(params);
3808
+ async updateSpan(params) {
3809
+ return this.stores.observability.updateSpan(params);
3380
3810
  }
3381
- async getAITrace(traceId) {
3382
- return this.stores.observability.getAITrace(traceId);
3811
+ async getTrace(traceId) {
3812
+ return this.stores.observability.getTrace(traceId);
3383
3813
  }
3384
- async getAITracesPaginated(args) {
3385
- return this.stores.observability.getAITracesPaginated(args);
3814
+ async getTracesPaginated(args) {
3815
+ return this.stores.observability.getTracesPaginated(args);
3386
3816
  }
3387
- async getScoresBySpan({
3817
+ async listScoresBySpan({
3388
3818
  traceId,
3389
3819
  spanId,
3390
3820
  pagination
3391
3821
  }) {
3392
- return this.stores.scores.getScoresBySpan({ traceId, spanId, pagination });
3822
+ return this.stores.scores.listScoresBySpan({ traceId, spanId, pagination });
3393
3823
  }
3394
- async batchCreateAISpans(args) {
3395
- return this.stores.observability.batchCreateAISpans(args);
3824
+ async batchCreateSpans(args) {
3825
+ return this.stores.observability.batchCreateSpans(args);
3396
3826
  }
3397
- async batchUpdateAISpans(args) {
3398
- return this.stores.observability.batchUpdateAISpans(args);
3827
+ async batchUpdateSpans(args) {
3828
+ return this.stores.observability.batchUpdateSpans(args);
3399
3829
  }
3400
3830
  };
3401
3831