@mastra/libsql 0.11.0-alpha.3 → 0.11.1-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -5,8 +5,9 @@ var error = require('@mastra/core/error');
5
5
  var utils = require('@mastra/core/utils');
6
6
  var vector = require('@mastra/core/vector');
7
7
  var filter = require('@mastra/core/vector/filter');
8
- var agent = require('@mastra/core/agent');
9
8
  var storage = require('@mastra/core/storage');
9
+ var core = require('@mastra/core');
10
+ var agent = require('@mastra/core/agent');
10
11
 
11
12
  // src/vector/index.ts
12
13
  var LibSQLFilterTranslator = class extends filter.BaseFilterTranslator {
@@ -906,394 +907,682 @@ var LibSQLVector = class extends vector.MastraVector {
906
907
  });
907
908
  }
908
909
  };
909
- function safelyParseJSON(jsonString) {
910
- try {
911
- return JSON.parse(jsonString);
912
- } catch {
913
- return {};
910
+ function transformEvalRow(row) {
911
+ const resultValue = JSON.parse(row.result);
912
+ const testInfoValue = row.test_info ? JSON.parse(row.test_info) : void 0;
913
+ if (!resultValue || typeof resultValue !== "object" || !("score" in resultValue)) {
914
+ throw new Error(`Invalid MetricResult format: ${JSON.stringify(resultValue)}`);
914
915
  }
916
+ return {
917
+ input: row.input,
918
+ output: row.output,
919
+ result: resultValue,
920
+ agentName: row.agent_name,
921
+ metricName: row.metric_name,
922
+ instructions: row.instructions,
923
+ testInfo: testInfoValue,
924
+ globalRunId: row.global_run_id,
925
+ runId: row.run_id,
926
+ createdAt: row.created_at
927
+ };
915
928
  }
916
- var LibSQLStore = class extends storage.MastraStorage {
929
+ var LegacyEvalsLibSQL = class extends storage.LegacyEvalsStorage {
917
930
  client;
918
- maxRetries;
919
- initialBackoffMs;
920
- constructor(config) {
921
- super({ name: `LibSQLStore` });
922
- this.maxRetries = config.maxRetries ?? 5;
923
- this.initialBackoffMs = config.initialBackoffMs ?? 100;
924
- if (config.url.endsWith(":memory:")) {
925
- this.shouldCacheInit = false;
926
- }
927
- this.client = client.createClient(config);
928
- if (config.url.startsWith("file:") || config.url.includes(":memory:")) {
929
- this.client.execute("PRAGMA journal_mode=WAL;").then(() => this.logger.debug("LibSQLStore: PRAGMA journal_mode=WAL set.")).catch((err) => this.logger.warn("LibSQLStore: Failed to set PRAGMA journal_mode=WAL.", err));
930
- this.client.execute("PRAGMA busy_timeout = 5000;").then(() => this.logger.debug("LibSQLStore: PRAGMA busy_timeout=5000 set.")).catch((err) => this.logger.warn("LibSQLStore: Failed to set PRAGMA busy_timeout.", err));
931
- }
932
- }
933
- get supports() {
934
- return {
935
- selectByIncludeResourceScope: true,
936
- resourceWorkingMemory: true
937
- };
938
- }
939
- getCreateTableSQL(tableName, schema) {
940
- const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
941
- const columns = Object.entries(schema).map(([name, col]) => {
942
- const parsedColumnName = utils.parseSqlIdentifier(name, "column name");
943
- let type = col.type.toUpperCase();
944
- if (type === "TEXT") type = "TEXT";
945
- if (type === "TIMESTAMP") type = "TEXT";
946
- const nullable = col.nullable ? "" : "NOT NULL";
947
- const primaryKey = col.primaryKey ? "PRIMARY KEY" : "";
948
- return `${parsedColumnName} ${type} ${nullable} ${primaryKey}`.trim();
949
- });
950
- if (tableName === storage.TABLE_WORKFLOW_SNAPSHOT) {
951
- const stmnt = `CREATE TABLE IF NOT EXISTS ${parsedTableName} (
952
- ${columns.join(",\n")},
953
- PRIMARY KEY (workflow_name, run_id)
954
- )`;
955
- return stmnt;
956
- }
957
- return `CREATE TABLE IF NOT EXISTS ${parsedTableName} (${columns.join(", ")})`;
931
+ constructor({ client }) {
932
+ super();
933
+ this.client = client;
958
934
  }
959
- async createTable({
960
- tableName,
961
- schema
962
- }) {
935
+ /** @deprecated use getEvals instead */
936
+ async getEvalsByAgentName(agentName, type) {
963
937
  try {
964
- this.logger.debug(`Creating database table`, { tableName, operation: "schema init" });
965
- const sql = this.getCreateTableSQL(tableName, schema);
966
- await this.client.execute(sql);
938
+ const baseQuery = `SELECT * FROM ${storage.TABLE_EVALS} WHERE agent_name = ?`;
939
+ 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)" : "";
940
+ const result = await this.client.execute({
941
+ sql: `${baseQuery}${typeCondition} ORDER BY created_at DESC`,
942
+ args: [agentName]
943
+ });
944
+ return result.rows?.map((row) => transformEvalRow(row)) ?? [];
967
945
  } catch (error$1) {
946
+ if (error$1 instanceof Error && error$1.message.includes("no such table")) {
947
+ return [];
948
+ }
968
949
  throw new error.MastraError(
969
950
  {
970
- id: "LIBSQL_STORE_CREATE_TABLE_FAILED",
951
+ id: "LIBSQL_STORE_GET_EVALS_BY_AGENT_NAME_FAILED",
971
952
  domain: error.ErrorDomain.STORAGE,
972
953
  category: error.ErrorCategory.THIRD_PARTY,
973
- details: {
974
- tableName
975
- }
954
+ details: { agentName }
976
955
  },
977
956
  error$1
978
957
  );
979
958
  }
980
959
  }
981
- getSqlType(type) {
982
- switch (type) {
983
- case "bigint":
984
- return "INTEGER";
985
- // SQLite uses INTEGER for all integer sizes
986
- case "jsonb":
987
- return "TEXT";
988
- // Store JSON as TEXT in SQLite
989
- default:
990
- return super.getSqlType(type);
960
+ async getEvals(options = {}) {
961
+ const { agentName, type, page = 0, perPage = 100, dateRange } = options;
962
+ const fromDate = dateRange?.start;
963
+ const toDate = dateRange?.end;
964
+ const conditions = [];
965
+ const queryParams = [];
966
+ if (agentName) {
967
+ conditions.push(`agent_name = ?`);
968
+ queryParams.push(agentName);
991
969
  }
992
- }
993
- /**
994
- * Alters table schema to add columns if they don't exist
995
- * @param tableName Name of the table
996
- * @param schema Schema of the table
997
- * @param ifNotExists Array of column names to add if they don't exist
998
- */
999
- async alterTable({
1000
- tableName,
1001
- schema,
1002
- ifNotExists
1003
- }) {
1004
- const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
970
+ if (type === "test") {
971
+ conditions.push(`(test_info IS NOT NULL AND json_extract(test_info, '$.testPath') IS NOT NULL)`);
972
+ } else if (type === "live") {
973
+ conditions.push(`(test_info IS NULL OR json_extract(test_info, '$.testPath') IS NULL)`);
974
+ }
975
+ if (fromDate) {
976
+ conditions.push(`created_at >= ?`);
977
+ queryParams.push(fromDate.toISOString());
978
+ }
979
+ if (toDate) {
980
+ conditions.push(`created_at <= ?`);
981
+ queryParams.push(toDate.toISOString());
982
+ }
983
+ const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
1005
984
  try {
1006
- const pragmaQuery = `PRAGMA table_info(${parsedTableName})`;
1007
- const result = await this.client.execute(pragmaQuery);
1008
- const existingColumnNames = new Set(result.rows.map((row) => row.name.toLowerCase()));
1009
- for (const columnName of ifNotExists) {
1010
- if (!existingColumnNames.has(columnName.toLowerCase()) && schema[columnName]) {
1011
- const columnDef = schema[columnName];
1012
- const sqlType = this.getSqlType(columnDef.type);
1013
- const nullable = columnDef.nullable === false ? "NOT NULL" : "";
1014
- const defaultValue = columnDef.nullable === false ? this.getDefaultValue(columnDef.type) : "";
1015
- const alterSql = `ALTER TABLE ${parsedTableName} ADD COLUMN "${columnName}" ${sqlType} ${nullable} ${defaultValue}`.trim();
1016
- await this.client.execute(alterSql);
1017
- this.logger?.debug?.(`Added column ${columnName} to table ${parsedTableName}`);
1018
- }
985
+ const countResult = await this.client.execute({
986
+ sql: `SELECT COUNT(*) as count FROM ${storage.TABLE_EVALS} ${whereClause}`,
987
+ args: queryParams
988
+ });
989
+ const total = Number(countResult.rows?.[0]?.count ?? 0);
990
+ const currentOffset = page * perPage;
991
+ const hasMore = currentOffset + perPage < total;
992
+ if (total === 0) {
993
+ return {
994
+ evals: [],
995
+ total: 0,
996
+ page,
997
+ perPage,
998
+ hasMore: false
999
+ };
1019
1000
  }
1001
+ const dataResult = await this.client.execute({
1002
+ sql: `SELECT * FROM ${storage.TABLE_EVALS} ${whereClause} ORDER BY created_at DESC LIMIT ? OFFSET ?`,
1003
+ args: [...queryParams, perPage, currentOffset]
1004
+ });
1005
+ return {
1006
+ evals: dataResult.rows?.map((row) => transformEvalRow(row)) ?? [],
1007
+ total,
1008
+ page,
1009
+ perPage,
1010
+ hasMore
1011
+ };
1020
1012
  } catch (error$1) {
1021
1013
  throw new error.MastraError(
1022
1014
  {
1023
- id: "LIBSQL_STORE_ALTER_TABLE_FAILED",
1015
+ id: "LIBSQL_STORE_GET_EVALS_FAILED",
1024
1016
  domain: error.ErrorDomain.STORAGE,
1025
- category: error.ErrorCategory.THIRD_PARTY,
1026
- details: {
1027
- tableName
1028
- }
1017
+ category: error.ErrorCategory.THIRD_PARTY
1029
1018
  },
1030
1019
  error$1
1031
1020
  );
1032
1021
  }
1033
1022
  }
1034
- async clearTable({ tableName }) {
1035
- const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
1023
+ };
1024
+ var MemoryLibSQL = class extends storage.MemoryStorage {
1025
+ client;
1026
+ operations;
1027
+ constructor({ client, operations }) {
1028
+ super();
1029
+ this.client = client;
1030
+ this.operations = operations;
1031
+ }
1032
+ parseRow(row) {
1033
+ let content = row.content;
1036
1034
  try {
1037
- await this.client.execute(`DELETE FROM ${parsedTableName}`);
1038
- } catch (e) {
1039
- const mastraError = new error.MastraError(
1035
+ content = JSON.parse(row.content);
1036
+ } catch {
1037
+ }
1038
+ const result = {
1039
+ id: row.id,
1040
+ content,
1041
+ role: row.role,
1042
+ createdAt: new Date(row.createdAt),
1043
+ threadId: row.thread_id,
1044
+ resourceId: row.resourceId
1045
+ };
1046
+ if (row.type && row.type !== `v2`) result.type = row.type;
1047
+ return result;
1048
+ }
1049
+ async _getIncludedMessages({
1050
+ threadId,
1051
+ selectBy
1052
+ }) {
1053
+ const include = selectBy?.include;
1054
+ if (!include) return null;
1055
+ const unionQueries = [];
1056
+ const params = [];
1057
+ for (const inc of include) {
1058
+ const { id, withPreviousMessages = 0, withNextMessages = 0 } = inc;
1059
+ const searchId = inc.threadId || threadId;
1060
+ unionQueries.push(
1061
+ `
1062
+ SELECT * FROM (
1063
+ WITH numbered_messages AS (
1064
+ SELECT
1065
+ id, content, role, type, "createdAt", thread_id, "resourceId",
1066
+ ROW_NUMBER() OVER (ORDER BY "createdAt" ASC) as row_num
1067
+ FROM "${storage.TABLE_MESSAGES}"
1068
+ WHERE thread_id = ?
1069
+ ),
1070
+ target_positions AS (
1071
+ SELECT row_num as target_pos
1072
+ FROM numbered_messages
1073
+ WHERE id = ?
1074
+ )
1075
+ SELECT DISTINCT m.*
1076
+ FROM numbered_messages m
1077
+ CROSS JOIN target_positions t
1078
+ WHERE m.row_num BETWEEN (t.target_pos - ?) AND (t.target_pos + ?)
1079
+ )
1080
+ `
1081
+ // Keep ASC for final sorting after fetching context
1082
+ );
1083
+ params.push(searchId, id, withPreviousMessages, withNextMessages);
1084
+ }
1085
+ const finalQuery = unionQueries.join(" UNION ALL ") + ' ORDER BY "createdAt" ASC';
1086
+ const includedResult = await this.client.execute({ sql: finalQuery, args: params });
1087
+ const includedRows = includedResult.rows?.map((row) => this.parseRow(row));
1088
+ const seen = /* @__PURE__ */ new Set();
1089
+ const dedupedRows = includedRows.filter((row) => {
1090
+ if (seen.has(row.id)) return false;
1091
+ seen.add(row.id);
1092
+ return true;
1093
+ });
1094
+ return dedupedRows;
1095
+ }
1096
+ async getMessages({
1097
+ threadId,
1098
+ selectBy,
1099
+ format
1100
+ }) {
1101
+ try {
1102
+ const messages = [];
1103
+ const limit = storage.resolveMessageLimit({ last: selectBy?.last, defaultLimit: 40 });
1104
+ if (selectBy?.include?.length) {
1105
+ const includeMessages = await this._getIncludedMessages({ threadId, selectBy });
1106
+ if (includeMessages) {
1107
+ messages.push(...includeMessages);
1108
+ }
1109
+ }
1110
+ const excludeIds = messages.map((m) => m.id);
1111
+ const remainingSql = `
1112
+ SELECT
1113
+ id,
1114
+ content,
1115
+ role,
1116
+ type,
1117
+ "createdAt",
1118
+ thread_id,
1119
+ "resourceId"
1120
+ FROM "${storage.TABLE_MESSAGES}"
1121
+ WHERE thread_id = ?
1122
+ ${excludeIds.length ? `AND id NOT IN (${excludeIds.map(() => "?").join(", ")})` : ""}
1123
+ ORDER BY "createdAt" DESC
1124
+ LIMIT ?
1125
+ `;
1126
+ const remainingArgs = [threadId, ...excludeIds.length ? excludeIds : [], limit];
1127
+ const remainingResult = await this.client.execute({ sql: remainingSql, args: remainingArgs });
1128
+ if (remainingResult.rows) {
1129
+ messages.push(...remainingResult.rows.map((row) => this.parseRow(row)));
1130
+ }
1131
+ messages.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
1132
+ const list = new agent.MessageList().add(messages, "memory");
1133
+ if (format === `v2`) return list.get.all.v2();
1134
+ return list.get.all.v1();
1135
+ } catch (error$1) {
1136
+ throw new error.MastraError(
1040
1137
  {
1041
- id: "LIBSQL_STORE_CLEAR_TABLE_FAILED",
1138
+ id: "LIBSQL_STORE_GET_MESSAGES_FAILED",
1042
1139
  domain: error.ErrorDomain.STORAGE,
1043
1140
  category: error.ErrorCategory.THIRD_PARTY,
1044
- details: {
1045
- tableName
1046
- }
1141
+ details: { threadId }
1047
1142
  },
1048
- e
1143
+ error$1
1049
1144
  );
1050
- this.logger?.trackException?.(mastraError);
1051
- this.logger?.error?.(mastraError.toString());
1052
1145
  }
1053
1146
  }
1054
- prepareStatement({ tableName, record }) {
1055
- const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
1056
- const columns = Object.keys(record).map((col) => utils.parseSqlIdentifier(col, "column name"));
1057
- const values = Object.values(record).map((v) => {
1058
- if (typeof v === `undefined`) {
1059
- return null;
1060
- }
1061
- if (v instanceof Date) {
1062
- return v.toISOString();
1063
- }
1064
- return typeof v === "object" ? JSON.stringify(v) : v;
1065
- });
1066
- const placeholders = values.map(() => "?").join(", ");
1067
- return {
1068
- sql: `INSERT OR REPLACE INTO ${parsedTableName} (${columns.join(", ")}) VALUES (${placeholders})`,
1069
- args: values
1070
- };
1071
- }
1072
- async executeWriteOperationWithRetry(operationFn, operationDescription) {
1073
- let retries = 0;
1074
- while (true) {
1147
+ async getMessagesPaginated(args) {
1148
+ const { threadId, format, selectBy } = args;
1149
+ const { page = 0, perPage: perPageInput, dateRange } = selectBy?.pagination || {};
1150
+ const perPage = perPageInput !== void 0 ? perPageInput : storage.resolveMessageLimit({ last: selectBy?.last, defaultLimit: 40 });
1151
+ const fromDate = dateRange?.start;
1152
+ const toDate = dateRange?.end;
1153
+ const messages = [];
1154
+ if (selectBy?.include?.length) {
1075
1155
  try {
1076
- return await operationFn();
1077
- } catch (error) {
1078
- if (error.message && (error.message.includes("SQLITE_BUSY") || error.message.includes("database is locked")) && retries < this.maxRetries) {
1079
- retries++;
1080
- const backoffTime = this.initialBackoffMs * Math.pow(2, retries - 1);
1081
- this.logger.warn(
1082
- `LibSQLStore: Encountered SQLITE_BUSY during ${operationDescription}. Retrying (${retries}/${this.maxRetries}) in ${backoffTime}ms...`
1083
- );
1084
- await new Promise((resolve) => setTimeout(resolve, backoffTime));
1085
- } else {
1086
- this.logger.error(`LibSQLStore: Error during ${operationDescription} after ${retries} retries: ${error}`);
1087
- throw error;
1156
+ const includeMessages = await this._getIncludedMessages({ threadId, selectBy });
1157
+ if (includeMessages) {
1158
+ messages.push(...includeMessages);
1088
1159
  }
1160
+ } catch (error$1) {
1161
+ throw new error.MastraError(
1162
+ {
1163
+ id: "LIBSQL_STORE_GET_MESSAGES_PAGINATED_GET_INCLUDE_MESSAGES_FAILED",
1164
+ domain: error.ErrorDomain.STORAGE,
1165
+ category: error.ErrorCategory.THIRD_PARTY,
1166
+ details: { threadId }
1167
+ },
1168
+ error$1
1169
+ );
1089
1170
  }
1090
1171
  }
1091
- }
1092
- insert(args) {
1093
- return this.executeWriteOperationWithRetry(() => this.doInsert(args), `insert into table ${args.tableName}`);
1094
- }
1095
- async doInsert({
1096
- tableName,
1097
- record
1098
- }) {
1099
- await this.client.execute(
1100
- this.prepareStatement({
1101
- tableName,
1102
- record
1103
- })
1104
- );
1105
- }
1106
- batchInsert(args) {
1107
- return this.executeWriteOperationWithRetry(
1108
- () => this.doBatchInsert(args),
1109
- `batch insert into table ${args.tableName}`
1110
- ).catch((error$1) => {
1111
- throw new error.MastraError(
1112
- {
1113
- id: "LIBSQL_STORE_BATCH_INSERT_FAILED",
1114
- domain: error.ErrorDomain.STORAGE,
1115
- category: error.ErrorCategory.THIRD_PARTY,
1116
- details: {
1117
- tableName: args.tableName
1118
- }
1119
- },
1120
- error$1
1121
- );
1122
- });
1123
- }
1124
- async doBatchInsert({
1125
- tableName,
1126
- records
1127
- }) {
1128
- if (records.length === 0) return;
1129
- const batchStatements = records.map((r) => this.prepareStatement({ tableName, record: r }));
1130
- await this.client.batch(batchStatements, "write");
1131
- }
1132
- async load({ tableName, keys }) {
1133
- const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
1134
- const parsedKeys = Object.keys(keys).map((key) => utils.parseSqlIdentifier(key, "column name"));
1135
- const conditions = parsedKeys.map((key) => `${key} = ?`).join(" AND ");
1136
- const values = Object.values(keys);
1137
- const result = await this.client.execute({
1138
- sql: `SELECT * FROM ${parsedTableName} WHERE ${conditions} ORDER BY createdAt DESC LIMIT 1`,
1139
- args: values
1140
- });
1141
- if (!result.rows || result.rows.length === 0) {
1142
- return null;
1143
- }
1144
- const row = result.rows[0];
1145
- const parsed = Object.fromEntries(
1146
- Object.entries(row || {}).map(([k, v]) => {
1147
- try {
1148
- return [k, typeof v === "string" ? v.startsWith("{") || v.startsWith("[") ? JSON.parse(v) : v : v];
1149
- } catch {
1150
- return [k, v];
1151
- }
1152
- })
1153
- );
1154
- return parsed;
1155
- }
1156
- async getThreadById({ threadId }) {
1157
1172
  try {
1158
- const result = await this.load({
1159
- tableName: storage.TABLE_THREADS,
1160
- keys: { id: threadId }
1161
- });
1162
- if (!result) {
1163
- return null;
1173
+ const currentOffset = page * perPage;
1174
+ const conditions = [`thread_id = ?`];
1175
+ const queryParams = [threadId];
1176
+ if (fromDate) {
1177
+ conditions.push(`"createdAt" >= ?`);
1178
+ queryParams.push(fromDate.toISOString());
1164
1179
  }
1165
- return {
1166
- ...result,
1167
- metadata: typeof result.metadata === "string" ? JSON.parse(result.metadata) : result.metadata
1168
- };
1169
- } catch (error$1) {
1170
- throw new error.MastraError(
1171
- {
1172
- id: "LIBSQL_STORE_GET_THREAD_BY_ID_FAILED",
1173
- domain: error.ErrorDomain.STORAGE,
1174
- category: error.ErrorCategory.THIRD_PARTY,
1175
- details: { threadId }
1176
- },
1177
- error$1
1178
- );
1179
- }
1180
- }
1181
- /**
1182
- * @deprecated use getThreadsByResourceIdPaginated instead for paginated results.
1183
- */
1184
- async getThreadsByResourceId(args) {
1185
- const { resourceId } = args;
1186
- try {
1187
- const baseQuery = `FROM ${storage.TABLE_THREADS} WHERE resourceId = ?`;
1188
- const queryParams = [resourceId];
1189
- const mapRowToStorageThreadType = (row) => ({
1190
- id: row.id,
1191
- resourceId: row.resourceId,
1192
- title: row.title,
1193
- createdAt: new Date(row.createdAt),
1194
- // Convert string to Date
1195
- updatedAt: new Date(row.updatedAt),
1196
- // Convert string to Date
1197
- metadata: typeof row.metadata === "string" ? JSON.parse(row.metadata) : row.metadata
1198
- });
1199
- const result = await this.client.execute({
1200
- sql: `SELECT * ${baseQuery} ORDER BY createdAt DESC`,
1201
- args: queryParams
1202
- });
1203
- if (!result.rows) {
1204
- return [];
1180
+ if (toDate) {
1181
+ conditions.push(`"createdAt" <= ?`);
1182
+ queryParams.push(toDate.toISOString());
1205
1183
  }
1206
- return result.rows.map(mapRowToStorageThreadType);
1207
- } catch (error$1) {
1208
- const mastraError = new error.MastraError(
1209
- {
1210
- id: "LIBSQL_STORE_GET_THREADS_BY_RESOURCE_ID_FAILED",
1211
- domain: error.ErrorDomain.STORAGE,
1212
- category: error.ErrorCategory.THIRD_PARTY,
1213
- details: { resourceId }
1214
- },
1215
- error$1
1216
- );
1217
- this.logger?.trackException?.(mastraError);
1218
- this.logger?.error?.(mastraError.toString());
1219
- return [];
1220
- }
1221
- }
1222
- async getThreadsByResourceIdPaginated(args) {
1223
- const { resourceId, page = 0, perPage = 100 } = args;
1224
- try {
1225
- const baseQuery = `FROM ${storage.TABLE_THREADS} WHERE resourceId = ?`;
1226
- const queryParams = [resourceId];
1227
- const mapRowToStorageThreadType = (row) => ({
1228
- id: row.id,
1229
- resourceId: row.resourceId,
1230
- title: row.title,
1231
- createdAt: new Date(row.createdAt),
1232
- // Convert string to Date
1233
- updatedAt: new Date(row.updatedAt),
1234
- // Convert string to Date
1235
- metadata: typeof row.metadata === "string" ? JSON.parse(row.metadata) : row.metadata
1236
- });
1237
- const currentOffset = page * perPage;
1184
+ const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
1238
1185
  const countResult = await this.client.execute({
1239
- sql: `SELECT COUNT(*) as count ${baseQuery}`,
1186
+ sql: `SELECT COUNT(*) as count FROM ${storage.TABLE_MESSAGES} ${whereClause}`,
1240
1187
  args: queryParams
1241
1188
  });
1242
1189
  const total = Number(countResult.rows?.[0]?.count ?? 0);
1243
- if (total === 0) {
1190
+ if (total === 0 && messages.length === 0) {
1244
1191
  return {
1245
- threads: [],
1192
+ messages: [],
1246
1193
  total: 0,
1247
1194
  page,
1248
1195
  perPage,
1249
1196
  hasMore: false
1250
1197
  };
1251
1198
  }
1199
+ const excludeIds = messages.map((m) => m.id);
1200
+ const excludeIdsParam = excludeIds.map((_, idx) => `$${idx + queryParams.length + 1}`).join(", ");
1252
1201
  const dataResult = await this.client.execute({
1253
- sql: `SELECT * ${baseQuery} ORDER BY createdAt DESC LIMIT ? OFFSET ?`,
1254
- args: [...queryParams, perPage, currentOffset]
1202
+ 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 ?`,
1203
+ args: [...queryParams, ...excludeIds, perPage, currentOffset]
1255
1204
  });
1256
- const threads = (dataResult.rows || []).map(mapRowToStorageThreadType);
1205
+ messages.push(...(dataResult.rows || []).map((row) => this.parseRow(row)));
1206
+ const messagesToReturn = format === "v1" ? new agent.MessageList().add(messages, "memory").get.all.v1() : new agent.MessageList().add(messages, "memory").get.all.v2();
1257
1207
  return {
1258
- threads,
1208
+ messages: messagesToReturn,
1259
1209
  total,
1260
1210
  page,
1261
1211
  perPage,
1262
- hasMore: currentOffset + threads.length < total
1212
+ hasMore: currentOffset + messages.length < total
1263
1213
  };
1264
1214
  } catch (error$1) {
1265
1215
  const mastraError = new error.MastraError(
1266
1216
  {
1267
- id: "LIBSQL_STORE_GET_THREADS_BY_RESOURCE_ID_FAILED",
1217
+ id: "LIBSQL_STORE_GET_MESSAGES_PAGINATED_FAILED",
1268
1218
  domain: error.ErrorDomain.STORAGE,
1269
1219
  category: error.ErrorCategory.THIRD_PARTY,
1270
- details: { resourceId }
1220
+ details: { threadId }
1271
1221
  },
1272
1222
  error$1
1273
1223
  );
1274
1224
  this.logger?.trackException?.(mastraError);
1275
1225
  this.logger?.error?.(mastraError.toString());
1276
- return { threads: [], total: 0, page, perPage, hasMore: false };
1226
+ return { messages: [], total: 0, page, perPage, hasMore: false };
1277
1227
  }
1278
1228
  }
1279
- async saveThread({ thread }) {
1229
+ async saveMessages({
1230
+ messages,
1231
+ format
1232
+ }) {
1233
+ if (messages.length === 0) return messages;
1280
1234
  try {
1281
- await this.insert({
1282
- tableName: storage.TABLE_THREADS,
1283
- record: {
1284
- ...thread,
1285
- metadata: JSON.stringify(thread.metadata)
1235
+ const threadId = messages[0]?.threadId;
1236
+ if (!threadId) {
1237
+ throw new Error("Thread ID is required");
1238
+ }
1239
+ const batchStatements = messages.map((message) => {
1240
+ const time = message.createdAt || /* @__PURE__ */ new Date();
1241
+ if (!message.threadId) {
1242
+ throw new Error(
1243
+ `Expected to find a threadId for message, but couldn't find one. An unexpected error has occurred.`
1244
+ );
1245
+ }
1246
+ if (!message.resourceId) {
1247
+ throw new Error(
1248
+ `Expected to find a resourceId for message, but couldn't find one. An unexpected error has occurred.`
1249
+ );
1286
1250
  }
1251
+ return {
1252
+ sql: `INSERT INTO ${storage.TABLE_MESSAGES} (id, thread_id, content, role, type, createdAt, resourceId)
1253
+ VALUES (?, ?, ?, ?, ?, ?, ?)
1254
+ ON CONFLICT(id) DO UPDATE SET
1255
+ thread_id=excluded.thread_id,
1256
+ content=excluded.content,
1257
+ role=excluded.role,
1258
+ type=excluded.type,
1259
+ resourceId=excluded.resourceId
1260
+ `,
1261
+ args: [
1262
+ message.id,
1263
+ message.threadId,
1264
+ typeof message.content === "object" ? JSON.stringify(message.content) : message.content,
1265
+ message.role,
1266
+ message.type || "v2",
1267
+ time instanceof Date ? time.toISOString() : time,
1268
+ message.resourceId
1269
+ ]
1270
+ };
1287
1271
  });
1288
- return thread;
1289
- } catch (error$1) {
1290
- const mastraError = new error.MastraError(
1291
- {
1292
- id: "LIBSQL_STORE_SAVE_THREAD_FAILED",
1293
- domain: error.ErrorDomain.STORAGE,
1294
- category: error.ErrorCategory.THIRD_PARTY,
1295
- details: { threadId: thread.id }
1296
- },
1272
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1273
+ batchStatements.push({
1274
+ sql: `UPDATE ${storage.TABLE_THREADS} SET updatedAt = ? WHERE id = ?`,
1275
+ args: [now, threadId]
1276
+ });
1277
+ await this.client.batch(batchStatements, "write");
1278
+ const list = new agent.MessageList().add(messages, "memory");
1279
+ if (format === `v2`) return list.get.all.v2();
1280
+ return list.get.all.v1();
1281
+ } catch (error$1) {
1282
+ throw new error.MastraError(
1283
+ {
1284
+ id: "LIBSQL_STORE_SAVE_MESSAGES_FAILED",
1285
+ domain: error.ErrorDomain.STORAGE,
1286
+ category: error.ErrorCategory.THIRD_PARTY
1287
+ },
1288
+ error$1
1289
+ );
1290
+ }
1291
+ }
1292
+ async updateMessages({
1293
+ messages
1294
+ }) {
1295
+ if (messages.length === 0) {
1296
+ return [];
1297
+ }
1298
+ const messageIds = messages.map((m) => m.id);
1299
+ const placeholders = messageIds.map(() => "?").join(",");
1300
+ const selectSql = `SELECT * FROM ${storage.TABLE_MESSAGES} WHERE id IN (${placeholders})`;
1301
+ const existingResult = await this.client.execute({ sql: selectSql, args: messageIds });
1302
+ const existingMessages = existingResult.rows.map((row) => this.parseRow(row));
1303
+ if (existingMessages.length === 0) {
1304
+ return [];
1305
+ }
1306
+ const batchStatements = [];
1307
+ const threadIdsToUpdate = /* @__PURE__ */ new Set();
1308
+ const columnMapping = {
1309
+ threadId: "thread_id"
1310
+ };
1311
+ for (const existingMessage of existingMessages) {
1312
+ const updatePayload = messages.find((m) => m.id === existingMessage.id);
1313
+ if (!updatePayload) continue;
1314
+ const { id, ...fieldsToUpdate } = updatePayload;
1315
+ if (Object.keys(fieldsToUpdate).length === 0) continue;
1316
+ threadIdsToUpdate.add(existingMessage.threadId);
1317
+ if (updatePayload.threadId && updatePayload.threadId !== existingMessage.threadId) {
1318
+ threadIdsToUpdate.add(updatePayload.threadId);
1319
+ }
1320
+ const setClauses = [];
1321
+ const args = [];
1322
+ const updatableFields = { ...fieldsToUpdate };
1323
+ if (updatableFields.content) {
1324
+ const newContent = {
1325
+ ...existingMessage.content,
1326
+ ...updatableFields.content,
1327
+ // Deep merge metadata if it exists on both
1328
+ ...existingMessage.content?.metadata && updatableFields.content.metadata ? {
1329
+ metadata: {
1330
+ ...existingMessage.content.metadata,
1331
+ ...updatableFields.content.metadata
1332
+ }
1333
+ } : {}
1334
+ };
1335
+ setClauses.push(`${core.parseSqlIdentifier("content", "column name")} = ?`);
1336
+ args.push(JSON.stringify(newContent));
1337
+ delete updatableFields.content;
1338
+ }
1339
+ for (const key in updatableFields) {
1340
+ if (Object.prototype.hasOwnProperty.call(updatableFields, key)) {
1341
+ const dbKey = columnMapping[key] || key;
1342
+ setClauses.push(`${core.parseSqlIdentifier(dbKey, "column name")} = ?`);
1343
+ let value = updatableFields[key];
1344
+ if (typeof value === "object" && value !== null) {
1345
+ value = JSON.stringify(value);
1346
+ }
1347
+ args.push(value);
1348
+ }
1349
+ }
1350
+ if (setClauses.length === 0) continue;
1351
+ args.push(id);
1352
+ const sql = `UPDATE ${storage.TABLE_MESSAGES} SET ${setClauses.join(", ")} WHERE id = ?`;
1353
+ batchStatements.push({ sql, args });
1354
+ }
1355
+ if (batchStatements.length === 0) {
1356
+ return existingMessages;
1357
+ }
1358
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1359
+ for (const threadId of threadIdsToUpdate) {
1360
+ if (threadId) {
1361
+ batchStatements.push({
1362
+ sql: `UPDATE ${storage.TABLE_THREADS} SET updatedAt = ? WHERE id = ?`,
1363
+ args: [now, threadId]
1364
+ });
1365
+ }
1366
+ }
1367
+ await this.client.batch(batchStatements, "write");
1368
+ const updatedResult = await this.client.execute({ sql: selectSql, args: messageIds });
1369
+ return updatedResult.rows.map((row) => this.parseRow(row));
1370
+ }
1371
+ async getResourceById({ resourceId }) {
1372
+ const result = await this.operations.load({
1373
+ tableName: storage.TABLE_RESOURCES,
1374
+ keys: { id: resourceId }
1375
+ });
1376
+ if (!result) {
1377
+ return null;
1378
+ }
1379
+ return {
1380
+ ...result,
1381
+ // Ensure workingMemory is always returned as a string, even if auto-parsed as JSON
1382
+ workingMemory: result.workingMemory && typeof result.workingMemory === "object" ? JSON.stringify(result.workingMemory) : result.workingMemory,
1383
+ metadata: typeof result.metadata === "string" ? JSON.parse(result.metadata) : result.metadata,
1384
+ createdAt: new Date(result.createdAt),
1385
+ updatedAt: new Date(result.updatedAt)
1386
+ };
1387
+ }
1388
+ async saveResource({ resource }) {
1389
+ console.log("resource", resource);
1390
+ await this.operations.insert({
1391
+ tableName: storage.TABLE_RESOURCES,
1392
+ record: {
1393
+ ...resource,
1394
+ metadata: JSON.stringify(resource.metadata)
1395
+ }
1396
+ });
1397
+ return resource;
1398
+ }
1399
+ async updateResource({
1400
+ resourceId,
1401
+ workingMemory,
1402
+ metadata
1403
+ }) {
1404
+ const existingResource = await this.getResourceById({ resourceId });
1405
+ if (!existingResource) {
1406
+ const newResource = {
1407
+ id: resourceId,
1408
+ workingMemory,
1409
+ metadata: metadata || {},
1410
+ createdAt: /* @__PURE__ */ new Date(),
1411
+ updatedAt: /* @__PURE__ */ new Date()
1412
+ };
1413
+ return this.saveResource({ resource: newResource });
1414
+ }
1415
+ const updatedResource = {
1416
+ ...existingResource,
1417
+ workingMemory: workingMemory !== void 0 ? workingMemory : existingResource.workingMemory,
1418
+ metadata: {
1419
+ ...existingResource.metadata,
1420
+ ...metadata
1421
+ },
1422
+ updatedAt: /* @__PURE__ */ new Date()
1423
+ };
1424
+ const updates = [];
1425
+ const values = [];
1426
+ if (workingMemory !== void 0) {
1427
+ updates.push("workingMemory = ?");
1428
+ values.push(workingMemory);
1429
+ }
1430
+ if (metadata) {
1431
+ updates.push("metadata = ?");
1432
+ values.push(JSON.stringify(updatedResource.metadata));
1433
+ }
1434
+ updates.push("updatedAt = ?");
1435
+ values.push(updatedResource.updatedAt.toISOString());
1436
+ values.push(resourceId);
1437
+ await this.client.execute({
1438
+ sql: `UPDATE ${storage.TABLE_RESOURCES} SET ${updates.join(", ")} WHERE id = ?`,
1439
+ args: values
1440
+ });
1441
+ return updatedResource;
1442
+ }
1443
+ async getThreadById({ threadId }) {
1444
+ try {
1445
+ const result = await this.operations.load({
1446
+ tableName: storage.TABLE_THREADS,
1447
+ keys: { id: threadId }
1448
+ });
1449
+ if (!result) {
1450
+ return null;
1451
+ }
1452
+ return {
1453
+ ...result,
1454
+ metadata: typeof result.metadata === "string" ? JSON.parse(result.metadata) : result.metadata,
1455
+ createdAt: new Date(result.createdAt),
1456
+ updatedAt: new Date(result.updatedAt)
1457
+ };
1458
+ } catch (error$1) {
1459
+ throw new error.MastraError(
1460
+ {
1461
+ id: "LIBSQL_STORE_GET_THREAD_BY_ID_FAILED",
1462
+ domain: error.ErrorDomain.STORAGE,
1463
+ category: error.ErrorCategory.THIRD_PARTY,
1464
+ details: { threadId }
1465
+ },
1466
+ error$1
1467
+ );
1468
+ }
1469
+ }
1470
+ /**
1471
+ * @deprecated use getThreadsByResourceIdPaginated instead for paginated results.
1472
+ */
1473
+ async getThreadsByResourceId(args) {
1474
+ const { resourceId } = args;
1475
+ try {
1476
+ const baseQuery = `FROM ${storage.TABLE_THREADS} WHERE resourceId = ?`;
1477
+ const queryParams = [resourceId];
1478
+ const mapRowToStorageThreadType = (row) => ({
1479
+ id: row.id,
1480
+ resourceId: row.resourceId,
1481
+ title: row.title,
1482
+ createdAt: new Date(row.createdAt),
1483
+ // Convert string to Date
1484
+ updatedAt: new Date(row.updatedAt),
1485
+ // Convert string to Date
1486
+ metadata: typeof row.metadata === "string" ? JSON.parse(row.metadata) : row.metadata
1487
+ });
1488
+ const result = await this.client.execute({
1489
+ sql: `SELECT * ${baseQuery} ORDER BY createdAt DESC`,
1490
+ args: queryParams
1491
+ });
1492
+ if (!result.rows) {
1493
+ return [];
1494
+ }
1495
+ return result.rows.map(mapRowToStorageThreadType);
1496
+ } catch (error$1) {
1497
+ const mastraError = new error.MastraError(
1498
+ {
1499
+ id: "LIBSQL_STORE_GET_THREADS_BY_RESOURCE_ID_FAILED",
1500
+ domain: error.ErrorDomain.STORAGE,
1501
+ category: error.ErrorCategory.THIRD_PARTY,
1502
+ details: { resourceId }
1503
+ },
1504
+ error$1
1505
+ );
1506
+ this.logger?.trackException?.(mastraError);
1507
+ this.logger?.error?.(mastraError.toString());
1508
+ return [];
1509
+ }
1510
+ }
1511
+ async getThreadsByResourceIdPaginated(args) {
1512
+ const { resourceId, page = 0, perPage = 100 } = args;
1513
+ try {
1514
+ const baseQuery = `FROM ${storage.TABLE_THREADS} WHERE resourceId = ?`;
1515
+ const queryParams = [resourceId];
1516
+ const mapRowToStorageThreadType = (row) => ({
1517
+ id: row.id,
1518
+ resourceId: row.resourceId,
1519
+ title: row.title,
1520
+ createdAt: new Date(row.createdAt),
1521
+ // Convert string to Date
1522
+ updatedAt: new Date(row.updatedAt),
1523
+ // Convert string to Date
1524
+ metadata: typeof row.metadata === "string" ? JSON.parse(row.metadata) : row.metadata
1525
+ });
1526
+ const currentOffset = page * perPage;
1527
+ const countResult = await this.client.execute({
1528
+ sql: `SELECT COUNT(*) as count ${baseQuery}`,
1529
+ args: queryParams
1530
+ });
1531
+ const total = Number(countResult.rows?.[0]?.count ?? 0);
1532
+ if (total === 0) {
1533
+ return {
1534
+ threads: [],
1535
+ total: 0,
1536
+ page,
1537
+ perPage,
1538
+ hasMore: false
1539
+ };
1540
+ }
1541
+ const dataResult = await this.client.execute({
1542
+ sql: `SELECT * ${baseQuery} ORDER BY createdAt DESC LIMIT ? OFFSET ?`,
1543
+ args: [...queryParams, perPage, currentOffset]
1544
+ });
1545
+ const threads = (dataResult.rows || []).map(mapRowToStorageThreadType);
1546
+ return {
1547
+ threads,
1548
+ total,
1549
+ page,
1550
+ perPage,
1551
+ hasMore: currentOffset + threads.length < total
1552
+ };
1553
+ } catch (error$1) {
1554
+ const mastraError = new error.MastraError(
1555
+ {
1556
+ id: "LIBSQL_STORE_GET_THREADS_BY_RESOURCE_ID_FAILED",
1557
+ domain: error.ErrorDomain.STORAGE,
1558
+ category: error.ErrorCategory.THIRD_PARTY,
1559
+ details: { resourceId }
1560
+ },
1561
+ error$1
1562
+ );
1563
+ this.logger?.trackException?.(mastraError);
1564
+ this.logger?.error?.(mastraError.toString());
1565
+ return { threads: [], total: 0, page, perPage, hasMore: false };
1566
+ }
1567
+ }
1568
+ async saveThread({ thread }) {
1569
+ try {
1570
+ await this.operations.insert({
1571
+ tableName: storage.TABLE_THREADS,
1572
+ record: {
1573
+ ...thread,
1574
+ metadata: JSON.stringify(thread.metadata)
1575
+ }
1576
+ });
1577
+ return thread;
1578
+ } catch (error$1) {
1579
+ const mastraError = new error.MastraError(
1580
+ {
1581
+ id: "LIBSQL_STORE_SAVE_THREAD_FAILED",
1582
+ domain: error.ErrorDomain.STORAGE,
1583
+ category: error.ErrorCategory.THIRD_PARTY,
1584
+ details: { threadId: thread.id }
1585
+ },
1297
1586
  error$1
1298
1587
  );
1299
1588
  this.logger?.trackException?.(mastraError);
@@ -1313,7 +1602,10 @@ var LibSQLStore = class extends storage.MastraStorage {
1313
1602
  domain: error.ErrorDomain.STORAGE,
1314
1603
  category: error.ErrorCategory.USER,
1315
1604
  text: `Thread ${id} not found`,
1316
- details: { threadId: id }
1605
+ details: {
1606
+ status: 404,
1607
+ threadId: id
1608
+ }
1317
1609
  });
1318
1610
  }
1319
1611
  const updatedThread = {
@@ -1343,281 +1635,342 @@ var LibSQLStore = class extends storage.MastraStorage {
1343
1635
  );
1344
1636
  }
1345
1637
  }
1346
- async deleteThread({ threadId }) {
1347
- try {
1348
- await this.client.execute({
1349
- sql: `DELETE FROM ${storage.TABLE_MESSAGES} WHERE thread_id = ?`,
1350
- args: [threadId]
1351
- });
1352
- await this.client.execute({
1353
- sql: `DELETE FROM ${storage.TABLE_THREADS} WHERE id = ?`,
1354
- args: [threadId]
1355
- });
1356
- } catch (error$1) {
1638
+ async deleteThread({ threadId }) {
1639
+ try {
1640
+ await this.client.execute({
1641
+ sql: `DELETE FROM ${storage.TABLE_MESSAGES} WHERE thread_id = ?`,
1642
+ args: [threadId]
1643
+ });
1644
+ await this.client.execute({
1645
+ sql: `DELETE FROM ${storage.TABLE_THREADS} WHERE id = ?`,
1646
+ args: [threadId]
1647
+ });
1648
+ } catch (error$1) {
1649
+ throw new error.MastraError(
1650
+ {
1651
+ id: "LIBSQL_STORE_DELETE_THREAD_FAILED",
1652
+ domain: error.ErrorDomain.STORAGE,
1653
+ category: error.ErrorCategory.THIRD_PARTY,
1654
+ details: { threadId }
1655
+ },
1656
+ error$1
1657
+ );
1658
+ }
1659
+ }
1660
+ };
1661
+ function createExecuteWriteOperationWithRetry({
1662
+ logger,
1663
+ maxRetries,
1664
+ initialBackoffMs
1665
+ }) {
1666
+ return async function executeWriteOperationWithRetry(operationFn, operationDescription) {
1667
+ let retries = 0;
1668
+ while (true) {
1669
+ try {
1670
+ return await operationFn();
1671
+ } catch (error) {
1672
+ if (error.message && (error.message.includes("SQLITE_BUSY") || error.message.includes("database is locked")) && retries < maxRetries) {
1673
+ retries++;
1674
+ const backoffTime = initialBackoffMs * Math.pow(2, retries - 1);
1675
+ logger.warn(
1676
+ `LibSQLStore: Encountered SQLITE_BUSY during ${operationDescription}. Retrying (${retries}/${maxRetries}) in ${backoffTime}ms...`
1677
+ );
1678
+ await new Promise((resolve) => setTimeout(resolve, backoffTime));
1679
+ } else {
1680
+ logger.error(`LibSQLStore: Error during ${operationDescription} after ${retries} retries: ${error}`);
1681
+ throw error;
1682
+ }
1683
+ }
1684
+ }
1685
+ };
1686
+ }
1687
+ function prepareStatement({ tableName, record }) {
1688
+ const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
1689
+ const columns = Object.keys(record).map((col) => utils.parseSqlIdentifier(col, "column name"));
1690
+ const values = Object.values(record).map((v) => {
1691
+ if (typeof v === `undefined`) {
1692
+ return null;
1693
+ }
1694
+ if (v instanceof Date) {
1695
+ return v.toISOString();
1696
+ }
1697
+ return typeof v === "object" ? JSON.stringify(v) : v;
1698
+ });
1699
+ const placeholders = values.map(() => "?").join(", ");
1700
+ return {
1701
+ sql: `INSERT OR REPLACE INTO ${parsedTableName} (${columns.join(", ")}) VALUES (${placeholders})`,
1702
+ args: values
1703
+ };
1704
+ }
1705
+
1706
+ // src/storage/domains/operations/index.ts
1707
+ var StoreOperationsLibSQL = class extends storage.StoreOperations {
1708
+ client;
1709
+ /**
1710
+ * Maximum number of retries for write operations if an SQLITE_BUSY error occurs.
1711
+ * @default 5
1712
+ */
1713
+ maxRetries;
1714
+ /**
1715
+ * Initial backoff time in milliseconds for retrying write operations on SQLITE_BUSY.
1716
+ * The backoff time will double with each retry (exponential backoff).
1717
+ * @default 100
1718
+ */
1719
+ initialBackoffMs;
1720
+ constructor({
1721
+ client,
1722
+ maxRetries,
1723
+ initialBackoffMs
1724
+ }) {
1725
+ super();
1726
+ this.client = client;
1727
+ this.maxRetries = maxRetries ?? 5;
1728
+ this.initialBackoffMs = initialBackoffMs ?? 100;
1729
+ }
1730
+ async hasColumn(table, column) {
1731
+ const result = await this.client.execute({
1732
+ sql: `PRAGMA table_info(${table})`
1733
+ });
1734
+ return (await result.rows)?.some((row) => row.name === column);
1735
+ }
1736
+ getCreateTableSQL(tableName, schema) {
1737
+ const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
1738
+ const columns = Object.entries(schema).map(([name, col]) => {
1739
+ const parsedColumnName = utils.parseSqlIdentifier(name, "column name");
1740
+ let type = col.type.toUpperCase();
1741
+ if (type === "TEXT") type = "TEXT";
1742
+ if (type === "TIMESTAMP") type = "TEXT";
1743
+ const nullable = col.nullable ? "" : "NOT NULL";
1744
+ const primaryKey = col.primaryKey ? "PRIMARY KEY" : "";
1745
+ return `${parsedColumnName} ${type} ${nullable} ${primaryKey}`.trim();
1746
+ });
1747
+ if (tableName === storage.TABLE_WORKFLOW_SNAPSHOT) {
1748
+ const stmnt = `CREATE TABLE IF NOT EXISTS ${parsedTableName} (
1749
+ ${columns.join(",\n")},
1750
+ PRIMARY KEY (workflow_name, run_id)
1751
+ )`;
1752
+ return stmnt;
1753
+ }
1754
+ return `CREATE TABLE IF NOT EXISTS ${parsedTableName} (${columns.join(", ")})`;
1755
+ }
1756
+ async createTable({
1757
+ tableName,
1758
+ schema
1759
+ }) {
1760
+ try {
1761
+ this.logger.debug(`Creating database table`, { tableName, operation: "schema init" });
1762
+ const sql = this.getCreateTableSQL(tableName, schema);
1763
+ await this.client.execute(sql);
1764
+ } catch (error$1) {
1765
+ throw new error.MastraError(
1766
+ {
1767
+ id: "LIBSQL_STORE_CREATE_TABLE_FAILED",
1768
+ domain: error.ErrorDomain.STORAGE,
1769
+ category: error.ErrorCategory.THIRD_PARTY,
1770
+ details: {
1771
+ tableName
1772
+ }
1773
+ },
1774
+ error$1
1775
+ );
1776
+ }
1777
+ }
1778
+ getSqlType(type) {
1779
+ switch (type) {
1780
+ case "bigint":
1781
+ return "INTEGER";
1782
+ // SQLite uses INTEGER for all integer sizes
1783
+ case "jsonb":
1784
+ return "TEXT";
1785
+ // Store JSON as TEXT in SQLite
1786
+ default:
1787
+ return super.getSqlType(type);
1788
+ }
1789
+ }
1790
+ async doInsert({
1791
+ tableName,
1792
+ record
1793
+ }) {
1794
+ await this.client.execute(
1795
+ prepareStatement({
1796
+ tableName,
1797
+ record
1798
+ })
1799
+ );
1800
+ }
1801
+ insert(args) {
1802
+ const executeWriteOperationWithRetry = createExecuteWriteOperationWithRetry({
1803
+ logger: this.logger,
1804
+ maxRetries: this.maxRetries,
1805
+ initialBackoffMs: this.initialBackoffMs
1806
+ });
1807
+ return executeWriteOperationWithRetry(() => this.doInsert(args), `insert into table ${args.tableName}`);
1808
+ }
1809
+ async load({ tableName, keys }) {
1810
+ const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
1811
+ const parsedKeys = Object.keys(keys).map((key) => utils.parseSqlIdentifier(key, "column name"));
1812
+ const conditions = parsedKeys.map((key) => `${key} = ?`).join(" AND ");
1813
+ const values = Object.values(keys);
1814
+ const result = await this.client.execute({
1815
+ sql: `SELECT * FROM ${parsedTableName} WHERE ${conditions} ORDER BY createdAt DESC LIMIT 1`,
1816
+ args: values
1817
+ });
1818
+ if (!result.rows || result.rows.length === 0) {
1819
+ return null;
1820
+ }
1821
+ const row = result.rows[0];
1822
+ const parsed = Object.fromEntries(
1823
+ Object.entries(row || {}).map(([k, v]) => {
1824
+ try {
1825
+ return [k, typeof v === "string" ? v.startsWith("{") || v.startsWith("[") ? JSON.parse(v) : v : v];
1826
+ } catch {
1827
+ return [k, v];
1828
+ }
1829
+ })
1830
+ );
1831
+ return parsed;
1832
+ }
1833
+ async doBatchInsert({
1834
+ tableName,
1835
+ records
1836
+ }) {
1837
+ if (records.length === 0) return;
1838
+ const batchStatements = records.map((r) => prepareStatement({ tableName, record: r }));
1839
+ await this.client.batch(batchStatements, "write");
1840
+ }
1841
+ batchInsert(args) {
1842
+ const executeWriteOperationWithRetry = createExecuteWriteOperationWithRetry({
1843
+ logger: this.logger,
1844
+ maxRetries: this.maxRetries,
1845
+ initialBackoffMs: this.initialBackoffMs
1846
+ });
1847
+ return executeWriteOperationWithRetry(
1848
+ () => this.doBatchInsert(args),
1849
+ `batch insert into table ${args.tableName}`
1850
+ ).catch((error$1) => {
1357
1851
  throw new error.MastraError(
1358
1852
  {
1359
- id: "LIBSQL_STORE_DELETE_THREAD_FAILED",
1853
+ id: "LIBSQL_STORE_BATCH_INSERT_FAILED",
1360
1854
  domain: error.ErrorDomain.STORAGE,
1361
1855
  category: error.ErrorCategory.THIRD_PARTY,
1362
- details: { threadId }
1856
+ details: {
1857
+ tableName: args.tableName
1858
+ }
1363
1859
  },
1364
1860
  error$1
1365
1861
  );
1366
- }
1367
- }
1368
- parseRow(row) {
1369
- let content = row.content;
1370
- try {
1371
- content = JSON.parse(row.content);
1372
- } catch {
1373
- }
1374
- const result = {
1375
- id: row.id,
1376
- content,
1377
- role: row.role,
1378
- createdAt: new Date(row.createdAt),
1379
- threadId: row.thread_id,
1380
- resourceId: row.resourceId
1381
- };
1382
- if (row.type && row.type !== `v2`) result.type = row.type;
1383
- return result;
1384
- }
1385
- async _getIncludedMessages({
1386
- threadId,
1387
- selectBy
1388
- }) {
1389
- const include = selectBy?.include;
1390
- if (!include) return null;
1391
- const unionQueries = [];
1392
- const params = [];
1393
- for (const inc of include) {
1394
- const { id, withPreviousMessages = 0, withNextMessages = 0 } = inc;
1395
- const searchId = inc.threadId || threadId;
1396
- unionQueries.push(
1397
- `
1398
- SELECT * FROM (
1399
- WITH numbered_messages AS (
1400
- SELECT
1401
- id, content, role, type, "createdAt", thread_id, "resourceId",
1402
- ROW_NUMBER() OVER (ORDER BY "createdAt" ASC) as row_num
1403
- FROM "${storage.TABLE_MESSAGES}"
1404
- WHERE thread_id = ?
1405
- ),
1406
- target_positions AS (
1407
- SELECT row_num as target_pos
1408
- FROM numbered_messages
1409
- WHERE id = ?
1410
- )
1411
- SELECT DISTINCT m.*
1412
- FROM numbered_messages m
1413
- CROSS JOIN target_positions t
1414
- WHERE m.row_num BETWEEN (t.target_pos - ?) AND (t.target_pos + ?)
1415
- )
1416
- `
1417
- // Keep ASC for final sorting after fetching context
1418
- );
1419
- params.push(searchId, id, withPreviousMessages, withNextMessages);
1420
- }
1421
- const finalQuery = unionQueries.join(" UNION ALL ") + ' ORDER BY "createdAt" ASC';
1422
- const includedResult = await this.client.execute({ sql: finalQuery, args: params });
1423
- const includedRows = includedResult.rows?.map((row) => this.parseRow(row));
1424
- const seen = /* @__PURE__ */ new Set();
1425
- const dedupedRows = includedRows.filter((row) => {
1426
- if (seen.has(row.id)) return false;
1427
- seen.add(row.id);
1428
- return true;
1429
1862
  });
1430
- return dedupedRows;
1431
1863
  }
1432
- async getMessages({
1433
- threadId,
1434
- selectBy,
1435
- format
1864
+ /**
1865
+ * Alters table schema to add columns if they don't exist
1866
+ * @param tableName Name of the table
1867
+ * @param schema Schema of the table
1868
+ * @param ifNotExists Array of column names to add if they don't exist
1869
+ */
1870
+ async alterTable({
1871
+ tableName,
1872
+ schema,
1873
+ ifNotExists
1436
1874
  }) {
1875
+ const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
1437
1876
  try {
1438
- const messages = [];
1439
- const limit = this.resolveMessageLimit({ last: selectBy?.last, defaultLimit: 40 });
1440
- if (selectBy?.include?.length) {
1441
- const includeMessages = await this._getIncludedMessages({ threadId, selectBy });
1442
- if (includeMessages) {
1443
- messages.push(...includeMessages);
1877
+ const pragmaQuery = `PRAGMA table_info(${parsedTableName})`;
1878
+ const result = await this.client.execute(pragmaQuery);
1879
+ const existingColumnNames = new Set(result.rows.map((row) => row.name.toLowerCase()));
1880
+ for (const columnName of ifNotExists) {
1881
+ if (!existingColumnNames.has(columnName.toLowerCase()) && schema[columnName]) {
1882
+ const columnDef = schema[columnName];
1883
+ const sqlType = this.getSqlType(columnDef.type);
1884
+ const nullable = columnDef.nullable === false ? "NOT NULL" : "";
1885
+ const defaultValue = columnDef.nullable === false ? this.getDefaultValue(columnDef.type) : "";
1886
+ const alterSql = `ALTER TABLE ${parsedTableName} ADD COLUMN "${columnName}" ${sqlType} ${nullable} ${defaultValue}`.trim();
1887
+ await this.client.execute(alterSql);
1888
+ this.logger?.debug?.(`Added column ${columnName} to table ${parsedTableName}`);
1444
1889
  }
1445
1890
  }
1446
- const excludeIds = messages.map((m) => m.id);
1447
- const remainingSql = `
1448
- SELECT
1449
- id,
1450
- content,
1451
- role,
1452
- type,
1453
- "createdAt",
1454
- thread_id,
1455
- "resourceId"
1456
- FROM "${storage.TABLE_MESSAGES}"
1457
- WHERE thread_id = ?
1458
- ${excludeIds.length ? `AND id NOT IN (${excludeIds.map(() => "?").join(", ")})` : ""}
1459
- ORDER BY "createdAt" DESC
1460
- LIMIT ?
1461
- `;
1462
- const remainingArgs = [threadId, ...excludeIds.length ? excludeIds : [], limit];
1463
- const remainingResult = await this.client.execute({ sql: remainingSql, args: remainingArgs });
1464
- if (remainingResult.rows) {
1465
- messages.push(...remainingResult.rows.map((row) => this.parseRow(row)));
1466
- }
1467
- messages.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
1468
- const list = new agent.MessageList().add(messages, "memory");
1469
- if (format === `v2`) return list.get.all.v2();
1470
- return list.get.all.v1();
1471
1891
  } catch (error$1) {
1472
1892
  throw new error.MastraError(
1473
1893
  {
1474
- id: "LIBSQL_STORE_GET_MESSAGES_FAILED",
1894
+ id: "LIBSQL_STORE_ALTER_TABLE_FAILED",
1475
1895
  domain: error.ErrorDomain.STORAGE,
1476
1896
  category: error.ErrorCategory.THIRD_PARTY,
1477
- details: { threadId }
1897
+ details: {
1898
+ tableName
1899
+ }
1478
1900
  },
1479
1901
  error$1
1480
1902
  );
1481
1903
  }
1482
1904
  }
1483
- async getMessagesPaginated(args) {
1484
- const { threadId, format, selectBy } = args;
1485
- const { page = 0, perPage: perPageInput, dateRange } = selectBy?.pagination || {};
1486
- const perPage = perPageInput !== void 0 ? perPageInput : this.resolveMessageLimit({ last: selectBy?.last, defaultLimit: 40 });
1487
- const fromDate = dateRange?.start;
1488
- const toDate = dateRange?.end;
1489
- const messages = [];
1490
- if (selectBy?.include?.length) {
1491
- try {
1492
- const includeMessages = await this._getIncludedMessages({ threadId, selectBy });
1493
- if (includeMessages) {
1494
- messages.push(...includeMessages);
1495
- }
1496
- } catch (error$1) {
1497
- throw new error.MastraError(
1498
- {
1499
- id: "LIBSQL_STORE_GET_MESSAGES_PAGINATED_GET_INCLUDE_MESSAGES_FAILED",
1500
- domain: error.ErrorDomain.STORAGE,
1501
- category: error.ErrorCategory.THIRD_PARTY,
1502
- details: { threadId }
1503
- },
1504
- error$1
1505
- );
1506
- }
1507
- }
1905
+ async clearTable({ tableName }) {
1906
+ const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
1508
1907
  try {
1509
- const currentOffset = page * perPage;
1510
- const conditions = [`thread_id = ?`];
1511
- const queryParams = [threadId];
1512
- if (fromDate) {
1513
- conditions.push(`"createdAt" >= ?`);
1514
- queryParams.push(fromDate.toISOString());
1515
- }
1516
- if (toDate) {
1517
- conditions.push(`"createdAt" <= ?`);
1518
- queryParams.push(toDate.toISOString());
1519
- }
1520
- const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
1521
- const countResult = await this.client.execute({
1522
- sql: `SELECT COUNT(*) as count FROM ${storage.TABLE_MESSAGES} ${whereClause}`,
1523
- args: queryParams
1524
- });
1525
- const total = Number(countResult.rows?.[0]?.count ?? 0);
1526
- if (total === 0 && messages.length === 0) {
1527
- return {
1528
- messages: [],
1529
- total: 0,
1530
- page,
1531
- perPage,
1532
- hasMore: false
1533
- };
1534
- }
1535
- const excludeIds = messages.map((m) => m.id);
1536
- const excludeIdsParam = excludeIds.map((_, idx) => `$${idx + queryParams.length + 1}`).join(", ");
1537
- const dataResult = await this.client.execute({
1538
- 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 ?`,
1539
- args: [...queryParams, ...excludeIds, perPage, currentOffset]
1540
- });
1541
- messages.push(...(dataResult.rows || []).map((row) => this.parseRow(row)));
1542
- const messagesToReturn = format === "v1" ? new agent.MessageList().add(messages, "memory").get.all.v1() : new agent.MessageList().add(messages, "memory").get.all.v2();
1543
- return {
1544
- messages: messagesToReturn,
1545
- total,
1546
- page,
1547
- perPage,
1548
- hasMore: currentOffset + messages.length < total
1549
- };
1550
- } catch (error$1) {
1908
+ await this.client.execute(`DELETE FROM ${parsedTableName}`);
1909
+ } catch (e) {
1551
1910
  const mastraError = new error.MastraError(
1552
1911
  {
1553
- id: "LIBSQL_STORE_GET_MESSAGES_PAGINATED_FAILED",
1912
+ id: "LIBSQL_STORE_CLEAR_TABLE_FAILED",
1554
1913
  domain: error.ErrorDomain.STORAGE,
1555
1914
  category: error.ErrorCategory.THIRD_PARTY,
1556
- details: { threadId }
1915
+ details: {
1916
+ tableName
1917
+ }
1557
1918
  },
1558
- error$1
1919
+ e
1559
1920
  );
1560
1921
  this.logger?.trackException?.(mastraError);
1561
1922
  this.logger?.error?.(mastraError.toString());
1562
- return { messages: [], total: 0, page, perPage, hasMore: false };
1563
1923
  }
1564
1924
  }
1565
- async saveMessages({
1566
- messages,
1567
- format
1568
- }) {
1569
- if (messages.length === 0) return messages;
1925
+ async dropTable({ tableName }) {
1926
+ const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
1570
1927
  try {
1571
- const threadId = messages[0]?.threadId;
1572
- if (!threadId) {
1573
- throw new Error("Thread ID is required");
1574
- }
1575
- const batchStatements = messages.map((message) => {
1576
- const time = message.createdAt || /* @__PURE__ */ new Date();
1577
- if (!message.threadId) {
1578
- throw new Error(
1579
- `Expected to find a threadId for message, but couldn't find one. An unexpected error has occurred.`
1580
- );
1581
- }
1582
- if (!message.resourceId) {
1583
- throw new Error(
1584
- `Expected to find a resourceId for message, but couldn't find one. An unexpected error has occurred.`
1585
- );
1586
- }
1587
- return {
1588
- sql: `INSERT INTO ${storage.TABLE_MESSAGES} (id, thread_id, content, role, type, createdAt, resourceId)
1589
- VALUES (?, ?, ?, ?, ?, ?, ?)
1590
- ON CONFLICT(id) DO UPDATE SET
1591
- thread_id=excluded.thread_id,
1592
- content=excluded.content,
1593
- role=excluded.role,
1594
- type=excluded.type,
1595
- resourceId=excluded.resourceId
1596
- `,
1597
- args: [
1598
- message.id,
1599
- message.threadId,
1600
- typeof message.content === "object" ? JSON.stringify(message.content) : message.content,
1601
- message.role,
1602
- message.type || "v2",
1603
- time instanceof Date ? time.toISOString() : time,
1604
- message.resourceId
1605
- ]
1606
- };
1607
- });
1608
- const now = (/* @__PURE__ */ new Date()).toISOString();
1609
- batchStatements.push({
1610
- sql: `UPDATE ${storage.TABLE_THREADS} SET updatedAt = ? WHERE id = ?`,
1611
- args: [now, threadId]
1928
+ await this.client.execute(`DROP TABLE IF EXISTS ${parsedTableName}`);
1929
+ } catch (e) {
1930
+ throw new error.MastraError(
1931
+ {
1932
+ id: "LIBSQL_STORE_DROP_TABLE_FAILED",
1933
+ domain: error.ErrorDomain.STORAGE,
1934
+ category: error.ErrorCategory.THIRD_PARTY,
1935
+ details: {
1936
+ tableName
1937
+ }
1938
+ },
1939
+ e
1940
+ );
1941
+ }
1942
+ }
1943
+ };
1944
+ var ScoresLibSQL = class extends storage.ScoresStorage {
1945
+ operations;
1946
+ client;
1947
+ constructor({ client, operations }) {
1948
+ super();
1949
+ this.operations = operations;
1950
+ this.client = client;
1951
+ }
1952
+ async getScoresByRunId({
1953
+ runId,
1954
+ pagination
1955
+ }) {
1956
+ try {
1957
+ const result = await this.client.execute({
1958
+ sql: `SELECT * FROM ${storage.TABLE_SCORERS} WHERE runId = ? ORDER BY createdAt DESC LIMIT ? OFFSET ?`,
1959
+ args: [runId, pagination.perPage + 1, pagination.page * pagination.perPage]
1612
1960
  });
1613
- await this.client.batch(batchStatements, "write");
1614
- const list = new agent.MessageList().add(messages, "memory");
1615
- if (format === `v2`) return list.get.all.v2();
1616
- return list.get.all.v1();
1961
+ return {
1962
+ scores: result.rows?.slice(0, pagination.perPage).map((row) => this.transformScoreRow(row)) ?? [],
1963
+ pagination: {
1964
+ total: result.rows?.length ?? 0,
1965
+ page: pagination.page,
1966
+ perPage: pagination.perPage,
1967
+ hasMore: result.rows?.length > pagination.perPage
1968
+ }
1969
+ };
1617
1970
  } catch (error$1) {
1618
1971
  throw new error.MastraError(
1619
1972
  {
1620
- id: "LIBSQL_STORE_SAVE_MESSAGES_FAILED",
1973
+ id: "LIBSQL_STORE_GET_SCORES_BY_RUN_ID_FAILED",
1621
1974
  domain: error.ErrorDomain.STORAGE,
1622
1975
  category: error.ErrorCategory.THIRD_PARTY
1623
1976
  },
@@ -1625,185 +1978,144 @@ var LibSQLStore = class extends storage.MastraStorage {
1625
1978
  );
1626
1979
  }
1627
1980
  }
1628
- async updateMessages({
1629
- messages
1981
+ async getScoresByScorerId({
1982
+ scorerId,
1983
+ entityId,
1984
+ entityType,
1985
+ pagination
1630
1986
  }) {
1631
- if (messages.length === 0) {
1632
- return [];
1633
- }
1634
- const messageIds = messages.map((m) => m.id);
1635
- const placeholders = messageIds.map(() => "?").join(",");
1636
- const selectSql = `SELECT * FROM ${storage.TABLE_MESSAGES} WHERE id IN (${placeholders})`;
1637
- const existingResult = await this.client.execute({ sql: selectSql, args: messageIds });
1638
- const existingMessages = existingResult.rows.map((row) => this.parseRow(row));
1639
- if (existingMessages.length === 0) {
1640
- return [];
1641
- }
1642
- const batchStatements = [];
1643
- const threadIdsToUpdate = /* @__PURE__ */ new Set();
1644
- const columnMapping = {
1645
- threadId: "thread_id"
1646
- };
1647
- for (const existingMessage of existingMessages) {
1648
- const updatePayload = messages.find((m) => m.id === existingMessage.id);
1649
- if (!updatePayload) continue;
1650
- const { id, ...fieldsToUpdate } = updatePayload;
1651
- if (Object.keys(fieldsToUpdate).length === 0) continue;
1652
- threadIdsToUpdate.add(existingMessage.threadId);
1653
- if (updatePayload.threadId && updatePayload.threadId !== existingMessage.threadId) {
1654
- threadIdsToUpdate.add(updatePayload.threadId);
1655
- }
1656
- const setClauses = [];
1657
- const args = [];
1658
- const updatableFields = { ...fieldsToUpdate };
1659
- if (updatableFields.content) {
1660
- const newContent = {
1661
- ...existingMessage.content,
1662
- ...updatableFields.content,
1663
- // Deep merge metadata if it exists on both
1664
- ...existingMessage.content?.metadata && updatableFields.content.metadata ? {
1665
- metadata: {
1666
- ...existingMessage.content.metadata,
1667
- ...updatableFields.content.metadata
1668
- }
1669
- } : {}
1670
- };
1671
- setClauses.push(`${utils.parseSqlIdentifier("content", "column name")} = ?`);
1672
- args.push(JSON.stringify(newContent));
1673
- delete updatableFields.content;
1987
+ try {
1988
+ const conditions = [];
1989
+ const queryParams = [];
1990
+ if (scorerId) {
1991
+ conditions.push(`scorerId = ?`);
1992
+ queryParams.push(scorerId);
1674
1993
  }
1675
- for (const key in updatableFields) {
1676
- if (Object.prototype.hasOwnProperty.call(updatableFields, key)) {
1677
- const dbKey = columnMapping[key] || key;
1678
- setClauses.push(`${utils.parseSqlIdentifier(dbKey, "column name")} = ?`);
1679
- let value = updatableFields[key];
1680
- if (typeof value === "object" && value !== null) {
1681
- value = JSON.stringify(value);
1682
- }
1683
- args.push(value);
1684
- }
1994
+ if (entityId) {
1995
+ conditions.push(`entityId = ?`);
1996
+ queryParams.push(entityId);
1685
1997
  }
1686
- if (setClauses.length === 0) continue;
1687
- args.push(id);
1688
- const sql = `UPDATE ${storage.TABLE_MESSAGES} SET ${setClauses.join(", ")} WHERE id = ?`;
1689
- batchStatements.push({ sql, args });
1690
- }
1691
- if (batchStatements.length === 0) {
1692
- return existingMessages;
1693
- }
1694
- const now = (/* @__PURE__ */ new Date()).toISOString();
1695
- for (const threadId of threadIdsToUpdate) {
1696
- if (threadId) {
1697
- batchStatements.push({
1698
- sql: `UPDATE ${storage.TABLE_THREADS} SET updatedAt = ? WHERE id = ?`,
1699
- args: [now, threadId]
1700
- });
1998
+ if (entityType) {
1999
+ conditions.push(`entityType = ?`);
2000
+ queryParams.push(entityType);
1701
2001
  }
2002
+ const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
2003
+ const result = await this.client.execute({
2004
+ sql: `SELECT * FROM ${storage.TABLE_SCORERS} ${whereClause} ORDER BY createdAt DESC LIMIT ? OFFSET ?`,
2005
+ args: [...queryParams, pagination.perPage + 1, pagination.page * pagination.perPage]
2006
+ });
2007
+ return {
2008
+ scores: result.rows?.slice(0, pagination.perPage).map((row) => this.transformScoreRow(row)) ?? [],
2009
+ pagination: {
2010
+ total: result.rows?.length ?? 0,
2011
+ page: pagination.page,
2012
+ perPage: pagination.perPage,
2013
+ hasMore: result.rows?.length > pagination.perPage
2014
+ }
2015
+ };
2016
+ } catch (error$1) {
2017
+ throw new error.MastraError(
2018
+ {
2019
+ id: "LIBSQL_STORE_GET_SCORES_BY_SCORER_ID_FAILED",
2020
+ domain: error.ErrorDomain.STORAGE,
2021
+ category: error.ErrorCategory.THIRD_PARTY
2022
+ },
2023
+ error$1
2024
+ );
1702
2025
  }
1703
- await this.client.batch(batchStatements, "write");
1704
- const updatedResult = await this.client.execute({ sql: selectSql, args: messageIds });
1705
- return updatedResult.rows.map((row) => this.parseRow(row));
1706
2026
  }
1707
- transformEvalRow(row) {
1708
- const resultValue = JSON.parse(row.result);
1709
- const testInfoValue = row.test_info ? JSON.parse(row.test_info) : void 0;
1710
- if (!resultValue || typeof resultValue !== "object" || !("score" in resultValue)) {
1711
- throw new Error(`Invalid MetricResult format: ${JSON.stringify(resultValue)}`);
1712
- }
2027
+ transformScoreRow(row) {
2028
+ const scorerValue = JSON.parse(row.scorer ?? "{}");
2029
+ const inputValue = JSON.parse(row.input ?? "{}");
2030
+ const outputValue = JSON.parse(row.output ?? "{}");
2031
+ const additionalLLMContextValue = row.additionalLLMContext ? JSON.parse(row.additionalLLMContext) : null;
2032
+ const runtimeContextValue = row.runtimeContext ? JSON.parse(row.runtimeContext) : null;
2033
+ const metadataValue = row.metadata ? JSON.parse(row.metadata) : null;
2034
+ const entityValue = row.entity ? JSON.parse(row.entity) : null;
2035
+ const extractStepResultValue = row.extractStepResult ? JSON.parse(row.extractStepResult) : null;
2036
+ const analyzeStepResultValue = row.analyzeStepResult ? JSON.parse(row.analyzeStepResult) : null;
1713
2037
  return {
1714
- input: row.input,
1715
- output: row.output,
1716
- result: resultValue,
1717
- agentName: row.agent_name,
1718
- metricName: row.metric_name,
1719
- instructions: row.instructions,
1720
- testInfo: testInfoValue,
1721
- globalRunId: row.global_run_id,
1722
- runId: row.run_id,
1723
- createdAt: row.created_at
2038
+ id: row.id,
2039
+ traceId: row.traceId,
2040
+ runId: row.runId,
2041
+ scorer: scorerValue,
2042
+ score: row.score,
2043
+ reason: row.reason,
2044
+ extractStepResult: extractStepResultValue,
2045
+ analyzeStepResult: analyzeStepResultValue,
2046
+ analyzePrompt: row.analyzePrompt,
2047
+ extractPrompt: row.extractPrompt,
2048
+ metadata: metadataValue,
2049
+ input: inputValue,
2050
+ output: outputValue,
2051
+ additionalContext: additionalLLMContextValue,
2052
+ runtimeContext: runtimeContextValue,
2053
+ entityType: row.entityType,
2054
+ entity: entityValue,
2055
+ entityId: row.entityId,
2056
+ scorerId: row.scorerId,
2057
+ source: row.source,
2058
+ resourceId: row.resourceId,
2059
+ threadId: row.threadId,
2060
+ createdAt: row.createdAt,
2061
+ updatedAt: row.updatedAt
1724
2062
  };
1725
2063
  }
1726
- /** @deprecated use getEvals instead */
1727
- async getEvalsByAgentName(agentName, type) {
2064
+ async getScoreById({ id }) {
2065
+ const result = await this.client.execute({
2066
+ sql: `SELECT * FROM ${storage.TABLE_SCORERS} WHERE id = ?`,
2067
+ args: [id]
2068
+ });
2069
+ return result.rows?.[0] ? this.transformScoreRow(result.rows[0]) : null;
2070
+ }
2071
+ async saveScore(score) {
1728
2072
  try {
1729
- const baseQuery = `SELECT * FROM ${storage.TABLE_EVALS} WHERE agent_name = ?`;
1730
- 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)" : "";
1731
- const result = await this.client.execute({
1732
- sql: `${baseQuery}${typeCondition} ORDER BY created_at DESC`,
1733
- args: [agentName]
2073
+ const id = crypto.randomUUID();
2074
+ await this.operations.insert({
2075
+ tableName: storage.TABLE_SCORERS,
2076
+ record: {
2077
+ id,
2078
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
2079
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
2080
+ ...score
2081
+ }
1734
2082
  });
1735
- return result.rows?.map((row) => this.transformEvalRow(row)) ?? [];
2083
+ const scoreFromDb = await this.getScoreById({ id });
2084
+ return { score: scoreFromDb };
1736
2085
  } catch (error$1) {
1737
- if (error$1 instanceof Error && error$1.message.includes("no such table")) {
1738
- return [];
1739
- }
1740
2086
  throw new error.MastraError(
1741
2087
  {
1742
- id: "LIBSQL_STORE_GET_EVALS_BY_AGENT_NAME_FAILED",
2088
+ id: "LIBSQL_STORE_SAVE_SCORE_FAILED",
1743
2089
  domain: error.ErrorDomain.STORAGE,
1744
- category: error.ErrorCategory.THIRD_PARTY,
1745
- details: { agentName }
2090
+ category: error.ErrorCategory.THIRD_PARTY
1746
2091
  },
1747
2092
  error$1
1748
2093
  );
1749
2094
  }
1750
2095
  }
1751
- async getEvals(options = {}) {
1752
- const { agentName, type, page = 0, perPage = 100, dateRange } = options;
1753
- const fromDate = dateRange?.start;
1754
- const toDate = dateRange?.end;
1755
- const conditions = [];
1756
- const queryParams = [];
1757
- if (agentName) {
1758
- conditions.push(`agent_name = ?`);
1759
- queryParams.push(agentName);
1760
- }
1761
- if (type === "test") {
1762
- conditions.push(`(test_info IS NOT NULL AND json_extract(test_info, '$.testPath') IS NOT NULL)`);
1763
- } else if (type === "live") {
1764
- conditions.push(`(test_info IS NULL OR json_extract(test_info, '$.testPath') IS NULL)`);
1765
- }
1766
- if (fromDate) {
1767
- conditions.push(`created_at >= ?`);
1768
- queryParams.push(fromDate.toISOString());
1769
- }
1770
- if (toDate) {
1771
- conditions.push(`created_at <= ?`);
1772
- queryParams.push(toDate.toISOString());
1773
- }
1774
- const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
2096
+ async getScoresByEntityId({
2097
+ entityId,
2098
+ entityType,
2099
+ pagination
2100
+ }) {
1775
2101
  try {
1776
- const countResult = await this.client.execute({
1777
- sql: `SELECT COUNT(*) as count FROM ${storage.TABLE_EVALS} ${whereClause}`,
1778
- args: queryParams
1779
- });
1780
- const total = Number(countResult.rows?.[0]?.count ?? 0);
1781
- const currentOffset = page * perPage;
1782
- const hasMore = currentOffset + perPage < total;
1783
- if (total === 0) {
1784
- return {
1785
- evals: [],
1786
- total: 0,
1787
- page,
1788
- perPage,
1789
- hasMore: false
1790
- };
1791
- }
1792
- const dataResult = await this.client.execute({
1793
- sql: `SELECT * FROM ${storage.TABLE_EVALS} ${whereClause} ORDER BY created_at DESC LIMIT ? OFFSET ?`,
1794
- args: [...queryParams, perPage, currentOffset]
2102
+ const result = await this.client.execute({
2103
+ sql: `SELECT * FROM ${storage.TABLE_SCORERS} WHERE entityId = ? AND entityType = ? ORDER BY createdAt DESC LIMIT ? OFFSET ?`,
2104
+ args: [entityId, entityType, pagination.perPage + 1, pagination.page * pagination.perPage]
1795
2105
  });
1796
2106
  return {
1797
- evals: dataResult.rows?.map((row) => this.transformEvalRow(row)) ?? [],
1798
- total,
1799
- page,
1800
- perPage,
1801
- hasMore
2107
+ scores: result.rows?.slice(0, pagination.perPage).map((row) => this.transformScoreRow(row)) ?? [],
2108
+ pagination: {
2109
+ total: result.rows?.length ?? 0,
2110
+ page: pagination.page,
2111
+ perPage: pagination.perPage,
2112
+ hasMore: result.rows?.length > pagination.perPage
2113
+ }
1802
2114
  };
1803
2115
  } catch (error$1) {
1804
2116
  throw new error.MastraError(
1805
2117
  {
1806
- id: "LIBSQL_STORE_GET_EVALS_FAILED",
2118
+ id: "LIBSQL_STORE_GET_SCORES_BY_ENTITY_ID_FAILED",
1807
2119
  domain: error.ErrorDomain.STORAGE,
1808
2120
  category: error.ErrorCategory.THIRD_PARTY
1809
2121
  },
@@ -1811,9 +2123,15 @@ var LibSQLStore = class extends storage.MastraStorage {
1811
2123
  );
1812
2124
  }
1813
2125
  }
1814
- /**
1815
- * @deprecated use getTracesPaginated instead.
1816
- */
2126
+ };
2127
+ var TracesLibSQL = class extends storage.TracesStorage {
2128
+ client;
2129
+ operations;
2130
+ constructor({ client, operations }) {
2131
+ super();
2132
+ this.client = client;
2133
+ this.operations = operations;
2134
+ }
1817
2135
  async getTraces(args) {
1818
2136
  if (args.fromDate || args.toDate) {
1819
2137
  args.dateRange = {
@@ -1890,35 +2208,133 @@ var LibSQLStore = class extends storage.MastraStorage {
1890
2208
  sql: `SELECT * FROM ${storage.TABLE_TRACES} ${whereClause} ORDER BY "startTime" DESC LIMIT ? OFFSET ?`,
1891
2209
  args: [...queryArgs, perPage, currentOffset]
1892
2210
  });
1893
- const traces = dataResult.rows?.map(
1894
- (row) => ({
1895
- id: row.id,
1896
- parentSpanId: row.parentSpanId,
1897
- traceId: row.traceId,
1898
- name: row.name,
1899
- scope: row.scope,
1900
- kind: row.kind,
1901
- status: safelyParseJSON(row.status),
1902
- events: safelyParseJSON(row.events),
1903
- links: safelyParseJSON(row.links),
1904
- attributes: safelyParseJSON(row.attributes),
1905
- startTime: row.startTime,
1906
- endTime: row.endTime,
1907
- other: safelyParseJSON(row.other),
1908
- createdAt: row.createdAt
1909
- })
1910
- ) ?? [];
1911
- return {
1912
- traces,
1913
- total,
1914
- page,
1915
- perPage,
1916
- hasMore: currentOffset + traces.length < total
1917
- };
2211
+ const traces = dataResult.rows?.map(
2212
+ (row) => ({
2213
+ id: row.id,
2214
+ parentSpanId: row.parentSpanId,
2215
+ traceId: row.traceId,
2216
+ name: row.name,
2217
+ scope: row.scope,
2218
+ kind: row.kind,
2219
+ status: storage.safelyParseJSON(row.status),
2220
+ events: storage.safelyParseJSON(row.events),
2221
+ links: storage.safelyParseJSON(row.links),
2222
+ attributes: storage.safelyParseJSON(row.attributes),
2223
+ startTime: row.startTime,
2224
+ endTime: row.endTime,
2225
+ other: storage.safelyParseJSON(row.other),
2226
+ createdAt: row.createdAt
2227
+ })
2228
+ ) ?? [];
2229
+ return {
2230
+ traces,
2231
+ total,
2232
+ page,
2233
+ perPage,
2234
+ hasMore: currentOffset + traces.length < total
2235
+ };
2236
+ } catch (error$1) {
2237
+ throw new error.MastraError(
2238
+ {
2239
+ id: "LIBSQL_STORE_GET_TRACES_PAGINATED_FAILED",
2240
+ domain: error.ErrorDomain.STORAGE,
2241
+ category: error.ErrorCategory.THIRD_PARTY
2242
+ },
2243
+ error$1
2244
+ );
2245
+ }
2246
+ }
2247
+ async batchTraceInsert({ records }) {
2248
+ this.logger.debug("Batch inserting traces", { count: records.length });
2249
+ await this.operations.batchInsert({
2250
+ tableName: storage.TABLE_TRACES,
2251
+ records
2252
+ });
2253
+ }
2254
+ };
2255
+ function parseWorkflowRun(row) {
2256
+ let parsedSnapshot = row.snapshot;
2257
+ if (typeof parsedSnapshot === "string") {
2258
+ try {
2259
+ parsedSnapshot = JSON.parse(row.snapshot);
2260
+ } catch (e) {
2261
+ console.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
2262
+ }
2263
+ }
2264
+ return {
2265
+ workflowName: row.workflow_name,
2266
+ runId: row.run_id,
2267
+ snapshot: parsedSnapshot,
2268
+ resourceId: row.resourceId,
2269
+ createdAt: new Date(row.createdAt),
2270
+ updatedAt: new Date(row.updatedAt)
2271
+ };
2272
+ }
2273
+ var WorkflowsLibSQL = class extends storage.WorkflowsStorage {
2274
+ operations;
2275
+ client;
2276
+ constructor({ operations, client }) {
2277
+ super();
2278
+ this.operations = operations;
2279
+ this.client = client;
2280
+ }
2281
+ async persistWorkflowSnapshot({
2282
+ workflowName,
2283
+ runId,
2284
+ snapshot
2285
+ }) {
2286
+ const data = {
2287
+ workflow_name: workflowName,
2288
+ run_id: runId,
2289
+ snapshot,
2290
+ createdAt: /* @__PURE__ */ new Date(),
2291
+ updatedAt: /* @__PURE__ */ new Date()
2292
+ };
2293
+ this.logger.debug("Persisting workflow snapshot", { workflowName, runId, data });
2294
+ await this.operations.insert({
2295
+ tableName: storage.TABLE_WORKFLOW_SNAPSHOT,
2296
+ record: data
2297
+ });
2298
+ }
2299
+ async loadWorkflowSnapshot({
2300
+ workflowName,
2301
+ runId
2302
+ }) {
2303
+ this.logger.debug("Loading workflow snapshot", { workflowName, runId });
2304
+ const d = await this.operations.load({
2305
+ tableName: storage.TABLE_WORKFLOW_SNAPSHOT,
2306
+ keys: { workflow_name: workflowName, run_id: runId }
2307
+ });
2308
+ return d ? d.snapshot : null;
2309
+ }
2310
+ async getWorkflowRunById({
2311
+ runId,
2312
+ workflowName
2313
+ }) {
2314
+ const conditions = [];
2315
+ const args = [];
2316
+ if (runId) {
2317
+ conditions.push("run_id = ?");
2318
+ args.push(runId);
2319
+ }
2320
+ if (workflowName) {
2321
+ conditions.push("workflow_name = ?");
2322
+ args.push(workflowName);
2323
+ }
2324
+ const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
2325
+ try {
2326
+ const result = await this.client.execute({
2327
+ sql: `SELECT * FROM ${storage.TABLE_WORKFLOW_SNAPSHOT} ${whereClause}`,
2328
+ args
2329
+ });
2330
+ if (!result.rows?.[0]) {
2331
+ return null;
2332
+ }
2333
+ return parseWorkflowRun(result.rows[0]);
1918
2334
  } catch (error$1) {
1919
2335
  throw new error.MastraError(
1920
2336
  {
1921
- id: "LIBSQL_STORE_GET_TRACES_PAGINATED_FAILED",
2337
+ id: "LIBSQL_STORE_GET_WORKFLOW_RUN_BY_ID_FAILED",
1922
2338
  domain: error.ErrorDomain.STORAGE,
1923
2339
  category: error.ErrorCategory.THIRD_PARTY
1924
2340
  },
@@ -1950,7 +2366,7 @@ var LibSQLStore = class extends storage.MastraStorage {
1950
2366
  args.push(toDate.toISOString());
1951
2367
  }
1952
2368
  if (resourceId) {
1953
- const hasResourceId = await this.hasColumn(storage.TABLE_WORKFLOW_SNAPSHOT, "resourceId");
2369
+ const hasResourceId = await this.operations.hasColumn(storage.TABLE_WORKFLOW_SNAPSHOT, "resourceId");
1954
2370
  if (hasResourceId) {
1955
2371
  conditions.push("resourceId = ?");
1956
2372
  args.push(resourceId);
@@ -1971,7 +2387,7 @@ var LibSQLStore = class extends storage.MastraStorage {
1971
2387
  sql: `SELECT * FROM ${storage.TABLE_WORKFLOW_SNAPSHOT} ${whereClause} ORDER BY createdAt DESC${limit !== void 0 && offset !== void 0 ? ` LIMIT ? OFFSET ?` : ""}`,
1972
2388
  args: limit !== void 0 && offset !== void 0 ? [...args, limit, offset] : args
1973
2389
  });
1974
- const runs = (result.rows || []).map((row) => this.parseWorkflowRun(row));
2390
+ const runs = (result.rows || []).map((row) => parseWorkflowRun(row));
1975
2391
  return { runs, total: total || runs.length };
1976
2392
  } catch (error$1) {
1977
2393
  throw new error.MastraError(
@@ -1984,133 +2400,227 @@ var LibSQLStore = class extends storage.MastraStorage {
1984
2400
  );
1985
2401
  }
1986
2402
  }
2403
+ };
2404
+
2405
+ // src/storage/index.ts
2406
+ var LibSQLStore = class extends storage.MastraStorage {
2407
+ client;
2408
+ maxRetries;
2409
+ initialBackoffMs;
2410
+ stores;
2411
+ constructor(config) {
2412
+ super({ name: `LibSQLStore` });
2413
+ this.maxRetries = config.maxRetries ?? 5;
2414
+ this.initialBackoffMs = config.initialBackoffMs ?? 100;
2415
+ if ("url" in config) {
2416
+ if (config.url.endsWith(":memory:")) {
2417
+ this.shouldCacheInit = false;
2418
+ }
2419
+ this.client = client.createClient({ url: config.url });
2420
+ if (config.url.startsWith("file:") || config.url.includes(":memory:")) {
2421
+ this.client.execute("PRAGMA journal_mode=WAL;").then(() => this.logger.debug("LibSQLStore: PRAGMA journal_mode=WAL set.")).catch((err) => this.logger.warn("LibSQLStore: Failed to set PRAGMA journal_mode=WAL.", err));
2422
+ this.client.execute("PRAGMA busy_timeout = 5000;").then(() => this.logger.debug("LibSQLStore: PRAGMA busy_timeout=5000 set.")).catch((err) => this.logger.warn("LibSQLStore: Failed to set PRAGMA busy_timeout.", err));
2423
+ }
2424
+ } else {
2425
+ this.client = config.client;
2426
+ }
2427
+ const operations = new StoreOperationsLibSQL({
2428
+ client: this.client,
2429
+ maxRetries: this.maxRetries,
2430
+ initialBackoffMs: this.initialBackoffMs
2431
+ });
2432
+ const scores = new ScoresLibSQL({ client: this.client, operations });
2433
+ const traces = new TracesLibSQL({ client: this.client, operations });
2434
+ const workflows = new WorkflowsLibSQL({ client: this.client, operations });
2435
+ const memory = new MemoryLibSQL({ client: this.client, operations });
2436
+ const legacyEvals = new LegacyEvalsLibSQL({ client: this.client });
2437
+ this.stores = {
2438
+ operations,
2439
+ scores,
2440
+ traces,
2441
+ workflows,
2442
+ memory,
2443
+ legacyEvals
2444
+ };
2445
+ }
2446
+ get supports() {
2447
+ return {
2448
+ selectByIncludeResourceScope: true,
2449
+ resourceWorkingMemory: true,
2450
+ hasColumn: true,
2451
+ createTable: true
2452
+ };
2453
+ }
2454
+ async createTable({
2455
+ tableName,
2456
+ schema
2457
+ }) {
2458
+ await this.stores.operations.createTable({ tableName, schema });
2459
+ }
2460
+ /**
2461
+ * Alters table schema to add columns if they don't exist
2462
+ * @param tableName Name of the table
2463
+ * @param schema Schema of the table
2464
+ * @param ifNotExists Array of column names to add if they don't exist
2465
+ */
2466
+ async alterTable({
2467
+ tableName,
2468
+ schema,
2469
+ ifNotExists
2470
+ }) {
2471
+ await this.stores.operations.alterTable({ tableName, schema, ifNotExists });
2472
+ }
2473
+ async clearTable({ tableName }) {
2474
+ await this.stores.operations.clearTable({ tableName });
2475
+ }
2476
+ async dropTable({ tableName }) {
2477
+ await this.stores.operations.dropTable({ tableName });
2478
+ }
2479
+ insert(args) {
2480
+ return this.stores.operations.insert(args);
2481
+ }
2482
+ batchInsert(args) {
2483
+ return this.stores.operations.batchInsert(args);
2484
+ }
2485
+ async load({ tableName, keys }) {
2486
+ return this.stores.operations.load({ tableName, keys });
2487
+ }
2488
+ async getThreadById({ threadId }) {
2489
+ return this.stores.memory.getThreadById({ threadId });
2490
+ }
2491
+ /**
2492
+ * @deprecated use getThreadsByResourceIdPaginated instead for paginated results.
2493
+ */
2494
+ async getThreadsByResourceId(args) {
2495
+ return this.stores.memory.getThreadsByResourceId(args);
2496
+ }
2497
+ async getThreadsByResourceIdPaginated(args) {
2498
+ return this.stores.memory.getThreadsByResourceIdPaginated(args);
2499
+ }
2500
+ async saveThread({ thread }) {
2501
+ return this.stores.memory.saveThread({ thread });
2502
+ }
2503
+ async updateThread({
2504
+ id,
2505
+ title,
2506
+ metadata
2507
+ }) {
2508
+ return this.stores.memory.updateThread({ id, title, metadata });
2509
+ }
2510
+ async deleteThread({ threadId }) {
2511
+ return this.stores.memory.deleteThread({ threadId });
2512
+ }
2513
+ async getMessages({
2514
+ threadId,
2515
+ selectBy,
2516
+ format
2517
+ }) {
2518
+ return this.stores.memory.getMessages({ threadId, selectBy, format });
2519
+ }
2520
+ async getMessagesPaginated(args) {
2521
+ return this.stores.memory.getMessagesPaginated(args);
2522
+ }
2523
+ async saveMessages(args) {
2524
+ return this.stores.memory.saveMessages(args);
2525
+ }
2526
+ async updateMessages({
2527
+ messages
2528
+ }) {
2529
+ return this.stores.memory.updateMessages({ messages });
2530
+ }
2531
+ /** @deprecated use getEvals instead */
2532
+ async getEvalsByAgentName(agentName, type) {
2533
+ return this.stores.legacyEvals.getEvalsByAgentName(agentName, type);
2534
+ }
2535
+ async getEvals(options = {}) {
2536
+ return this.stores.legacyEvals.getEvals(options);
2537
+ }
2538
+ async getScoreById({ id }) {
2539
+ return this.stores.scores.getScoreById({ id });
2540
+ }
2541
+ async saveScore(score) {
2542
+ return this.stores.scores.saveScore(score);
2543
+ }
2544
+ async getScoresByScorerId({
2545
+ scorerId,
2546
+ entityId,
2547
+ entityType,
2548
+ pagination
2549
+ }) {
2550
+ return this.stores.scores.getScoresByScorerId({ scorerId, entityId, entityType, pagination });
2551
+ }
2552
+ async getScoresByRunId({
2553
+ runId,
2554
+ pagination
2555
+ }) {
2556
+ return this.stores.scores.getScoresByRunId({ runId, pagination });
2557
+ }
2558
+ async getScoresByEntityId({
2559
+ entityId,
2560
+ entityType,
2561
+ pagination
2562
+ }) {
2563
+ return this.stores.scores.getScoresByEntityId({ entityId, entityType, pagination });
2564
+ }
2565
+ /**
2566
+ * TRACES
2567
+ */
2568
+ /**
2569
+ * @deprecated use getTracesPaginated instead.
2570
+ */
2571
+ async getTraces(args) {
2572
+ return this.stores.traces.getTraces(args);
2573
+ }
2574
+ async getTracesPaginated(args) {
2575
+ return this.stores.traces.getTracesPaginated(args);
2576
+ }
2577
+ async batchTraceInsert(args) {
2578
+ return this.stores.traces.batchTraceInsert(args);
2579
+ }
2580
+ /**
2581
+ * WORKFLOWS
2582
+ */
2583
+ async persistWorkflowSnapshot({
2584
+ workflowName,
2585
+ runId,
2586
+ snapshot
2587
+ }) {
2588
+ return this.stores.workflows.persistWorkflowSnapshot({ workflowName, runId, snapshot });
2589
+ }
2590
+ async loadWorkflowSnapshot({
2591
+ workflowName,
2592
+ runId
2593
+ }) {
2594
+ return this.stores.workflows.loadWorkflowSnapshot({ workflowName, runId });
2595
+ }
2596
+ async getWorkflowRuns({
2597
+ workflowName,
2598
+ fromDate,
2599
+ toDate,
2600
+ limit,
2601
+ offset,
2602
+ resourceId
2603
+ } = {}) {
2604
+ return this.stores.workflows.getWorkflowRuns({ workflowName, fromDate, toDate, limit, offset, resourceId });
2605
+ }
1987
2606
  async getWorkflowRunById({
1988
2607
  runId,
1989
2608
  workflowName
1990
2609
  }) {
1991
- const conditions = [];
1992
- const args = [];
1993
- if (runId) {
1994
- conditions.push("run_id = ?");
1995
- args.push(runId);
1996
- }
1997
- if (workflowName) {
1998
- conditions.push("workflow_name = ?");
1999
- args.push(workflowName);
2000
- }
2001
- const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
2002
- try {
2003
- const result = await this.client.execute({
2004
- sql: `SELECT * FROM ${storage.TABLE_WORKFLOW_SNAPSHOT} ${whereClause}`,
2005
- args
2006
- });
2007
- if (!result.rows?.[0]) {
2008
- return null;
2009
- }
2010
- return this.parseWorkflowRun(result.rows[0]);
2011
- } catch (error$1) {
2012
- throw new error.MastraError(
2013
- {
2014
- id: "LIBSQL_STORE_GET_WORKFLOW_RUN_BY_ID_FAILED",
2015
- domain: error.ErrorDomain.STORAGE,
2016
- category: error.ErrorCategory.THIRD_PARTY
2017
- },
2018
- error$1
2019
- );
2020
- }
2610
+ return this.stores.workflows.getWorkflowRunById({ runId, workflowName });
2021
2611
  }
2022
2612
  async getResourceById({ resourceId }) {
2023
- const result = await this.load({
2024
- tableName: storage.TABLE_RESOURCES,
2025
- keys: { id: resourceId }
2026
- });
2027
- if (!result) {
2028
- return null;
2029
- }
2030
- return {
2031
- ...result,
2032
- // Ensure workingMemory is always returned as a string, even if auto-parsed as JSON
2033
- workingMemory: typeof result.workingMemory === "object" ? JSON.stringify(result.workingMemory) : result.workingMemory,
2034
- metadata: typeof result.metadata === "string" ? JSON.parse(result.metadata) : result.metadata
2035
- };
2613
+ return this.stores.memory.getResourceById({ resourceId });
2036
2614
  }
2037
2615
  async saveResource({ resource }) {
2038
- await this.insert({
2039
- tableName: storage.TABLE_RESOURCES,
2040
- record: {
2041
- ...resource,
2042
- metadata: JSON.stringify(resource.metadata)
2043
- }
2044
- });
2045
- return resource;
2616
+ return this.stores.memory.saveResource({ resource });
2046
2617
  }
2047
2618
  async updateResource({
2048
2619
  resourceId,
2049
2620
  workingMemory,
2050
2621
  metadata
2051
2622
  }) {
2052
- const existingResource = await this.getResourceById({ resourceId });
2053
- if (!existingResource) {
2054
- const newResource = {
2055
- id: resourceId,
2056
- workingMemory,
2057
- metadata: metadata || {},
2058
- createdAt: /* @__PURE__ */ new Date(),
2059
- updatedAt: /* @__PURE__ */ new Date()
2060
- };
2061
- return this.saveResource({ resource: newResource });
2062
- }
2063
- const updatedResource = {
2064
- ...existingResource,
2065
- workingMemory: workingMemory !== void 0 ? workingMemory : existingResource.workingMemory,
2066
- metadata: {
2067
- ...existingResource.metadata,
2068
- ...metadata
2069
- },
2070
- updatedAt: /* @__PURE__ */ new Date()
2071
- };
2072
- const updates = [];
2073
- const values = [];
2074
- if (workingMemory !== void 0) {
2075
- updates.push("workingMemory = ?");
2076
- values.push(workingMemory);
2077
- }
2078
- if (metadata) {
2079
- updates.push("metadata = ?");
2080
- values.push(JSON.stringify(updatedResource.metadata));
2081
- }
2082
- updates.push("updatedAt = ?");
2083
- values.push(updatedResource.updatedAt.toISOString());
2084
- values.push(resourceId);
2085
- await this.client.execute({
2086
- sql: `UPDATE ${storage.TABLE_RESOURCES} SET ${updates.join(", ")} WHERE id = ?`,
2087
- args: values
2088
- });
2089
- return updatedResource;
2090
- }
2091
- async hasColumn(table, column) {
2092
- const result = await this.client.execute({
2093
- sql: `PRAGMA table_info(${table})`
2094
- });
2095
- return (await result.rows)?.some((row) => row.name === column);
2096
- }
2097
- parseWorkflowRun(row) {
2098
- let parsedSnapshot = row.snapshot;
2099
- if (typeof parsedSnapshot === "string") {
2100
- try {
2101
- parsedSnapshot = JSON.parse(row.snapshot);
2102
- } catch (e) {
2103
- console.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
2104
- }
2105
- }
2106
- return {
2107
- workflowName: row.workflow_name,
2108
- runId: row.run_id,
2109
- snapshot: parsedSnapshot,
2110
- resourceId: row.resourceId,
2111
- createdAt: new Date(row.createdAt),
2112
- updatedAt: new Date(row.updatedAt)
2113
- };
2623
+ return this.stores.memory.updateResource({ resourceId, workingMemory, metadata });
2114
2624
  }
2115
2625
  };
2116
2626