@mastra/libsql 1.0.0-beta.8 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/CHANGELOG.md +1358 -0
  2. package/dist/docs/README.md +39 -0
  3. package/dist/docs/SKILL.md +40 -0
  4. package/dist/docs/SOURCE_MAP.json +6 -0
  5. package/dist/docs/agents/01-agent-memory.md +166 -0
  6. package/dist/docs/agents/02-networks.md +292 -0
  7. package/dist/docs/agents/03-agent-approval.md +377 -0
  8. package/dist/docs/agents/04-network-approval.md +274 -0
  9. package/dist/docs/core/01-reference.md +151 -0
  10. package/dist/docs/guides/01-ai-sdk.md +141 -0
  11. package/dist/docs/memory/01-overview.md +76 -0
  12. package/dist/docs/memory/02-storage.md +233 -0
  13. package/dist/docs/memory/03-working-memory.md +390 -0
  14. package/dist/docs/memory/04-semantic-recall.md +233 -0
  15. package/dist/docs/memory/05-memory-processors.md +318 -0
  16. package/dist/docs/memory/06-reference.md +133 -0
  17. package/dist/docs/observability/01-overview.md +64 -0
  18. package/dist/docs/observability/02-default.md +177 -0
  19. package/dist/docs/rag/01-retrieval.md +548 -0
  20. package/dist/docs/storage/01-reference.md +542 -0
  21. package/dist/docs/vectors/01-reference.md +213 -0
  22. package/dist/docs/workflows/01-snapshots.md +240 -0
  23. package/dist/index.cjs +2394 -1824
  24. package/dist/index.cjs.map +1 -1
  25. package/dist/index.js +2392 -1827
  26. package/dist/index.js.map +1 -1
  27. package/dist/storage/db/index.d.ts +305 -0
  28. package/dist/storage/db/index.d.ts.map +1 -0
  29. package/dist/storage/{domains → db}/utils.d.ts +21 -13
  30. package/dist/storage/db/utils.d.ts.map +1 -0
  31. package/dist/storage/domains/agents/index.d.ts +5 -7
  32. package/dist/storage/domains/agents/index.d.ts.map +1 -1
  33. package/dist/storage/domains/memory/index.d.ts +8 -10
  34. package/dist/storage/domains/memory/index.d.ts.map +1 -1
  35. package/dist/storage/domains/observability/index.d.ts +42 -27
  36. package/dist/storage/domains/observability/index.d.ts.map +1 -1
  37. package/dist/storage/domains/scores/index.d.ts +11 -27
  38. package/dist/storage/domains/scores/index.d.ts.map +1 -1
  39. package/dist/storage/domains/workflows/index.d.ts +10 -14
  40. package/dist/storage/domains/workflows/index.d.ts.map +1 -1
  41. package/dist/storage/index.d.ts +28 -189
  42. package/dist/storage/index.d.ts.map +1 -1
  43. package/dist/vector/index.d.ts +6 -2
  44. package/dist/vector/index.d.ts.map +1 -1
  45. package/dist/vector/sql-builder.d.ts.map +1 -1
  46. package/package.json +9 -8
  47. package/dist/storage/domains/operations/index.d.ts +0 -110
  48. package/dist/storage/domains/operations/index.d.ts.map +0 -1
  49. package/dist/storage/domains/utils.d.ts.map +0 -1
package/dist/index.cjs CHANGED
@@ -6,6 +6,7 @@ var storage = require('@mastra/core/storage');
6
6
  var utils = require('@mastra/core/utils');
7
7
  var vector = require('@mastra/core/vector');
8
8
  var filter = require('@mastra/core/vector/filter');
9
+ var base = require('@mastra/core/base');
9
10
  var agent = require('@mastra/core/agent');
10
11
  var evals = require('@mastra/core/evals');
11
12
 
@@ -243,10 +244,10 @@ var FILTER_OPERATORS = {
243
244
  };
244
245
  },
245
246
  // Element Operators
246
- $exists: (key) => {
247
+ $exists: (key, value) => {
247
248
  const jsonPath = getJsonPath(key);
248
249
  return {
249
- sql: `json_extract(metadata, ${jsonPath}) IS NOT NULL`,
250
+ sql: value === false ? `json_extract(metadata, ${jsonPath}) IS NULL` : `json_extract(metadata, ${jsonPath}) IS NOT NULL`,
250
251
  needsValue: false
251
252
  };
252
253
  },
@@ -510,7 +511,7 @@ var LibSQLVector = class extends vector.MastraVector {
510
511
  maxRetries;
511
512
  initialBackoffMs;
512
513
  constructor({
513
- connectionUrl,
514
+ url,
514
515
  authToken,
515
516
  syncUrl,
516
517
  syncInterval,
@@ -520,14 +521,14 @@ var LibSQLVector = class extends vector.MastraVector {
520
521
  }) {
521
522
  super({ id });
522
523
  this.turso = client.createClient({
523
- url: connectionUrl,
524
+ url,
524
525
  syncUrl,
525
526
  authToken,
526
527
  syncInterval
527
528
  });
528
529
  this.maxRetries = maxRetries;
529
530
  this.initialBackoffMs = initialBackoffMs;
530
- if (connectionUrl.includes(`file:`) || connectionUrl.includes(`:memory:`)) {
531
+ if (url.includes(`file:`) || url.includes(`:memory:`)) {
531
532
  this.turso.execute("PRAGMA journal_mode=WAL;").then(() => this.logger.debug("LibSQLStore: PRAGMA journal_mode=WAL set.")).catch((err) => this.logger.warn("LibSQLStore: Failed to set PRAGMA journal_mode=WAL.", err));
532
533
  this.turso.execute("PRAGMA busy_timeout = 5000;").then(() => this.logger.debug("LibSQLStore: PRAGMA busy_timeout=5000 set.")).catch((err) => this.logger.warn("LibSQLStore: Failed to set PRAGMA busy_timeout=5000.", err));
533
534
  }
@@ -539,7 +540,7 @@ var LibSQLVector = class extends vector.MastraVector {
539
540
  try {
540
541
  return await operation();
541
542
  } catch (error) {
542
- if (error.code === "SQLITE_BUSY" || error.message && error.message.toLowerCase().includes("database is locked")) {
543
+ if (error.code === "SQLITE_BUSY" || error.code === "SQLITE_LOCKED" || error.code === "SQLITE_LOCKED_SHAREDCACHE" || error.message && error.message.toLowerCase().includes("database is locked") || error.message && error.message.toLowerCase().includes("database table is locked")) {
543
544
  attempts++;
544
545
  if (attempts >= this.maxRetries) {
545
546
  this.logger.error(
@@ -573,22 +574,14 @@ var LibSQLVector = class extends vector.MastraVector {
573
574
  minScore = -1
574
575
  // Default to -1 to include all results (cosine similarity ranges from -1 to 1)
575
576
  }) {
576
- try {
577
- if (!Number.isInteger(topK) || topK <= 0) {
578
- throw new Error("topK must be a positive integer");
579
- }
580
- if (!Array.isArray(queryVector) || !queryVector.every((x) => typeof x === "number" && Number.isFinite(x))) {
581
- throw new Error("queryVector must be an array of finite numbers");
582
- }
583
- } catch (error$1) {
584
- throw new error.MastraError(
585
- {
586
- id: storage.createVectorErrorId("LIBSQL", "QUERY", "INVALID_ARGS"),
587
- domain: error.ErrorDomain.STORAGE,
588
- category: error.ErrorCategory.USER
589
- },
590
- error$1
591
- );
577
+ vector.validateTopK("LIBSQL", topK);
578
+ if (!Array.isArray(queryVector) || !queryVector.every((x) => typeof x === "number" && Number.isFinite(x))) {
579
+ throw new error.MastraError({
580
+ id: storage.createVectorErrorId("LIBSQL", "QUERY", "INVALID_ARGS"),
581
+ domain: error.ErrorDomain.STORAGE,
582
+ category: error.ErrorCategory.USER,
583
+ details: { message: "queryVector must be an array of finite numbers" }
584
+ });
592
585
  }
593
586
  try {
594
587
  const parsedIndexName = utils.parseSqlIdentifier(indexName, "index name");
@@ -648,6 +641,7 @@ var LibSQLVector = class extends vector.MastraVector {
648
641
  }
649
642
  }
650
643
  async doUpsert({ indexName, vectors, metadata, ids }) {
644
+ vector.validateUpsertInput("LIBSQL", vectors, metadata, ids);
651
645
  const tx = await this.turso.transaction("write");
652
646
  try {
653
647
  const parsedIndexName = utils.parseSqlIdentifier(indexName, "index name");
@@ -1094,833 +1088,1347 @@ var LibSQLVector = class extends vector.MastraVector {
1094
1088
  });
1095
1089
  }
1096
1090
  };
1097
- var AgentsLibSQL = class extends storage.AgentsStorage {
1098
- client;
1099
- constructor({ client, operations: _ }) {
1100
- super();
1101
- this.client = client;
1102
- }
1103
- parseJson(value, fieldName) {
1104
- if (!value) return void 0;
1105
- if (typeof value !== "string") return value;
1106
- try {
1107
- return JSON.parse(value);
1108
- } catch (error$1) {
1109
- const details = {
1110
- value: value.length > 100 ? value.substring(0, 100) + "..." : value
1111
- };
1112
- if (fieldName) {
1113
- details.field = fieldName;
1091
+ function buildSelectColumns(tableName) {
1092
+ const schema = storage.TABLE_SCHEMAS[tableName];
1093
+ return Object.keys(schema).map((col) => {
1094
+ const colDef = schema[col];
1095
+ const parsedCol = utils.parseSqlIdentifier(col, "column name");
1096
+ return colDef?.type === "jsonb" ? `json(${parsedCol}) as ${parsedCol}` : parsedCol;
1097
+ }).join(", ");
1098
+ }
1099
+ function isLockError(error) {
1100
+ return error.code === "SQLITE_BUSY" || error.code === "SQLITE_LOCKED" || error.message?.toLowerCase().includes("database is locked") || error.message?.toLowerCase().includes("database table is locked") || error.message?.toLowerCase().includes("table is locked") || error.constructor.name === "SqliteError" && error.message?.toLowerCase().includes("locked");
1101
+ }
1102
+ function createExecuteWriteOperationWithRetry({
1103
+ logger,
1104
+ maxRetries,
1105
+ initialBackoffMs
1106
+ }) {
1107
+ return async function executeWriteOperationWithRetry(operationFn, operationDescription) {
1108
+ let attempts = 0;
1109
+ let backoff = initialBackoffMs;
1110
+ while (attempts < maxRetries) {
1111
+ try {
1112
+ return await operationFn();
1113
+ } catch (error) {
1114
+ logger.debug(`LibSQLStore: Error caught in retry loop for ${operationDescription}`, {
1115
+ errorType: error.constructor.name,
1116
+ errorCode: error.code,
1117
+ errorMessage: error.message,
1118
+ attempts,
1119
+ maxRetries
1120
+ });
1121
+ if (isLockError(error)) {
1122
+ attempts++;
1123
+ if (attempts >= maxRetries) {
1124
+ logger.error(
1125
+ `LibSQLStore: Operation failed after ${maxRetries} attempts due to database lock: ${error.message}`,
1126
+ { error, attempts, maxRetries }
1127
+ );
1128
+ throw error;
1129
+ }
1130
+ logger.warn(
1131
+ `LibSQLStore: Attempt ${attempts} failed due to database lock during ${operationDescription}. Retrying in ${backoff}ms...`,
1132
+ { errorMessage: error.message, attempts, backoff, maxRetries }
1133
+ );
1134
+ await new Promise((resolve) => setTimeout(resolve, backoff));
1135
+ backoff *= 2;
1136
+ } else {
1137
+ logger.error(`LibSQLStore: Non-lock error during ${operationDescription}, not retrying`, { error });
1138
+ throw error;
1139
+ }
1114
1140
  }
1115
- throw new error.MastraError(
1116
- {
1117
- id: storage.createStorageErrorId("LIBSQL", "PARSE_JSON", "INVALID_JSON"),
1118
- domain: error.ErrorDomain.STORAGE,
1119
- category: error.ErrorCategory.SYSTEM,
1120
- text: `Failed to parse JSON${fieldName ? ` for field "${fieldName}"` : ""}: ${error$1 instanceof Error ? error$1.message : "Unknown error"}`,
1121
- details
1122
- },
1123
- error$1
1124
- );
1125
1141
  }
1126
- }
1127
- parseRow(row) {
1128
- return {
1129
- id: row.id,
1130
- name: row.name,
1131
- description: row.description,
1132
- instructions: row.instructions,
1133
- model: this.parseJson(row.model, "model"),
1134
- tools: this.parseJson(row.tools, "tools"),
1135
- defaultOptions: this.parseJson(row.defaultOptions, "defaultOptions"),
1136
- workflows: this.parseJson(row.workflows, "workflows"),
1137
- agents: this.parseJson(row.agents, "agents"),
1138
- inputProcessors: this.parseJson(row.inputProcessors, "inputProcessors"),
1139
- outputProcessors: this.parseJson(row.outputProcessors, "outputProcessors"),
1140
- memory: this.parseJson(row.memory, "memory"),
1141
- scorers: this.parseJson(row.scorers, "scorers"),
1142
- metadata: this.parseJson(row.metadata, "metadata"),
1143
- createdAt: new Date(row.createdAt),
1144
- updatedAt: new Date(row.updatedAt)
1145
- };
1146
- }
1147
- async getAgentById({ id }) {
1148
- try {
1149
- const result = await this.client.execute({
1150
- sql: `SELECT * FROM "${storage.TABLE_AGENTS}" WHERE id = ?`,
1151
- args: [id]
1152
- });
1153
- if (!result.rows || result.rows.length === 0) {
1154
- return null;
1155
- }
1156
- return this.parseRow(result.rows[0]);
1157
- } catch (error$1) {
1158
- throw new error.MastraError(
1159
- {
1160
- id: storage.createStorageErrorId("LIBSQL", "GET_AGENT_BY_ID", "FAILED"),
1161
- domain: error.ErrorDomain.STORAGE,
1162
- category: error.ErrorCategory.THIRD_PARTY,
1163
- details: { agentId: id }
1164
- },
1165
- error$1
1166
- );
1142
+ throw new Error(`LibSQLStore: Unexpected exit from retry loop for ${operationDescription}`);
1143
+ };
1144
+ }
1145
+ function prepareStatement({ tableName, record }) {
1146
+ const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
1147
+ const schema = storage.TABLE_SCHEMAS[tableName];
1148
+ const columnNames = Object.keys(record);
1149
+ const columns = columnNames.map((col) => utils.parseSqlIdentifier(col, "column name"));
1150
+ const values = columnNames.map((col) => {
1151
+ const v = record[col];
1152
+ if (typeof v === `undefined` || v === null) {
1153
+ return null;
1167
1154
  }
1168
- }
1169
- async createAgent({ agent }) {
1170
- try {
1171
- const now = /* @__PURE__ */ new Date();
1172
- const nowIso = now.toISOString();
1173
- await this.client.execute({
1174
- sql: `INSERT INTO "${storage.TABLE_AGENTS}" (id, name, description, instructions, model, tools, "defaultOptions", workflows, agents, "inputProcessors", "outputProcessors", memory, scorers, metadata, "createdAt", "updatedAt")
1175
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
1176
- args: [
1177
- agent.id,
1178
- agent.name,
1179
- agent.description ?? null,
1180
- agent.instructions,
1181
- JSON.stringify(agent.model),
1182
- agent.tools ? JSON.stringify(agent.tools) : null,
1183
- agent.defaultOptions ? JSON.stringify(agent.defaultOptions) : null,
1184
- agent.workflows ? JSON.stringify(agent.workflows) : null,
1185
- agent.agents ? JSON.stringify(agent.agents) : null,
1186
- agent.inputProcessors ? JSON.stringify(agent.inputProcessors) : null,
1187
- agent.outputProcessors ? JSON.stringify(agent.outputProcessors) : null,
1188
- agent.memory ? JSON.stringify(agent.memory) : null,
1189
- agent.scorers ? JSON.stringify(agent.scorers) : null,
1190
- agent.metadata ? JSON.stringify(agent.metadata) : null,
1191
- nowIso,
1192
- nowIso
1193
- ]
1194
- });
1195
- return {
1196
- ...agent,
1197
- createdAt: now,
1198
- updatedAt: now
1199
- };
1200
- } catch (error$1) {
1201
- throw new error.MastraError(
1202
- {
1203
- id: storage.createStorageErrorId("LIBSQL", "CREATE_AGENT", "FAILED"),
1204
- domain: error.ErrorDomain.STORAGE,
1205
- category: error.ErrorCategory.THIRD_PARTY,
1206
- details: { agentId: agent.id }
1207
- },
1208
- error$1
1209
- );
1155
+ const colDef = schema[col];
1156
+ if (colDef?.type === "jsonb") {
1157
+ return JSON.stringify(v);
1210
1158
  }
1211
- }
1212
- async updateAgent({ id, ...updates }) {
1213
- try {
1214
- const existingAgent = await this.getAgentById({ id });
1215
- if (!existingAgent) {
1216
- throw new error.MastraError({
1217
- id: storage.createStorageErrorId("LIBSQL", "UPDATE_AGENT", "NOT_FOUND"),
1218
- domain: error.ErrorDomain.STORAGE,
1219
- category: error.ErrorCategory.USER,
1220
- text: `Agent ${id} not found`,
1221
- details: { agentId: id }
1222
- });
1223
- }
1224
- const setClauses = [];
1225
- const args = [];
1226
- if (updates.name !== void 0) {
1227
- setClauses.push("name = ?");
1228
- args.push(updates.name);
1229
- }
1230
- if (updates.description !== void 0) {
1231
- setClauses.push("description = ?");
1232
- args.push(updates.description);
1233
- }
1234
- if (updates.instructions !== void 0) {
1235
- setClauses.push("instructions = ?");
1236
- args.push(updates.instructions);
1237
- }
1238
- if (updates.model !== void 0) {
1239
- setClauses.push("model = ?");
1240
- args.push(JSON.stringify(updates.model));
1241
- }
1242
- if (updates.tools !== void 0) {
1243
- setClauses.push("tools = ?");
1244
- args.push(JSON.stringify(updates.tools));
1245
- }
1246
- if (updates.defaultOptions !== void 0) {
1247
- setClauses.push('"defaultOptions" = ?');
1248
- args.push(JSON.stringify(updates.defaultOptions));
1249
- }
1250
- if (updates.workflows !== void 0) {
1251
- setClauses.push("workflows = ?");
1252
- args.push(JSON.stringify(updates.workflows));
1253
- }
1254
- if (updates.agents !== void 0) {
1255
- setClauses.push("agents = ?");
1256
- args.push(JSON.stringify(updates.agents));
1257
- }
1258
- if (updates.inputProcessors !== void 0) {
1259
- setClauses.push('"inputProcessors" = ?');
1260
- args.push(JSON.stringify(updates.inputProcessors));
1261
- }
1262
- if (updates.outputProcessors !== void 0) {
1263
- setClauses.push('"outputProcessors" = ?');
1264
- args.push(JSON.stringify(updates.outputProcessors));
1265
- }
1266
- if (updates.memory !== void 0) {
1267
- setClauses.push("memory = ?");
1268
- args.push(JSON.stringify(updates.memory));
1269
- }
1270
- if (updates.scorers !== void 0) {
1271
- setClauses.push("scorers = ?");
1272
- args.push(JSON.stringify(updates.scorers));
1273
- }
1274
- if (updates.metadata !== void 0) {
1275
- const mergedMetadata = { ...existingAgent.metadata, ...updates.metadata };
1276
- setClauses.push("metadata = ?");
1277
- args.push(JSON.stringify(mergedMetadata));
1278
- }
1279
- const now = /* @__PURE__ */ new Date();
1280
- setClauses.push('"updatedAt" = ?');
1281
- args.push(now.toISOString());
1282
- args.push(id);
1283
- if (setClauses.length > 1) {
1284
- await this.client.execute({
1285
- sql: `UPDATE "${storage.TABLE_AGENTS}" SET ${setClauses.join(", ")} WHERE id = ?`,
1286
- args
1287
- });
1288
- }
1289
- const updatedAgent = await this.getAgentById({ id });
1290
- if (!updatedAgent) {
1291
- throw new error.MastraError({
1292
- id: storage.createStorageErrorId("LIBSQL", "UPDATE_AGENT", "NOT_FOUND_AFTER_UPDATE"),
1293
- domain: error.ErrorDomain.STORAGE,
1294
- category: error.ErrorCategory.SYSTEM,
1295
- text: `Agent ${id} not found after update`,
1296
- details: { agentId: id }
1297
- });
1298
- }
1299
- return updatedAgent;
1300
- } catch (error$1) {
1301
- if (error$1 instanceof error.MastraError) {
1302
- throw error$1;
1303
- }
1304
- throw new error.MastraError(
1305
- {
1306
- id: storage.createStorageErrorId("LIBSQL", "UPDATE_AGENT", "FAILED"),
1307
- domain: error.ErrorDomain.STORAGE,
1308
- category: error.ErrorCategory.THIRD_PARTY,
1309
- details: { agentId: id }
1310
- },
1311
- error$1
1312
- );
1159
+ if (v instanceof Date) {
1160
+ return v.toISOString();
1161
+ }
1162
+ return typeof v === "object" ? JSON.stringify(v) : v;
1163
+ });
1164
+ const placeholders = columnNames.map((col) => {
1165
+ const colDef = schema[col];
1166
+ return colDef?.type === "jsonb" ? "jsonb(?)" : "?";
1167
+ }).join(", ");
1168
+ return {
1169
+ sql: `INSERT OR REPLACE INTO ${parsedTableName} (${columns.join(", ")}) VALUES (${placeholders})`,
1170
+ args: values
1171
+ };
1172
+ }
1173
+ function prepareUpdateStatement({
1174
+ tableName,
1175
+ updates,
1176
+ keys
1177
+ }) {
1178
+ const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
1179
+ const schema = storage.TABLE_SCHEMAS[tableName];
1180
+ const updateColumnNames = Object.keys(updates);
1181
+ const updateColumns = updateColumnNames.map((col) => utils.parseSqlIdentifier(col, "column name"));
1182
+ const updateValues = updateColumnNames.map((col) => {
1183
+ const colDef = schema[col];
1184
+ const v = updates[col];
1185
+ if (colDef?.type === "jsonb") {
1186
+ return transformToSqlValue(v, true);
1313
1187
  }
1188
+ return transformToSqlValue(v, false);
1189
+ });
1190
+ const setClause = updateColumns.map((col, i) => {
1191
+ const colDef = schema[updateColumnNames[i]];
1192
+ return colDef?.type === "jsonb" ? `${col} = jsonb(?)` : `${col} = ?`;
1193
+ }).join(", ");
1194
+ const whereClause = prepareWhereClause(keys, schema);
1195
+ return {
1196
+ sql: `UPDATE ${parsedTableName} SET ${setClause}${whereClause.sql}`,
1197
+ args: [...updateValues, ...whereClause.args]
1198
+ };
1199
+ }
1200
+ function transformToSqlValue(value, forceJsonStringify = false) {
1201
+ if (typeof value === "undefined" || value === null) {
1202
+ return null;
1314
1203
  }
1315
- async deleteAgent({ id }) {
1316
- try {
1317
- await this.client.execute({
1318
- sql: `DELETE FROM "${storage.TABLE_AGENTS}" WHERE id = ?`,
1319
- args: [id]
1320
- });
1321
- } catch (error$1) {
1322
- throw new error.MastraError(
1323
- {
1324
- id: storage.createStorageErrorId("LIBSQL", "DELETE_AGENT", "FAILED"),
1325
- domain: error.ErrorDomain.STORAGE,
1326
- category: error.ErrorCategory.THIRD_PARTY,
1327
- details: { agentId: id }
1328
- },
1329
- error$1
1330
- );
1204
+ if (forceJsonStringify) {
1205
+ return JSON.stringify(value);
1206
+ }
1207
+ if (value instanceof Date) {
1208
+ return value.toISOString();
1209
+ }
1210
+ return typeof value === "object" ? JSON.stringify(value) : value;
1211
+ }
1212
+ function prepareDeleteStatement({ tableName, keys }) {
1213
+ const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
1214
+ const whereClause = prepareWhereClause(keys, storage.TABLE_SCHEMAS[tableName]);
1215
+ return {
1216
+ sql: `DELETE FROM ${parsedTableName}${whereClause.sql}`,
1217
+ args: whereClause.args
1218
+ };
1219
+ }
1220
+ function prepareWhereClause(filters, schema) {
1221
+ const conditions = [];
1222
+ const args = [];
1223
+ for (const [columnName, filterValue] of Object.entries(filters)) {
1224
+ const column = schema[columnName];
1225
+ if (!column) {
1226
+ throw new Error(`Unknown column: ${columnName}`);
1331
1227
  }
1228
+ const parsedColumn = utils.parseSqlIdentifier(columnName, "column name");
1229
+ const result = buildCondition2(parsedColumn, filterValue);
1230
+ conditions.push(result.condition);
1231
+ args.push(...result.args);
1332
1232
  }
1333
- async listAgents(args) {
1334
- const { page = 0, perPage: perPageInput, orderBy } = args || {};
1335
- const { field, direction } = this.parseOrderBy(orderBy);
1336
- if (page < 0) {
1233
+ return {
1234
+ sql: conditions.length > 0 ? ` WHERE ${conditions.join(" AND ")}` : "",
1235
+ args
1236
+ };
1237
+ }
1238
+ function buildCondition2(columnName, filterValue) {
1239
+ if (filterValue === null) {
1240
+ return { condition: `${columnName} IS NULL`, args: [] };
1241
+ }
1242
+ if (typeof filterValue === "object" && filterValue !== null && ("startAt" in filterValue || "endAt" in filterValue)) {
1243
+ return buildDateRangeCondition(columnName, filterValue);
1244
+ }
1245
+ return {
1246
+ condition: `${columnName} = ?`,
1247
+ args: [transformToSqlValue(filterValue)]
1248
+ };
1249
+ }
1250
+ function buildDateRangeCondition(columnName, range) {
1251
+ const conditions = [];
1252
+ const args = [];
1253
+ if (range.startAt !== void 0) {
1254
+ conditions.push(`${columnName} >= ?`);
1255
+ args.push(transformToSqlValue(range.startAt));
1256
+ }
1257
+ if (range.endAt !== void 0) {
1258
+ conditions.push(`${columnName} <= ?`);
1259
+ args.push(transformToSqlValue(range.endAt));
1260
+ }
1261
+ if (conditions.length === 0) {
1262
+ throw new Error("Date range must specify at least startAt or endAt");
1263
+ }
1264
+ return {
1265
+ condition: conditions.join(" AND "),
1266
+ args
1267
+ };
1268
+ }
1269
+ function transformFromSqlRow({
1270
+ tableName,
1271
+ sqlRow
1272
+ }) {
1273
+ const result = {};
1274
+ const jsonColumns = new Set(
1275
+ Object.keys(storage.TABLE_SCHEMAS[tableName]).filter((key) => storage.TABLE_SCHEMAS[tableName][key].type === "jsonb").map((key) => key)
1276
+ );
1277
+ const dateColumns = new Set(
1278
+ Object.keys(storage.TABLE_SCHEMAS[tableName]).filter((key) => storage.TABLE_SCHEMAS[tableName][key].type === "timestamp").map((key) => key)
1279
+ );
1280
+ for (const [key, value] of Object.entries(sqlRow)) {
1281
+ if (value === null || value === void 0) {
1282
+ result[key] = value;
1283
+ continue;
1284
+ }
1285
+ if (dateColumns.has(key) && typeof value === "string") {
1286
+ result[key] = new Date(value);
1287
+ continue;
1288
+ }
1289
+ if (jsonColumns.has(key) && typeof value === "string") {
1290
+ result[key] = storage.safelyParseJSON(value);
1291
+ continue;
1292
+ }
1293
+ result[key] = value;
1294
+ }
1295
+ return result;
1296
+ }
1297
+
1298
+ // src/storage/db/index.ts
1299
+ function resolveClient(config) {
1300
+ if ("client" in config) {
1301
+ return config.client;
1302
+ }
1303
+ return client.createClient({
1304
+ url: config.url,
1305
+ ...config.authToken ? { authToken: config.authToken } : {}
1306
+ });
1307
+ }
1308
+ var LibSQLDB = class extends base.MastraBase {
1309
+ client;
1310
+ maxRetries;
1311
+ initialBackoffMs;
1312
+ executeWriteOperationWithRetry;
1313
+ constructor({
1314
+ client,
1315
+ maxRetries,
1316
+ initialBackoffMs
1317
+ }) {
1318
+ super({
1319
+ component: "STORAGE",
1320
+ name: "LIBSQL_DB_LAYER"
1321
+ });
1322
+ this.client = client;
1323
+ this.maxRetries = maxRetries ?? 5;
1324
+ this.initialBackoffMs = initialBackoffMs ?? 100;
1325
+ this.executeWriteOperationWithRetry = createExecuteWriteOperationWithRetry({
1326
+ logger: this.logger,
1327
+ maxRetries: this.maxRetries,
1328
+ initialBackoffMs: this.initialBackoffMs
1329
+ });
1330
+ }
1331
+ /**
1332
+ * Checks if a column exists in the specified table.
1333
+ *
1334
+ * @param table - The name of the table to check
1335
+ * @param column - The name of the column to look for
1336
+ * @returns `true` if the column exists in the table, `false` otherwise
1337
+ */
1338
+ async hasColumn(table, column) {
1339
+ const sanitizedTable = utils.parseSqlIdentifier(table, "table name");
1340
+ const result = await this.client.execute({
1341
+ sql: `PRAGMA table_info("${sanitizedTable}")`
1342
+ });
1343
+ return result.rows?.some((row) => row.name === column);
1344
+ }
1345
+ /**
1346
+ * Internal insert implementation without retry logic.
1347
+ */
1348
+ async doInsert({
1349
+ tableName,
1350
+ record
1351
+ }) {
1352
+ await this.client.execute(
1353
+ prepareStatement({
1354
+ tableName,
1355
+ record
1356
+ })
1357
+ );
1358
+ }
1359
+ /**
1360
+ * Inserts or replaces a record in the specified table with automatic retry on lock errors.
1361
+ *
1362
+ * @param args - The insert arguments
1363
+ * @param args.tableName - The name of the table to insert into
1364
+ * @param args.record - The record to insert (key-value pairs)
1365
+ */
1366
+ insert(args) {
1367
+ return this.executeWriteOperationWithRetry(() => this.doInsert(args), `insert into table ${args.tableName}`);
1368
+ }
1369
+ /**
1370
+ * Internal update implementation without retry logic.
1371
+ */
1372
+ async doUpdate({
1373
+ tableName,
1374
+ keys,
1375
+ data
1376
+ }) {
1377
+ await this.client.execute(prepareUpdateStatement({ tableName, updates: data, keys }));
1378
+ }
1379
+ /**
1380
+ * Updates a record in the specified table with automatic retry on lock errors.
1381
+ *
1382
+ * @param args - The update arguments
1383
+ * @param args.tableName - The name of the table to update
1384
+ * @param args.keys - The key(s) identifying the record to update
1385
+ * @param args.data - The fields to update (key-value pairs)
1386
+ */
1387
+ update(args) {
1388
+ return this.executeWriteOperationWithRetry(() => this.doUpdate(args), `update table ${args.tableName}`);
1389
+ }
1390
+ /**
1391
+ * Internal batch insert implementation without retry logic.
1392
+ */
1393
+ async doBatchInsert({
1394
+ tableName,
1395
+ records
1396
+ }) {
1397
+ if (records.length === 0) return;
1398
+ const batchStatements = records.map((r) => prepareStatement({ tableName, record: r }));
1399
+ await this.client.batch(batchStatements, "write");
1400
+ }
1401
+ /**
1402
+ * Inserts multiple records in a single batch transaction with automatic retry on lock errors.
1403
+ *
1404
+ * @param args - The batch insert arguments
1405
+ * @param args.tableName - The name of the table to insert into
1406
+ * @param args.records - Array of records to insert
1407
+ * @throws {MastraError} When the batch insert fails after retries
1408
+ */
1409
+ async batchInsert(args) {
1410
+ return this.executeWriteOperationWithRetry(
1411
+ () => this.doBatchInsert(args),
1412
+ `batch insert into table ${args.tableName}`
1413
+ ).catch((error$1) => {
1337
1414
  throw new error.MastraError(
1338
1415
  {
1339
- id: storage.createStorageErrorId("LIBSQL", "LIST_AGENTS", "INVALID_PAGE"),
1416
+ id: storage.createStorageErrorId("LIBSQL", "BATCH_INSERT", "FAILED"),
1340
1417
  domain: error.ErrorDomain.STORAGE,
1341
- category: error.ErrorCategory.USER,
1342
- details: { page }
1418
+ category: error.ErrorCategory.THIRD_PARTY,
1419
+ details: {
1420
+ tableName: args.tableName
1421
+ }
1343
1422
  },
1344
- new Error("page must be >= 0")
1423
+ error$1
1424
+ );
1425
+ });
1426
+ }
1427
+ /**
1428
+ * Internal batch update implementation without retry logic.
1429
+ * Each record can be updated based on single or composite keys.
1430
+ */
1431
+ async doBatchUpdate({
1432
+ tableName,
1433
+ updates
1434
+ }) {
1435
+ if (updates.length === 0) return;
1436
+ const batchStatements = updates.map(
1437
+ ({ keys, data }) => prepareUpdateStatement({
1438
+ tableName,
1439
+ updates: data,
1440
+ keys
1441
+ })
1442
+ );
1443
+ await this.client.batch(batchStatements, "write");
1444
+ }
1445
+ /**
1446
+ * Updates multiple records in a single batch transaction with automatic retry on lock errors.
1447
+ * Each record can be updated based on single or composite keys.
1448
+ *
1449
+ * @param args - The batch update arguments
1450
+ * @param args.tableName - The name of the table to update
1451
+ * @param args.updates - Array of update operations, each containing keys and data
1452
+ * @throws {MastraError} When the batch update fails after retries
1453
+ */
1454
+ async batchUpdate(args) {
1455
+ return this.executeWriteOperationWithRetry(
1456
+ () => this.doBatchUpdate(args),
1457
+ `batch update in table ${args.tableName}`
1458
+ ).catch((error$1) => {
1459
+ throw new error.MastraError(
1460
+ {
1461
+ id: storage.createStorageErrorId("LIBSQL", "BATCH_UPDATE", "FAILED"),
1462
+ domain: error.ErrorDomain.STORAGE,
1463
+ category: error.ErrorCategory.THIRD_PARTY,
1464
+ details: {
1465
+ tableName: args.tableName
1466
+ }
1467
+ },
1468
+ error$1
1469
+ );
1470
+ });
1471
+ }
1472
+ /**
1473
+ * Internal batch delete implementation without retry logic.
1474
+ * Each record can be deleted based on single or composite keys.
1475
+ */
1476
+ async doBatchDelete({
1477
+ tableName,
1478
+ keys
1479
+ }) {
1480
+ if (keys.length === 0) return;
1481
+ const batchStatements = keys.map(
1482
+ (keyObj) => prepareDeleteStatement({
1483
+ tableName,
1484
+ keys: keyObj
1485
+ })
1486
+ );
1487
+ await this.client.batch(batchStatements, "write");
1488
+ }
1489
+ /**
1490
+ * Deletes multiple records in a single batch transaction with automatic retry on lock errors.
1491
+ * Each record can be deleted based on single or composite keys.
1492
+ *
1493
+ * @param args - The batch delete arguments
1494
+ * @param args.tableName - The name of the table to delete from
1495
+ * @param args.keys - Array of key objects identifying records to delete
1496
+ * @throws {MastraError} When the batch delete fails after retries
1497
+ */
1498
+ async batchDelete({
1499
+ tableName,
1500
+ keys
1501
+ }) {
1502
+ return this.executeWriteOperationWithRetry(
1503
+ () => this.doBatchDelete({ tableName, keys }),
1504
+ `batch delete from table ${tableName}`
1505
+ ).catch((error$1) => {
1506
+ throw new error.MastraError(
1507
+ {
1508
+ id: storage.createStorageErrorId("LIBSQL", "BATCH_DELETE", "FAILED"),
1509
+ domain: error.ErrorDomain.STORAGE,
1510
+ category: error.ErrorCategory.THIRD_PARTY,
1511
+ details: {
1512
+ tableName
1513
+ }
1514
+ },
1515
+ error$1
1516
+ );
1517
+ });
1518
+ }
1519
+ /**
1520
+ * Internal single-record delete implementation without retry logic.
1521
+ */
1522
+ async doDelete({ tableName, keys }) {
1523
+ await this.client.execute(prepareDeleteStatement({ tableName, keys }));
1524
+ }
1525
+ /**
1526
+ * Deletes a single record from the specified table with automatic retry on lock errors.
1527
+ *
1528
+ * @param args - The delete arguments
1529
+ * @param args.tableName - The name of the table to delete from
1530
+ * @param args.keys - The key(s) identifying the record to delete
1531
+ * @throws {MastraError} When the delete fails after retries
1532
+ */
1533
+ async delete(args) {
1534
+ return this.executeWriteOperationWithRetry(() => this.doDelete(args), `delete from table ${args.tableName}`).catch(
1535
+ (error$1) => {
1536
+ throw new error.MastraError(
1537
+ {
1538
+ id: storage.createStorageErrorId("LIBSQL", "DELETE", "FAILED"),
1539
+ domain: error.ErrorDomain.STORAGE,
1540
+ category: error.ErrorCategory.THIRD_PARTY,
1541
+ details: {
1542
+ tableName: args.tableName
1543
+ }
1544
+ },
1545
+ error$1
1546
+ );
1547
+ }
1548
+ );
1549
+ }
1550
+ /**
1551
+ * Selects a single record from the specified table by key(s).
1552
+ * Returns the most recently created record if multiple matches exist.
1553
+ * Automatically parses JSON string values back to objects/arrays.
1554
+ *
1555
+ * @typeParam R - The expected return type of the record
1556
+ * @param args - The select arguments
1557
+ * @param args.tableName - The name of the table to select from
1558
+ * @param args.keys - The key(s) identifying the record to select
1559
+ * @returns The matching record or `null` if not found
1560
+ */
1561
+ async select({ tableName, keys }) {
1562
+ const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
1563
+ const columns = buildSelectColumns(tableName);
1564
+ const parsedKeys = Object.keys(keys).map((key) => utils.parseSqlIdentifier(key, "column name"));
1565
+ const conditions = parsedKeys.map((key) => `${key} = ?`).join(" AND ");
1566
+ const values = Object.values(keys);
1567
+ const result = await this.client.execute({
1568
+ sql: `SELECT ${columns} FROM ${parsedTableName} WHERE ${conditions} ORDER BY createdAt DESC LIMIT 1`,
1569
+ args: values
1570
+ });
1571
+ if (!result.rows || result.rows.length === 0) {
1572
+ return null;
1573
+ }
1574
+ const row = result.rows[0];
1575
+ const parsed = Object.fromEntries(
1576
+ Object.entries(row || {}).map(([k, v]) => {
1577
+ try {
1578
+ return [k, typeof v === "string" ? v.startsWith("{") || v.startsWith("[") ? JSON.parse(v) : v : v];
1579
+ } catch {
1580
+ return [k, v];
1581
+ }
1582
+ })
1583
+ );
1584
+ return parsed;
1585
+ }
1586
+ /**
1587
+ * Selects multiple records from the specified table with optional filtering, ordering, and pagination.
1588
+ *
1589
+ * @typeParam R - The expected return type of each record
1590
+ * @param args - The select arguments
1591
+ * @param args.tableName - The name of the table to select from
1592
+ * @param args.whereClause - Optional WHERE clause with SQL string and arguments
1593
+ * @param args.orderBy - Optional ORDER BY clause (e.g., "createdAt DESC")
1594
+ * @param args.offset - Optional offset for pagination
1595
+ * @param args.limit - Optional limit for pagination
1596
+ * @param args.args - Optional additional query arguments
1597
+ * @returns Array of matching records
1598
+ */
1599
+ async selectMany({
1600
+ tableName,
1601
+ whereClause,
1602
+ orderBy,
1603
+ offset,
1604
+ limit,
1605
+ args
1606
+ }) {
1607
+ const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
1608
+ const columns = buildSelectColumns(tableName);
1609
+ let statement = `SELECT ${columns} FROM ${parsedTableName}`;
1610
+ if (whereClause?.sql) {
1611
+ statement += ` ${whereClause.sql}`;
1612
+ }
1613
+ if (orderBy) {
1614
+ statement += ` ORDER BY ${orderBy}`;
1615
+ }
1616
+ if (limit) {
1617
+ statement += ` LIMIT ${limit}`;
1618
+ }
1619
+ if (offset) {
1620
+ statement += ` OFFSET ${offset}`;
1621
+ }
1622
+ const result = await this.client.execute({
1623
+ sql: statement,
1624
+ args: [...whereClause?.args ?? [], ...args ?? []]
1625
+ });
1626
+ return (result.rows ?? []).map((row) => {
1627
+ return Object.fromEntries(
1628
+ Object.entries(row || {}).map(([k, v]) => {
1629
+ try {
1630
+ return [k, typeof v === "string" ? v.startsWith("{") || v.startsWith("[") ? JSON.parse(v) : v : v];
1631
+ } catch {
1632
+ return [k, v];
1633
+ }
1634
+ })
1345
1635
  );
1636
+ });
1637
+ }
1638
+ /**
1639
+ * Returns the total count of records matching the optional WHERE clause.
1640
+ *
1641
+ * @param args - The count arguments
1642
+ * @param args.tableName - The name of the table to count from
1643
+ * @param args.whereClause - Optional WHERE clause with SQL string and arguments
1644
+ * @returns The total count of matching records
1645
+ */
1646
+ async selectTotalCount({
1647
+ tableName,
1648
+ whereClause
1649
+ }) {
1650
+ const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
1651
+ const statement = `SELECT COUNT(*) as count FROM ${parsedTableName} ${whereClause ? `${whereClause.sql}` : ""}`;
1652
+ const result = await this.client.execute({
1653
+ sql: statement,
1654
+ args: whereClause?.args ?? []
1655
+ });
1656
+ if (!result.rows || result.rows.length === 0) {
1657
+ return 0;
1346
1658
  }
1347
- const perPage = storage.normalizePerPage(perPageInput, 100);
1348
- const { offset, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
1659
+ return result.rows[0]?.count ?? 0;
1660
+ }
1661
+ /**
1662
+ * Maps a storage column type to its SQLite equivalent.
1663
+ */
1664
+ getSqlType(type) {
1665
+ switch (type) {
1666
+ case "bigint":
1667
+ return "INTEGER";
1668
+ // SQLite uses INTEGER for all integer sizes
1669
+ case "timestamp":
1670
+ return "TEXT";
1671
+ // Store timestamps as ISO strings in SQLite
1672
+ case "float":
1673
+ return "REAL";
1674
+ // SQLite's floating point type
1675
+ case "boolean":
1676
+ return "INTEGER";
1677
+ // SQLite uses 0/1 for booleans
1678
+ case "jsonb":
1679
+ return "TEXT";
1680
+ // SQLite: column stores TEXT, we use jsonb()/json() functions for binary optimization
1681
+ default:
1682
+ return storage.getSqlType(type);
1683
+ }
1684
+ }
1685
+ /**
1686
+ * Creates a table if it doesn't exist based on the provided schema.
1687
+ *
1688
+ * @param args - The create table arguments
1689
+ * @param args.tableName - The name of the table to create
1690
+ * @param args.schema - The schema definition for the table columns
1691
+ */
1692
+ async createTable({
1693
+ tableName,
1694
+ schema
1695
+ }) {
1349
1696
  try {
1350
- const countResult = await this.client.execute({
1351
- sql: `SELECT COUNT(*) as count FROM "${storage.TABLE_AGENTS}"`,
1352
- args: []
1697
+ const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
1698
+ const columnDefinitions = Object.entries(schema).map(([colName, colDef]) => {
1699
+ const type = this.getSqlType(colDef.type);
1700
+ const nullable = colDef.nullable === false ? "NOT NULL" : "";
1701
+ const primaryKey = colDef.primaryKey ? "PRIMARY KEY" : "";
1702
+ return `"${colName}" ${type} ${nullable} ${primaryKey}`.trim();
1353
1703
  });
1354
- const total = Number(countResult.rows?.[0]?.count ?? 0);
1355
- if (total === 0) {
1356
- return {
1357
- agents: [],
1358
- total: 0,
1359
- page,
1360
- perPage: perPageForResponse,
1361
- hasMore: false
1362
- };
1704
+ const tableConstraints = [];
1705
+ if (tableName === storage.TABLE_WORKFLOW_SNAPSHOT) {
1706
+ tableConstraints.push("UNIQUE (workflow_name, run_id)");
1707
+ }
1708
+ if (tableName === storage.TABLE_SPANS) {
1709
+ tableConstraints.push("UNIQUE (spanId, traceId)");
1710
+ }
1711
+ const allDefinitions = [...columnDefinitions, ...tableConstraints].join(",\n ");
1712
+ const sql = `CREATE TABLE IF NOT EXISTS ${parsedTableName} (
1713
+ ${allDefinitions}
1714
+ )`;
1715
+ await this.client.execute(sql);
1716
+ this.logger.debug(`LibSQLDB: Created table ${tableName}`);
1717
+ if (tableName === storage.TABLE_SPANS) {
1718
+ await this.migrateSpansTable();
1363
1719
  }
1364
- const limitValue = perPageInput === false ? total : perPage;
1365
- const dataResult = await this.client.execute({
1366
- sql: `SELECT * FROM "${storage.TABLE_AGENTS}" ORDER BY "${field}" ${direction} LIMIT ? OFFSET ?`,
1367
- args: [limitValue, offset]
1368
- });
1369
- const agents = (dataResult.rows || []).map((row) => this.parseRow(row));
1370
- return {
1371
- agents,
1372
- total,
1373
- page,
1374
- perPage: perPageForResponse,
1375
- hasMore: perPageInput === false ? false : offset + perPage < total
1376
- };
1377
1720
  } catch (error$1) {
1721
+ if (error$1 instanceof error.MastraError) {
1722
+ throw error$1;
1723
+ }
1378
1724
  throw new error.MastraError(
1379
1725
  {
1380
- id: storage.createStorageErrorId("LIBSQL", "LIST_AGENTS", "FAILED"),
1726
+ id: storage.createStorageErrorId("LIBSQL", "CREATE_TABLE", "FAILED"),
1381
1727
  domain: error.ErrorDomain.STORAGE,
1382
- category: error.ErrorCategory.THIRD_PARTY
1728
+ category: error.ErrorCategory.THIRD_PARTY,
1729
+ details: { tableName }
1383
1730
  },
1384
1731
  error$1
1385
1732
  );
1386
1733
  }
1387
1734
  }
1388
- };
1389
- var MemoryLibSQL = class extends storage.MemoryStorage {
1390
- client;
1391
- operations;
1392
- constructor({ client, operations }) {
1393
- super();
1394
- this.client = client;
1395
- this.operations = operations;
1735
+ /**
1736
+ * Migrates the spans table schema from OLD_SPAN_SCHEMA to current SPAN_SCHEMA.
1737
+ * This adds new columns that don't exist in old schema and ensures required indexes exist.
1738
+ */
1739
+ async migrateSpansTable() {
1740
+ const schema = storage.TABLE_SCHEMAS[storage.TABLE_SPANS];
1741
+ try {
1742
+ for (const [columnName, columnDef] of Object.entries(schema)) {
1743
+ const columnExists = await this.hasColumn(storage.TABLE_SPANS, columnName);
1744
+ if (!columnExists) {
1745
+ const sqlType = this.getSqlType(columnDef.type);
1746
+ const alterSql = `ALTER TABLE "${storage.TABLE_SPANS}" ADD COLUMN "${columnName}" ${sqlType}`;
1747
+ await this.client.execute(alterSql);
1748
+ this.logger.debug(`LibSQLDB: Added column '${columnName}' to ${storage.TABLE_SPANS}`);
1749
+ }
1750
+ }
1751
+ const indexExists = await this.spansUniqueIndexExists();
1752
+ if (!indexExists) {
1753
+ const duplicateInfo = await this.checkForDuplicateSpans();
1754
+ if (duplicateInfo.hasDuplicates) {
1755
+ const errorMessage = `
1756
+ ===========================================================================
1757
+ MIGRATION REQUIRED: Duplicate spans detected in ${storage.TABLE_SPANS}
1758
+ ===========================================================================
1759
+
1760
+ Found ${duplicateInfo.duplicateCount} duplicate (traceId, spanId) combinations.
1761
+
1762
+ The spans table requires a unique constraint on (traceId, spanId), but your
1763
+ database contains duplicate entries that must be resolved first.
1764
+
1765
+ To fix this, run the manual migration command:
1766
+
1767
+ npx mastra migrate
1768
+
1769
+ This command will:
1770
+ 1. Remove duplicate spans (keeping the most complete/recent version)
1771
+ 2. Add the required unique constraint
1772
+
1773
+ Note: This migration may take some time for large tables.
1774
+ ===========================================================================
1775
+ `;
1776
+ throw new error.MastraError({
1777
+ id: storage.createStorageErrorId("LIBSQL", "MIGRATION_REQUIRED", "DUPLICATE_SPANS"),
1778
+ domain: error.ErrorDomain.STORAGE,
1779
+ category: error.ErrorCategory.USER,
1780
+ text: errorMessage
1781
+ });
1782
+ } else {
1783
+ await this.client.execute(
1784
+ `CREATE UNIQUE INDEX IF NOT EXISTS "mastra_ai_spans_spanid_traceid_idx" ON "${storage.TABLE_SPANS}" ("spanId", "traceId")`
1785
+ );
1786
+ this.logger.debug(`LibSQLDB: Created unique index on (spanId, traceId) for ${storage.TABLE_SPANS}`);
1787
+ }
1788
+ }
1789
+ this.logger.info(`LibSQLDB: Migration completed for ${storage.TABLE_SPANS}`);
1790
+ } catch (error$1) {
1791
+ if (error$1 instanceof error.MastraError) {
1792
+ throw error$1;
1793
+ }
1794
+ this.logger.warn(`LibSQLDB: Failed to migrate spans table ${storage.TABLE_SPANS}:`, error$1);
1795
+ }
1396
1796
  }
1397
- parseRow(row) {
1398
- let content = row.content;
1797
+ /**
1798
+ * Checks if the unique index on (spanId, traceId) already exists on the spans table.
1799
+ * Used to skip deduplication when the index already exists (migration already complete).
1800
+ */
1801
+ async spansUniqueIndexExists() {
1399
1802
  try {
1400
- content = JSON.parse(row.content);
1803
+ const result = await this.client.execute(
1804
+ `SELECT 1 FROM sqlite_master WHERE type = 'index' AND name = 'mastra_ai_spans_spanid_traceid_idx'`
1805
+ );
1806
+ return (result.rows?.length ?? 0) > 0;
1401
1807
  } catch {
1808
+ return false;
1402
1809
  }
1403
- const result = {
1404
- id: row.id,
1405
- content,
1406
- role: row.role,
1407
- createdAt: new Date(row.createdAt),
1408
- threadId: row.thread_id,
1409
- resourceId: row.resourceId
1410
- };
1411
- if (row.type && row.type !== `v2`) result.type = row.type;
1412
- return result;
1413
1810
  }
1414
- async _getIncludedMessages({ include }) {
1415
- if (!include || include.length === 0) return null;
1416
- const unionQueries = [];
1417
- const params = [];
1418
- for (const inc of include) {
1419
- const { id, withPreviousMessages = 0, withNextMessages = 0 } = inc;
1420
- unionQueries.push(
1421
- `
1422
- SELECT * FROM (
1423
- WITH target_thread AS (
1424
- SELECT thread_id FROM "${storage.TABLE_MESSAGES}" WHERE id = ?
1425
- ),
1426
- numbered_messages AS (
1427
- SELECT
1428
- id, content, role, type, "createdAt", thread_id, "resourceId",
1429
- ROW_NUMBER() OVER (ORDER BY "createdAt" ASC) as row_num
1430
- FROM "${storage.TABLE_MESSAGES}"
1431
- WHERE thread_id = (SELECT thread_id FROM target_thread)
1432
- ),
1433
- target_positions AS (
1434
- SELECT row_num as target_pos
1435
- FROM numbered_messages
1436
- WHERE id = ?
1437
- )
1438
- SELECT DISTINCT m.*
1439
- FROM numbered_messages m
1440
- CROSS JOIN target_positions t
1441
- WHERE m.row_num BETWEEN (t.target_pos - ?) AND (t.target_pos + ?)
1442
- )
1443
- `
1444
- // Keep ASC for final sorting after fetching context
1811
+ /**
1812
+ * Checks for duplicate (traceId, spanId) combinations in the spans table.
1813
+ * Returns information about duplicates for logging/CLI purposes.
1814
+ */
1815
+ async checkForDuplicateSpans() {
1816
+ try {
1817
+ const result = await this.client.execute(`
1818
+ SELECT COUNT(*) as duplicate_count FROM (
1819
+ SELECT "spanId", "traceId"
1820
+ FROM "${storage.TABLE_SPANS}"
1821
+ GROUP BY "spanId", "traceId"
1822
+ HAVING COUNT(*) > 1
1823
+ )
1824
+ `);
1825
+ const duplicateCount = Number(result.rows?.[0]?.duplicate_count ?? 0);
1826
+ return {
1827
+ hasDuplicates: duplicateCount > 0,
1828
+ duplicateCount
1829
+ };
1830
+ } catch (error) {
1831
+ this.logger.debug(`LibSQLDB: Could not check for duplicates: ${error}`);
1832
+ return { hasDuplicates: false, duplicateCount: 0 };
1833
+ }
1834
+ }
1835
+ /**
1836
+ * Manually run the spans migration to deduplicate and add the unique constraint.
1837
+ * This is intended to be called from the CLI when duplicates are detected.
1838
+ *
1839
+ * @returns Migration result with status and details
1840
+ */
1841
+ async migrateSpans() {
1842
+ const indexExists = await this.spansUniqueIndexExists();
1843
+ if (indexExists) {
1844
+ return {
1845
+ success: true,
1846
+ alreadyMigrated: true,
1847
+ duplicatesRemoved: 0,
1848
+ message: `Migration already complete. Unique index exists on ${storage.TABLE_SPANS}.`
1849
+ };
1850
+ }
1851
+ const duplicateInfo = await this.checkForDuplicateSpans();
1852
+ if (duplicateInfo.hasDuplicates) {
1853
+ this.logger.info(
1854
+ `Found ${duplicateInfo.duplicateCount} duplicate (traceId, spanId) combinations. Starting deduplication...`
1445
1855
  );
1446
- params.push(id, id, withPreviousMessages, withNextMessages);
1856
+ await this.deduplicateSpans();
1857
+ } else {
1858
+ this.logger.info(`No duplicate spans found.`);
1447
1859
  }
1448
- const finalQuery = unionQueries.join(" UNION ALL ") + ' ORDER BY "createdAt" ASC';
1449
- const includedResult = await this.client.execute({ sql: finalQuery, args: params });
1450
- const includedRows = includedResult.rows?.map((row) => this.parseRow(row));
1451
- const seen = /* @__PURE__ */ new Set();
1452
- const dedupedRows = includedRows.filter((row) => {
1453
- if (seen.has(row.id)) return false;
1454
- seen.add(row.id);
1455
- return true;
1456
- });
1457
- return dedupedRows;
1860
+ await this.client.execute(
1861
+ `CREATE UNIQUE INDEX IF NOT EXISTS "mastra_ai_spans_spanid_traceid_idx" ON "${storage.TABLE_SPANS}" ("spanId", "traceId")`
1862
+ );
1863
+ return {
1864
+ success: true,
1865
+ alreadyMigrated: false,
1866
+ duplicatesRemoved: duplicateInfo.duplicateCount,
1867
+ message: duplicateInfo.hasDuplicates ? `Migration complete. Removed duplicates and added unique index to ${storage.TABLE_SPANS}.` : `Migration complete. Added unique index to ${storage.TABLE_SPANS}.`
1868
+ };
1458
1869
  }
1459
- async listMessagesById({ messageIds }) {
1460
- if (messageIds.length === 0) return { messages: [] };
1870
+ /**
1871
+ * Check migration status for the spans table.
1872
+ * Returns information about whether migration is needed.
1873
+ */
1874
+ async checkSpansMigrationStatus() {
1875
+ const indexExists = await this.spansUniqueIndexExists();
1876
+ if (indexExists) {
1877
+ return {
1878
+ needsMigration: false,
1879
+ hasDuplicates: false,
1880
+ duplicateCount: 0,
1881
+ constraintExists: true,
1882
+ tableName: storage.TABLE_SPANS
1883
+ };
1884
+ }
1885
+ const duplicateInfo = await this.checkForDuplicateSpans();
1886
+ return {
1887
+ needsMigration: true,
1888
+ hasDuplicates: duplicateInfo.hasDuplicates,
1889
+ duplicateCount: duplicateInfo.duplicateCount,
1890
+ constraintExists: false,
1891
+ tableName: storage.TABLE_SPANS
1892
+ };
1893
+ }
1894
+ /**
1895
+ * Deduplicates spans table by removing duplicate (spanId, traceId) combinations.
1896
+ * Keeps the "best" record for each duplicate group based on:
1897
+ * 1. Completed spans (endedAt IS NOT NULL) over incomplete ones
1898
+ * 2. Most recently updated (updatedAt DESC)
1899
+ * 3. Most recently created (createdAt DESC) as tiebreaker
1900
+ */
1901
+ async deduplicateSpans() {
1461
1902
  try {
1462
- const sql = `
1463
- SELECT
1464
- id,
1465
- content,
1466
- role,
1467
- type,
1468
- "createdAt",
1469
- thread_id,
1470
- "resourceId"
1471
- FROM "${storage.TABLE_MESSAGES}"
1472
- WHERE id IN (${messageIds.map(() => "?").join(", ")})
1473
- ORDER BY "createdAt" DESC
1474
- `;
1475
- const result = await this.client.execute({ sql, args: messageIds });
1476
- if (!result.rows) return { messages: [] };
1477
- const list = new agent.MessageList().add(result.rows.map(this.parseRow), "memory");
1478
- return { messages: list.get.all.db() };
1903
+ const duplicateCheck = await this.client.execute(`
1904
+ SELECT COUNT(*) as duplicate_count FROM (
1905
+ SELECT "spanId", "traceId"
1906
+ FROM "${storage.TABLE_SPANS}"
1907
+ GROUP BY "spanId", "traceId"
1908
+ HAVING COUNT(*) > 1
1909
+ )
1910
+ `);
1911
+ const duplicateCount = Number(duplicateCheck.rows?.[0]?.duplicate_count ?? 0);
1912
+ if (duplicateCount === 0) {
1913
+ this.logger.debug(`LibSQLDB: No duplicate spans found, skipping deduplication`);
1914
+ return;
1915
+ }
1916
+ this.logger.warn(`LibSQLDB: Found ${duplicateCount} duplicate (spanId, traceId) combinations, deduplicating...`);
1917
+ const deleteResult = await this.client.execute(`
1918
+ DELETE FROM "${storage.TABLE_SPANS}"
1919
+ WHERE rowid NOT IN (
1920
+ SELECT MIN(best_rowid) FROM (
1921
+ SELECT
1922
+ rowid as best_rowid,
1923
+ "spanId",
1924
+ "traceId",
1925
+ ROW_NUMBER() OVER (
1926
+ PARTITION BY "spanId", "traceId"
1927
+ ORDER BY
1928
+ CASE WHEN "endedAt" IS NOT NULL THEN 0 ELSE 1 END,
1929
+ "updatedAt" DESC,
1930
+ "createdAt" DESC
1931
+ ) as rn
1932
+ FROM "${storage.TABLE_SPANS}"
1933
+ ) ranked
1934
+ WHERE rn = 1
1935
+ GROUP BY "spanId", "traceId"
1936
+ )
1937
+ AND ("spanId", "traceId") IN (
1938
+ SELECT "spanId", "traceId"
1939
+ FROM "${storage.TABLE_SPANS}"
1940
+ GROUP BY "spanId", "traceId"
1941
+ HAVING COUNT(*) > 1
1942
+ )
1943
+ `);
1944
+ const deletedCount = deleteResult.rowsAffected ?? 0;
1945
+ this.logger.warn(`LibSQLDB: Deleted ${deletedCount} duplicate span records`);
1946
+ } catch (error) {
1947
+ this.logger.warn(`LibSQLDB: Failed to deduplicate spans:`, error);
1948
+ }
1949
+ }
1950
+ /**
1951
+ * Gets a default value for a column type (used when adding NOT NULL columns).
1952
+ */
1953
+ getDefaultValue(type) {
1954
+ switch (type) {
1955
+ case "text":
1956
+ case "uuid":
1957
+ return "DEFAULT ''";
1958
+ case "integer":
1959
+ case "bigint":
1960
+ case "float":
1961
+ return "DEFAULT 0";
1962
+ case "boolean":
1963
+ return "DEFAULT 0";
1964
+ case "jsonb":
1965
+ return "DEFAULT '{}'";
1966
+ case "timestamp":
1967
+ return "DEFAULT CURRENT_TIMESTAMP";
1968
+ default:
1969
+ return "DEFAULT ''";
1970
+ }
1971
+ }
1972
+ /**
1973
+ * Alters an existing table to add missing columns.
1974
+ * Used for schema migrations when new columns are added.
1975
+ *
1976
+ * @param args - The alter table arguments
1977
+ * @param args.tableName - The name of the table to alter
1978
+ * @param args.schema - The full schema definition for the table
1979
+ * @param args.ifNotExists - Array of column names to add if they don't exist
1980
+ */
1981
+ async alterTable({
1982
+ tableName,
1983
+ schema,
1984
+ ifNotExists
1985
+ }) {
1986
+ const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
1987
+ try {
1988
+ const tableInfo = await this.client.execute({
1989
+ sql: `PRAGMA table_info("${parsedTableName}")`
1990
+ });
1991
+ const existingColumns = new Set((tableInfo.rows || []).map((row) => row.name?.toLowerCase()));
1992
+ for (const columnName of ifNotExists) {
1993
+ if (!existingColumns.has(columnName.toLowerCase()) && schema[columnName]) {
1994
+ const columnDef = schema[columnName];
1995
+ const sqlType = this.getSqlType(columnDef.type);
1996
+ const defaultValue = this.getDefaultValue(columnDef.type);
1997
+ const alterSql = `ALTER TABLE ${parsedTableName} ADD COLUMN "${columnName}" ${sqlType} ${defaultValue}`;
1998
+ await this.client.execute(alterSql);
1999
+ this.logger.debug(`LibSQLDB: Added column ${columnName} to table ${tableName}`);
2000
+ }
2001
+ }
1479
2002
  } catch (error$1) {
1480
2003
  throw new error.MastraError(
1481
2004
  {
1482
- id: storage.createStorageErrorId("LIBSQL", "LIST_MESSAGES_BY_ID", "FAILED"),
2005
+ id: storage.createStorageErrorId("LIBSQL", "ALTER_TABLE", "FAILED"),
1483
2006
  domain: error.ErrorDomain.STORAGE,
1484
2007
  category: error.ErrorCategory.THIRD_PARTY,
1485
- details: { messageIds: JSON.stringify(messageIds) }
2008
+ details: { tableName }
1486
2009
  },
1487
2010
  error$1
1488
2011
  );
1489
2012
  }
1490
2013
  }
1491
- async listMessages(args) {
1492
- const { threadId, resourceId, include, filter, perPage: perPageInput, page = 0, orderBy } = args;
1493
- const threadIds = Array.isArray(threadId) ? threadId : [threadId];
1494
- if (threadIds.length === 0 || threadIds.some((id) => !id.trim())) {
1495
- throw new error.MastraError(
2014
+ /**
2015
+ * Deletes all records from the specified table.
2016
+ * Errors are logged but not thrown.
2017
+ *
2018
+ * @param args - The delete arguments
2019
+ * @param args.tableName - The name of the table to clear
2020
+ */
2021
+ async deleteData({ tableName }) {
2022
+ const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
2023
+ try {
2024
+ await this.client.execute(`DELETE FROM ${parsedTableName}`);
2025
+ } catch (e) {
2026
+ const mastraError = new error.MastraError(
1496
2027
  {
1497
- id: storage.createStorageErrorId("LIBSQL", "LIST_MESSAGES", "INVALID_THREAD_ID"),
2028
+ id: storage.createStorageErrorId("LIBSQL", "CLEAR_TABLE", "FAILED"),
1498
2029
  domain: error.ErrorDomain.STORAGE,
1499
2030
  category: error.ErrorCategory.THIRD_PARTY,
1500
- details: { threadId: Array.isArray(threadId) ? threadId.join(",") : threadId }
2031
+ details: {
2032
+ tableName
2033
+ }
1501
2034
  },
1502
- new Error("threadId must be a non-empty string or array of non-empty strings")
2035
+ e
1503
2036
  );
2037
+ this.logger?.trackException?.(mastraError);
2038
+ this.logger?.error?.(mastraError.toString());
1504
2039
  }
1505
- if (page < 0) {
2040
+ }
2041
+ };
2042
+
2043
+ // src/storage/domains/agents/index.ts
2044
+ var AgentsLibSQL = class extends storage.AgentsStorage {
2045
+ #db;
2046
+ constructor(config) {
2047
+ super();
2048
+ const client = resolveClient(config);
2049
+ this.#db = new LibSQLDB({ client, maxRetries: config.maxRetries, initialBackoffMs: config.initialBackoffMs });
2050
+ }
2051
+ async init() {
2052
+ await this.#db.createTable({ tableName: storage.TABLE_AGENTS, schema: storage.AGENTS_SCHEMA });
2053
+ }
2054
+ async dangerouslyClearAll() {
2055
+ await this.#db.deleteData({ tableName: storage.TABLE_AGENTS });
2056
+ }
2057
+ parseJson(value, fieldName) {
2058
+ if (!value) return void 0;
2059
+ if (typeof value !== "string") return value;
2060
+ try {
2061
+ return JSON.parse(value);
2062
+ } catch (error$1) {
2063
+ const details = {
2064
+ value: value.length > 100 ? value.substring(0, 100) + "..." : value
2065
+ };
2066
+ if (fieldName) {
2067
+ details.field = fieldName;
2068
+ }
1506
2069
  throw new error.MastraError(
1507
2070
  {
1508
- id: storage.createStorageErrorId("LIBSQL", "LIST_MESSAGES", "INVALID_PAGE"),
2071
+ id: storage.createStorageErrorId("LIBSQL", "PARSE_JSON", "INVALID_JSON"),
1509
2072
  domain: error.ErrorDomain.STORAGE,
1510
- category: error.ErrorCategory.USER,
1511
- details: { page }
2073
+ category: error.ErrorCategory.SYSTEM,
2074
+ text: `Failed to parse JSON${fieldName ? ` for field "${fieldName}"` : ""}: ${error$1 instanceof Error ? error$1.message : "Unknown error"}`,
2075
+ details
1512
2076
  },
1513
- new Error("page must be >= 0")
2077
+ error$1
1514
2078
  );
1515
2079
  }
1516
- const perPage = storage.normalizePerPage(perPageInput, 40);
1517
- const { offset, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
2080
+ }
2081
+ parseRow(row) {
2082
+ return {
2083
+ id: row.id,
2084
+ name: row.name,
2085
+ description: row.description,
2086
+ instructions: row.instructions,
2087
+ model: this.parseJson(row.model, "model"),
2088
+ tools: this.parseJson(row.tools, "tools"),
2089
+ defaultOptions: this.parseJson(row.defaultOptions, "defaultOptions"),
2090
+ workflows: this.parseJson(row.workflows, "workflows"),
2091
+ agents: this.parseJson(row.agents, "agents"),
2092
+ inputProcessors: this.parseJson(row.inputProcessors, "inputProcessors"),
2093
+ outputProcessors: this.parseJson(row.outputProcessors, "outputProcessors"),
2094
+ memory: this.parseJson(row.memory, "memory"),
2095
+ scorers: this.parseJson(row.scorers, "scorers"),
2096
+ metadata: this.parseJson(row.metadata, "metadata"),
2097
+ createdAt: new Date(row.createdAt),
2098
+ updatedAt: new Date(row.updatedAt)
2099
+ };
2100
+ }
2101
+ async getAgentById({ id }) {
1518
2102
  try {
1519
- const { field, direction } = this.parseOrderBy(orderBy, "ASC");
1520
- const orderByStatement = `ORDER BY "${field}" ${direction}`;
1521
- const threadPlaceholders = threadIds.map(() => "?").join(", ");
1522
- const conditions = [`thread_id IN (${threadPlaceholders})`];
1523
- const queryParams = [...threadIds];
1524
- if (resourceId) {
1525
- conditions.push(`"resourceId" = ?`);
1526
- queryParams.push(resourceId);
1527
- }
1528
- if (filter?.dateRange?.start) {
1529
- conditions.push(`"createdAt" >= ?`);
1530
- queryParams.push(
1531
- filter.dateRange.start instanceof Date ? filter.dateRange.start.toISOString() : filter.dateRange.start
1532
- );
1533
- }
1534
- if (filter?.dateRange?.end) {
1535
- conditions.push(`"createdAt" <= ?`);
1536
- queryParams.push(
1537
- filter.dateRange.end instanceof Date ? filter.dateRange.end.toISOString() : filter.dateRange.end
1538
- );
1539
- }
1540
- const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
1541
- const countResult = await this.client.execute({
1542
- sql: `SELECT COUNT(*) as count FROM ${storage.TABLE_MESSAGES} ${whereClause}`,
1543
- args: queryParams
1544
- });
1545
- const total = Number(countResult.rows?.[0]?.count ?? 0);
1546
- const limitValue = perPageInput === false ? total : perPage;
1547
- const dataResult = await this.client.execute({
1548
- sql: `SELECT id, content, role, type, "createdAt", "resourceId", "thread_id" FROM ${storage.TABLE_MESSAGES} ${whereClause} ${orderByStatement} LIMIT ? OFFSET ?`,
1549
- args: [...queryParams, limitValue, offset]
2103
+ const result = await this.#db.select({
2104
+ tableName: storage.TABLE_AGENTS,
2105
+ keys: { id }
1550
2106
  });
1551
- const messages = (dataResult.rows || []).map((row) => this.parseRow(row));
1552
- if (total === 0 && messages.length === 0 && (!include || include.length === 0)) {
1553
- return {
1554
- messages: [],
1555
- total: 0,
1556
- page,
1557
- perPage: perPageForResponse,
1558
- hasMore: false
1559
- };
1560
- }
1561
- const messageIds = new Set(messages.map((m) => m.id));
1562
- if (include && include.length > 0) {
1563
- const includeMessages = await this._getIncludedMessages({ include });
1564
- if (includeMessages) {
1565
- for (const includeMsg of includeMessages) {
1566
- if (!messageIds.has(includeMsg.id)) {
1567
- messages.push(includeMsg);
1568
- messageIds.add(includeMsg.id);
1569
- }
1570
- }
1571
- }
1572
- }
1573
- const list = new agent.MessageList().add(messages, "memory");
1574
- let finalMessages = list.get.all.db();
1575
- finalMessages = finalMessages.sort((a, b) => {
1576
- const isDateField = field === "createdAt" || field === "updatedAt";
1577
- const aValue = isDateField ? new Date(a[field]).getTime() : a[field];
1578
- const bValue = isDateField ? new Date(b[field]).getTime() : b[field];
1579
- if (typeof aValue === "number" && typeof bValue === "number") {
1580
- return direction === "ASC" ? aValue - bValue : bValue - aValue;
2107
+ return result ? this.parseRow(result) : null;
2108
+ } catch (error$1) {
2109
+ throw new error.MastraError(
2110
+ {
2111
+ id: storage.createStorageErrorId("LIBSQL", "GET_AGENT_BY_ID", "FAILED"),
2112
+ domain: error.ErrorDomain.STORAGE,
2113
+ category: error.ErrorCategory.THIRD_PARTY,
2114
+ details: { agentId: id }
2115
+ },
2116
+ error$1
2117
+ );
2118
+ }
2119
+ }
2120
+ async createAgent({ agent }) {
2121
+ try {
2122
+ const now = /* @__PURE__ */ new Date();
2123
+ await this.#db.insert({
2124
+ tableName: storage.TABLE_AGENTS,
2125
+ record: {
2126
+ id: agent.id,
2127
+ name: agent.name,
2128
+ description: agent.description ?? null,
2129
+ instructions: agent.instructions,
2130
+ model: agent.model,
2131
+ tools: agent.tools ?? null,
2132
+ defaultOptions: agent.defaultOptions ?? null,
2133
+ workflows: agent.workflows ?? null,
2134
+ agents: agent.agents ?? null,
2135
+ inputProcessors: agent.inputProcessors ?? null,
2136
+ outputProcessors: agent.outputProcessors ?? null,
2137
+ memory: agent.memory ?? null,
2138
+ scorers: agent.scorers ?? null,
2139
+ metadata: agent.metadata ?? null,
2140
+ createdAt: now,
2141
+ updatedAt: now
1581
2142
  }
1582
- return direction === "ASC" ? String(aValue).localeCompare(String(bValue)) : String(bValue).localeCompare(String(aValue));
1583
2143
  });
1584
- const threadIdSet = new Set(threadIds);
1585
- const returnedThreadMessageIds = new Set(
1586
- finalMessages.filter((m) => m.threadId && threadIdSet.has(m.threadId)).map((m) => m.id)
1587
- );
1588
- const allThreadMessagesReturned = returnedThreadMessageIds.size >= total;
1589
- const hasMore = perPageInput !== false && !allThreadMessagesReturned && offset + perPage < total;
1590
2144
  return {
1591
- messages: finalMessages,
1592
- total,
1593
- page,
1594
- perPage: perPageForResponse,
1595
- hasMore
2145
+ ...agent,
2146
+ createdAt: now,
2147
+ updatedAt: now
1596
2148
  };
1597
2149
  } catch (error$1) {
1598
- const mastraError = new error.MastraError(
2150
+ throw new error.MastraError(
1599
2151
  {
1600
- id: storage.createStorageErrorId("LIBSQL", "LIST_MESSAGES", "FAILED"),
2152
+ id: storage.createStorageErrorId("LIBSQL", "CREATE_AGENT", "FAILED"),
1601
2153
  domain: error.ErrorDomain.STORAGE,
1602
2154
  category: error.ErrorCategory.THIRD_PARTY,
1603
- details: {
1604
- threadId: Array.isArray(threadId) ? threadId.join(",") : threadId,
1605
- resourceId: resourceId ?? ""
1606
- }
2155
+ details: { agentId: agent.id }
1607
2156
  },
1608
2157
  error$1
1609
2158
  );
1610
- this.logger?.error?.(mastraError.toString());
1611
- this.logger?.trackException?.(mastraError);
1612
- return {
1613
- messages: [],
1614
- total: 0,
1615
- page,
1616
- perPage: perPageForResponse,
1617
- hasMore: false
1618
- };
1619
2159
  }
1620
2160
  }
1621
- async saveMessages({ messages }) {
1622
- if (messages.length === 0) return { messages };
2161
+ async updateAgent({ id, ...updates }) {
1623
2162
  try {
1624
- const threadId = messages[0]?.threadId;
1625
- if (!threadId) {
1626
- throw new Error("Thread ID is required");
2163
+ const existingAgent = await this.getAgentById({ id });
2164
+ if (!existingAgent) {
2165
+ throw new error.MastraError({
2166
+ id: storage.createStorageErrorId("LIBSQL", "UPDATE_AGENT", "NOT_FOUND"),
2167
+ domain: error.ErrorDomain.STORAGE,
2168
+ category: error.ErrorCategory.USER,
2169
+ text: `Agent ${id} not found`,
2170
+ details: { agentId: id }
2171
+ });
1627
2172
  }
1628
- const batchStatements = messages.map((message) => {
1629
- const time = message.createdAt || /* @__PURE__ */ new Date();
1630
- if (!message.threadId) {
1631
- throw new Error(
1632
- `Expected to find a threadId for message, but couldn't find one. An unexpected error has occurred.`
1633
- );
1634
- }
1635
- if (!message.resourceId) {
1636
- throw new Error(
1637
- `Expected to find a resourceId for message, but couldn't find one. An unexpected error has occurred.`
1638
- );
1639
- }
1640
- return {
1641
- sql: `INSERT INTO "${storage.TABLE_MESSAGES}" (id, thread_id, content, role, type, "createdAt", "resourceId")
1642
- VALUES (?, ?, ?, ?, ?, ?, ?)
1643
- ON CONFLICT(id) DO UPDATE SET
1644
- thread_id=excluded.thread_id,
1645
- content=excluded.content,
1646
- role=excluded.role,
1647
- type=excluded.type,
1648
- "resourceId"=excluded."resourceId"
1649
- `,
1650
- args: [
1651
- message.id,
1652
- message.threadId,
1653
- typeof message.content === "object" ? JSON.stringify(message.content) : message.content,
1654
- message.role,
1655
- message.type || "v2",
1656
- time instanceof Date ? time.toISOString() : time,
1657
- message.resourceId
1658
- ]
1659
- };
1660
- });
1661
- const now = (/* @__PURE__ */ new Date()).toISOString();
1662
- batchStatements.push({
1663
- sql: `UPDATE "${storage.TABLE_THREADS}" SET "updatedAt" = ? WHERE id = ?`,
1664
- args: [now, threadId]
1665
- });
1666
- const BATCH_SIZE = 50;
1667
- const messageStatements = batchStatements.slice(0, -1);
1668
- const threadUpdateStatement = batchStatements[batchStatements.length - 1];
1669
- for (let i = 0; i < messageStatements.length; i += BATCH_SIZE) {
1670
- const batch = messageStatements.slice(i, i + BATCH_SIZE);
1671
- if (batch.length > 0) {
1672
- await this.client.batch(batch, "write");
1673
- }
2173
+ const data = {
2174
+ updatedAt: /* @__PURE__ */ new Date()
2175
+ };
2176
+ if (updates.name !== void 0) data.name = updates.name;
2177
+ if (updates.description !== void 0) data.description = updates.description;
2178
+ if (updates.instructions !== void 0) data.instructions = updates.instructions;
2179
+ if (updates.model !== void 0) data.model = updates.model;
2180
+ if (updates.tools !== void 0) data.tools = updates.tools;
2181
+ if (updates.defaultOptions !== void 0) data.defaultOptions = updates.defaultOptions;
2182
+ if (updates.workflows !== void 0) data.workflows = updates.workflows;
2183
+ if (updates.agents !== void 0) data.agents = updates.agents;
2184
+ if (updates.inputProcessors !== void 0) data.inputProcessors = updates.inputProcessors;
2185
+ if (updates.outputProcessors !== void 0) data.outputProcessors = updates.outputProcessors;
2186
+ if (updates.memory !== void 0) data.memory = updates.memory;
2187
+ if (updates.scorers !== void 0) data.scorers = updates.scorers;
2188
+ if (updates.metadata !== void 0) {
2189
+ data.metadata = { ...existingAgent.metadata, ...updates.metadata };
1674
2190
  }
1675
- if (threadUpdateStatement) {
1676
- await this.client.execute(threadUpdateStatement);
2191
+ if (Object.keys(data).length > 1) {
2192
+ await this.#db.update({
2193
+ tableName: storage.TABLE_AGENTS,
2194
+ keys: { id },
2195
+ data
2196
+ });
2197
+ }
2198
+ const updatedAgent = await this.getAgentById({ id });
2199
+ if (!updatedAgent) {
2200
+ throw new error.MastraError({
2201
+ id: storage.createStorageErrorId("LIBSQL", "UPDATE_AGENT", "NOT_FOUND_AFTER_UPDATE"),
2202
+ domain: error.ErrorDomain.STORAGE,
2203
+ category: error.ErrorCategory.SYSTEM,
2204
+ text: `Agent ${id} not found after update`,
2205
+ details: { agentId: id }
2206
+ });
1677
2207
  }
1678
- const list = new agent.MessageList().add(messages, "memory");
1679
- return { messages: list.get.all.db() };
2208
+ return updatedAgent;
1680
2209
  } catch (error$1) {
2210
+ if (error$1 instanceof error.MastraError) {
2211
+ throw error$1;
2212
+ }
1681
2213
  throw new error.MastraError(
1682
2214
  {
1683
- id: storage.createStorageErrorId("LIBSQL", "SAVE_MESSAGES", "FAILED"),
2215
+ id: storage.createStorageErrorId("LIBSQL", "UPDATE_AGENT", "FAILED"),
1684
2216
  domain: error.ErrorDomain.STORAGE,
1685
- category: error.ErrorCategory.THIRD_PARTY
2217
+ category: error.ErrorCategory.THIRD_PARTY,
2218
+ details: { agentId: id }
1686
2219
  },
1687
2220
  error$1
1688
2221
  );
1689
2222
  }
1690
2223
  }
1691
- async updateMessages({
1692
- messages
1693
- }) {
1694
- if (messages.length === 0) {
1695
- return [];
2224
+ async deleteAgent({ id }) {
2225
+ try {
2226
+ await this.#db.delete({
2227
+ tableName: storage.TABLE_AGENTS,
2228
+ keys: { id }
2229
+ });
2230
+ } catch (error$1) {
2231
+ throw new error.MastraError(
2232
+ {
2233
+ id: storage.createStorageErrorId("LIBSQL", "DELETE_AGENT", "FAILED"),
2234
+ domain: error.ErrorDomain.STORAGE,
2235
+ category: error.ErrorCategory.THIRD_PARTY,
2236
+ details: { agentId: id }
2237
+ },
2238
+ error$1
2239
+ );
1696
2240
  }
1697
- const messageIds = messages.map((m) => m.id);
1698
- const placeholders = messageIds.map(() => "?").join(",");
1699
- const selectSql = `SELECT * FROM ${storage.TABLE_MESSAGES} WHERE id IN (${placeholders})`;
1700
- const existingResult = await this.client.execute({ sql: selectSql, args: messageIds });
1701
- const existingMessages = existingResult.rows.map((row) => this.parseRow(row));
1702
- if (existingMessages.length === 0) {
1703
- return [];
2241
+ }
2242
+ async listAgents(args) {
2243
+ const { page = 0, perPage: perPageInput, orderBy } = args || {};
2244
+ const { field, direction } = this.parseOrderBy(orderBy);
2245
+ if (page < 0) {
2246
+ throw new error.MastraError(
2247
+ {
2248
+ id: storage.createStorageErrorId("LIBSQL", "LIST_AGENTS", "INVALID_PAGE"),
2249
+ domain: error.ErrorDomain.STORAGE,
2250
+ category: error.ErrorCategory.USER,
2251
+ details: { page }
2252
+ },
2253
+ new Error("page must be >= 0")
2254
+ );
1704
2255
  }
1705
- const batchStatements = [];
1706
- const threadIdsToUpdate = /* @__PURE__ */ new Set();
1707
- const columnMapping = {
1708
- threadId: "thread_id"
1709
- };
1710
- for (const existingMessage of existingMessages) {
1711
- const updatePayload = messages.find((m) => m.id === existingMessage.id);
1712
- if (!updatePayload) continue;
1713
- const { id, ...fieldsToUpdate } = updatePayload;
1714
- if (Object.keys(fieldsToUpdate).length === 0) continue;
1715
- threadIdsToUpdate.add(existingMessage.threadId);
1716
- if (updatePayload.threadId && updatePayload.threadId !== existingMessage.threadId) {
1717
- threadIdsToUpdate.add(updatePayload.threadId);
1718
- }
1719
- const setClauses = [];
1720
- const args = [];
1721
- const updatableFields = { ...fieldsToUpdate };
1722
- if (updatableFields.content) {
1723
- const newContent = {
1724
- ...existingMessage.content,
1725
- ...updatableFields.content,
1726
- // Deep merge metadata if it exists on both
1727
- ...existingMessage.content?.metadata && updatableFields.content.metadata ? {
1728
- metadata: {
1729
- ...existingMessage.content.metadata,
1730
- ...updatableFields.content.metadata
1731
- }
1732
- } : {}
2256
+ const perPage = storage.normalizePerPage(perPageInput, 100);
2257
+ const { offset, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
2258
+ try {
2259
+ const total = await this.#db.selectTotalCount({ tableName: storage.TABLE_AGENTS });
2260
+ if (total === 0) {
2261
+ return {
2262
+ agents: [],
2263
+ total: 0,
2264
+ page,
2265
+ perPage: perPageForResponse,
2266
+ hasMore: false
1733
2267
  };
1734
- setClauses.push(`${utils.parseSqlIdentifier("content", "column name")} = ?`);
1735
- args.push(JSON.stringify(newContent));
1736
- delete updatableFields.content;
1737
- }
1738
- for (const key in updatableFields) {
1739
- if (Object.prototype.hasOwnProperty.call(updatableFields, key)) {
1740
- const dbKey = columnMapping[key] || key;
1741
- setClauses.push(`${utils.parseSqlIdentifier(dbKey, "column name")} = ?`);
1742
- let value = updatableFields[key];
1743
- if (typeof value === "object" && value !== null) {
1744
- value = JSON.stringify(value);
1745
- }
1746
- args.push(value);
1747
- }
1748
2268
  }
1749
- if (setClauses.length === 0) continue;
1750
- args.push(id);
1751
- const sql = `UPDATE ${storage.TABLE_MESSAGES} SET ${setClauses.join(", ")} WHERE id = ?`;
1752
- batchStatements.push({ sql, args });
1753
- }
1754
- if (batchStatements.length === 0) {
1755
- return existingMessages;
2269
+ const limitValue = perPageInput === false ? total : perPage;
2270
+ const rows = await this.#db.selectMany({
2271
+ tableName: storage.TABLE_AGENTS,
2272
+ orderBy: `"${field}" ${direction}`,
2273
+ limit: limitValue,
2274
+ offset
2275
+ });
2276
+ const agents = rows.map((row) => this.parseRow(row));
2277
+ return {
2278
+ agents,
2279
+ total,
2280
+ page,
2281
+ perPage: perPageForResponse,
2282
+ hasMore: perPageInput === false ? false : offset + perPage < total
2283
+ };
2284
+ } catch (error$1) {
2285
+ throw new error.MastraError(
2286
+ {
2287
+ id: storage.createStorageErrorId("LIBSQL", "LIST_AGENTS", "FAILED"),
2288
+ domain: error.ErrorDomain.STORAGE,
2289
+ category: error.ErrorCategory.THIRD_PARTY
2290
+ },
2291
+ error$1
2292
+ );
1756
2293
  }
1757
- const now = (/* @__PURE__ */ new Date()).toISOString();
1758
- for (const threadId of threadIdsToUpdate) {
1759
- if (threadId) {
1760
- batchStatements.push({
1761
- sql: `UPDATE ${storage.TABLE_THREADS} SET updatedAt = ? WHERE id = ?`,
1762
- args: [now, threadId]
1763
- });
1764
- }
2294
+ }
2295
+ };
2296
+ var MemoryLibSQL = class extends storage.MemoryStorage {
2297
+ #client;
2298
+ #db;
2299
+ constructor(config) {
2300
+ super();
2301
+ const client = resolveClient(config);
2302
+ this.#client = client;
2303
+ this.#db = new LibSQLDB({ client, maxRetries: config.maxRetries, initialBackoffMs: config.initialBackoffMs });
2304
+ }
2305
+ async init() {
2306
+ await this.#db.createTable({ tableName: storage.TABLE_THREADS, schema: storage.TABLE_SCHEMAS[storage.TABLE_THREADS] });
2307
+ await this.#db.createTable({ tableName: storage.TABLE_MESSAGES, schema: storage.TABLE_SCHEMAS[storage.TABLE_MESSAGES] });
2308
+ await this.#db.createTable({ tableName: storage.TABLE_RESOURCES, schema: storage.TABLE_SCHEMAS[storage.TABLE_RESOURCES] });
2309
+ await this.#db.alterTable({
2310
+ tableName: storage.TABLE_MESSAGES,
2311
+ schema: storage.TABLE_SCHEMAS[storage.TABLE_MESSAGES],
2312
+ ifNotExists: ["resourceId"]
2313
+ });
2314
+ }
2315
+ async dangerouslyClearAll() {
2316
+ await this.#db.deleteData({ tableName: storage.TABLE_MESSAGES });
2317
+ await this.#db.deleteData({ tableName: storage.TABLE_THREADS });
2318
+ await this.#db.deleteData({ tableName: storage.TABLE_RESOURCES });
2319
+ }
2320
+ parseRow(row) {
2321
+ let content = row.content;
2322
+ try {
2323
+ content = JSON.parse(row.content);
2324
+ } catch {
1765
2325
  }
1766
- await this.client.batch(batchStatements, "write");
1767
- const updatedResult = await this.client.execute({ sql: selectSql, args: messageIds });
1768
- return updatedResult.rows.map((row) => this.parseRow(row));
2326
+ const result = {
2327
+ id: row.id,
2328
+ content,
2329
+ role: row.role,
2330
+ createdAt: new Date(row.createdAt),
2331
+ threadId: row.thread_id,
2332
+ resourceId: row.resourceId
2333
+ };
2334
+ if (row.type && row.type !== `v2`) result.type = row.type;
2335
+ return result;
1769
2336
  }
1770
- async deleteMessages(messageIds) {
1771
- if (!messageIds || messageIds.length === 0) {
1772
- return;
2337
+ async _getIncludedMessages({ include }) {
2338
+ if (!include || include.length === 0) return null;
2339
+ const unionQueries = [];
2340
+ const params = [];
2341
+ for (const inc of include) {
2342
+ const { id, withPreviousMessages = 0, withNextMessages = 0 } = inc;
2343
+ unionQueries.push(
2344
+ `
2345
+ SELECT * FROM (
2346
+ WITH target_thread AS (
2347
+ SELECT thread_id FROM "${storage.TABLE_MESSAGES}" WHERE id = ?
2348
+ ),
2349
+ numbered_messages AS (
2350
+ SELECT
2351
+ id, content, role, type, "createdAt", thread_id, "resourceId",
2352
+ ROW_NUMBER() OVER (ORDER BY "createdAt" ASC) as row_num
2353
+ FROM "${storage.TABLE_MESSAGES}"
2354
+ WHERE thread_id = (SELECT thread_id FROM target_thread)
2355
+ ),
2356
+ target_positions AS (
2357
+ SELECT row_num as target_pos
2358
+ FROM numbered_messages
2359
+ WHERE id = ?
2360
+ )
2361
+ SELECT DISTINCT m.*
2362
+ FROM numbered_messages m
2363
+ CROSS JOIN target_positions t
2364
+ WHERE m.row_num BETWEEN (t.target_pos - ?) AND (t.target_pos + ?)
2365
+ )
2366
+ `
2367
+ // Keep ASC for final sorting after fetching context
2368
+ );
2369
+ params.push(id, id, withPreviousMessages, withNextMessages);
1773
2370
  }
2371
+ const finalQuery = unionQueries.join(" UNION ALL ") + ' ORDER BY "createdAt" ASC';
2372
+ const includedResult = await this.#client.execute({ sql: finalQuery, args: params });
2373
+ const includedRows = includedResult.rows?.map((row) => this.parseRow(row));
2374
+ const seen = /* @__PURE__ */ new Set();
2375
+ const dedupedRows = includedRows.filter((row) => {
2376
+ if (seen.has(row.id)) return false;
2377
+ seen.add(row.id);
2378
+ return true;
2379
+ });
2380
+ return dedupedRows;
2381
+ }
2382
+ async listMessagesById({ messageIds }) {
2383
+ if (messageIds.length === 0) return { messages: [] };
1774
2384
  try {
1775
- const BATCH_SIZE = 100;
1776
- const threadIds = /* @__PURE__ */ new Set();
1777
- const tx = await this.client.transaction("write");
1778
- try {
1779
- for (let i = 0; i < messageIds.length; i += BATCH_SIZE) {
1780
- const batch = messageIds.slice(i, i + BATCH_SIZE);
1781
- const placeholders = batch.map(() => "?").join(",");
1782
- const result = await tx.execute({
1783
- sql: `SELECT DISTINCT thread_id FROM "${storage.TABLE_MESSAGES}" WHERE id IN (${placeholders})`,
1784
- args: batch
1785
- });
1786
- result.rows?.forEach((row) => {
1787
- if (row.thread_id) threadIds.add(row.thread_id);
1788
- });
1789
- await tx.execute({
1790
- sql: `DELETE FROM "${storage.TABLE_MESSAGES}" WHERE id IN (${placeholders})`,
1791
- args: batch
1792
- });
1793
- }
1794
- if (threadIds.size > 0) {
1795
- const now = (/* @__PURE__ */ new Date()).toISOString();
1796
- for (const threadId of threadIds) {
1797
- await tx.execute({
1798
- sql: `UPDATE "${storage.TABLE_THREADS}" SET "updatedAt" = ? WHERE id = ?`,
1799
- args: [now, threadId]
1800
- });
1801
- }
1802
- }
1803
- await tx.commit();
1804
- } catch (error) {
1805
- await tx.rollback();
1806
- throw error;
1807
- }
2385
+ const sql = `
2386
+ SELECT
2387
+ id,
2388
+ content,
2389
+ role,
2390
+ type,
2391
+ "createdAt",
2392
+ thread_id,
2393
+ "resourceId"
2394
+ FROM "${storage.TABLE_MESSAGES}"
2395
+ WHERE id IN (${messageIds.map(() => "?").join(", ")})
2396
+ ORDER BY "createdAt" DESC
2397
+ `;
2398
+ const result = await this.#client.execute({ sql, args: messageIds });
2399
+ if (!result.rows) return { messages: [] };
2400
+ const list = new agent.MessageList().add(result.rows.map(this.parseRow), "memory");
2401
+ return { messages: list.get.all.db() };
1808
2402
  } catch (error$1) {
1809
2403
  throw new error.MastraError(
1810
2404
  {
1811
- id: storage.createStorageErrorId("LIBSQL", "DELETE_MESSAGES", "FAILED"),
2405
+ id: storage.createStorageErrorId("LIBSQL", "LIST_MESSAGES_BY_ID", "FAILED"),
1812
2406
  domain: error.ErrorDomain.STORAGE,
1813
2407
  category: error.ErrorCategory.THIRD_PARTY,
1814
- details: { messageIds: messageIds.join(", ") }
2408
+ details: { messageIds: JSON.stringify(messageIds) }
1815
2409
  },
1816
2410
  error$1
1817
2411
  );
1818
2412
  }
1819
2413
  }
1820
- async getResourceById({ resourceId }) {
1821
- const result = await this.operations.load({
1822
- tableName: storage.TABLE_RESOURCES,
1823
- keys: { id: resourceId }
1824
- });
1825
- if (!result) {
1826
- return null;
1827
- }
1828
- return {
1829
- ...result,
1830
- // Ensure workingMemory is always returned as a string, even if auto-parsed as JSON
1831
- workingMemory: result.workingMemory && typeof result.workingMemory === "object" ? JSON.stringify(result.workingMemory) : result.workingMemory,
1832
- metadata: typeof result.metadata === "string" ? JSON.parse(result.metadata) : result.metadata,
1833
- createdAt: new Date(result.createdAt),
1834
- updatedAt: new Date(result.updatedAt)
1835
- };
1836
- }
1837
- async saveResource({ resource }) {
1838
- await this.operations.insert({
1839
- tableName: storage.TABLE_RESOURCES,
1840
- record: {
1841
- ...resource,
1842
- metadata: JSON.stringify(resource.metadata)
1843
- }
1844
- });
1845
- return resource;
1846
- }
1847
- async updateResource({
1848
- resourceId,
1849
- workingMemory,
1850
- metadata
1851
- }) {
1852
- const existingResource = await this.getResourceById({ resourceId });
1853
- if (!existingResource) {
1854
- const newResource = {
1855
- id: resourceId,
1856
- workingMemory,
1857
- metadata: metadata || {},
1858
- createdAt: /* @__PURE__ */ new Date(),
1859
- updatedAt: /* @__PURE__ */ new Date()
1860
- };
1861
- return this.saveResource({ resource: newResource });
1862
- }
1863
- const updatedResource = {
1864
- ...existingResource,
1865
- workingMemory: workingMemory !== void 0 ? workingMemory : existingResource.workingMemory,
1866
- metadata: {
1867
- ...existingResource.metadata,
1868
- ...metadata
1869
- },
1870
- updatedAt: /* @__PURE__ */ new Date()
1871
- };
1872
- const updates = [];
1873
- const values = [];
1874
- if (workingMemory !== void 0) {
1875
- updates.push("workingMemory = ?");
1876
- values.push(workingMemory);
1877
- }
1878
- if (metadata) {
1879
- updates.push("metadata = ?");
1880
- values.push(JSON.stringify(updatedResource.metadata));
1881
- }
1882
- updates.push("updatedAt = ?");
1883
- values.push(updatedResource.updatedAt.toISOString());
1884
- values.push(resourceId);
1885
- await this.client.execute({
1886
- sql: `UPDATE ${storage.TABLE_RESOURCES} SET ${updates.join(", ")} WHERE id = ?`,
1887
- args: values
1888
- });
1889
- return updatedResource;
1890
- }
1891
- async getThreadById({ threadId }) {
1892
- try {
1893
- const result = await this.operations.load({
1894
- tableName: storage.TABLE_THREADS,
1895
- keys: { id: threadId }
1896
- });
1897
- if (!result) {
1898
- return null;
1899
- }
1900
- return {
1901
- ...result,
1902
- metadata: typeof result.metadata === "string" ? JSON.parse(result.metadata) : result.metadata,
1903
- createdAt: new Date(result.createdAt),
1904
- updatedAt: new Date(result.updatedAt)
1905
- };
1906
- } catch (error$1) {
2414
+ async listMessages(args) {
2415
+ const { threadId, resourceId, include, filter, perPage: perPageInput, page = 0, orderBy } = args;
2416
+ const threadIds = Array.isArray(threadId) ? threadId : [threadId];
2417
+ if (threadIds.length === 0 || threadIds.some((id) => !id.trim())) {
1907
2418
  throw new error.MastraError(
1908
2419
  {
1909
- id: storage.createStorageErrorId("LIBSQL", "GET_THREAD_BY_ID", "FAILED"),
2420
+ id: storage.createStorageErrorId("LIBSQL", "LIST_MESSAGES", "INVALID_THREAD_ID"),
1910
2421
  domain: error.ErrorDomain.STORAGE,
1911
2422
  category: error.ErrorCategory.THIRD_PARTY,
1912
- details: { threadId }
2423
+ details: { threadId: Array.isArray(threadId) ? threadId.join(",") : threadId }
1913
2424
  },
1914
- error$1
2425
+ new Error("threadId must be a non-empty string or array of non-empty strings")
1915
2426
  );
1916
2427
  }
1917
- }
1918
- async listThreadsByResourceId(args) {
1919
- const { resourceId, page = 0, perPage: perPageInput, orderBy } = args;
1920
2428
  if (page < 0) {
1921
2429
  throw new error.MastraError(
1922
2430
  {
1923
- id: storage.createStorageErrorId("LIBSQL", "LIST_THREADS_BY_RESOURCE_ID", "INVALID_PAGE"),
2431
+ id: storage.createStorageErrorId("LIBSQL", "LIST_MESSAGES", "INVALID_PAGE"),
1924
2432
  domain: error.ErrorDomain.STORAGE,
1925
2433
  category: error.ErrorCategory.USER,
1926
2434
  details: { page }
@@ -1928,973 +2436,1239 @@ var MemoryLibSQL = class extends storage.MemoryStorage {
1928
2436
  new Error("page must be >= 0")
1929
2437
  );
1930
2438
  }
1931
- const perPage = storage.normalizePerPage(perPageInput, 100);
2439
+ const perPage = storage.normalizePerPage(perPageInput, 40);
1932
2440
  const { offset, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
1933
- const { field, direction } = this.parseOrderBy(orderBy);
1934
2441
  try {
1935
- const baseQuery = `FROM ${storage.TABLE_THREADS} WHERE resourceId = ?`;
1936
- const queryParams = [resourceId];
1937
- const mapRowToStorageThreadType = (row) => ({
1938
- id: row.id,
1939
- resourceId: row.resourceId,
1940
- title: row.title,
1941
- createdAt: new Date(row.createdAt),
1942
- // Convert string to Date
1943
- updatedAt: new Date(row.updatedAt),
1944
- // Convert string to Date
1945
- metadata: typeof row.metadata === "string" ? JSON.parse(row.metadata) : row.metadata
1946
- });
1947
- const countResult = await this.client.execute({
1948
- sql: `SELECT COUNT(*) as count ${baseQuery}`,
2442
+ const { field, direction } = this.parseOrderBy(orderBy, "ASC");
2443
+ const orderByStatement = `ORDER BY "${field}" ${direction}`;
2444
+ const threadPlaceholders = threadIds.map(() => "?").join(", ");
2445
+ const conditions = [`thread_id IN (${threadPlaceholders})`];
2446
+ const queryParams = [...threadIds];
2447
+ if (resourceId) {
2448
+ conditions.push(`"resourceId" = ?`);
2449
+ queryParams.push(resourceId);
2450
+ }
2451
+ if (filter?.dateRange?.start) {
2452
+ const startOp = filter.dateRange.startExclusive ? ">" : ">=";
2453
+ conditions.push(`"createdAt" ${startOp} ?`);
2454
+ queryParams.push(
2455
+ filter.dateRange.start instanceof Date ? filter.dateRange.start.toISOString() : filter.dateRange.start
2456
+ );
2457
+ }
2458
+ if (filter?.dateRange?.end) {
2459
+ const endOp = filter.dateRange.endExclusive ? "<" : "<=";
2460
+ conditions.push(`"createdAt" ${endOp} ?`);
2461
+ queryParams.push(
2462
+ filter.dateRange.end instanceof Date ? filter.dateRange.end.toISOString() : filter.dateRange.end
2463
+ );
2464
+ }
2465
+ const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
2466
+ const countResult = await this.#client.execute({
2467
+ sql: `SELECT COUNT(*) as count FROM ${storage.TABLE_MESSAGES} ${whereClause}`,
1949
2468
  args: queryParams
1950
2469
  });
1951
2470
  const total = Number(countResult.rows?.[0]?.count ?? 0);
1952
- if (total === 0) {
2471
+ const limitValue = perPageInput === false ? total : perPage;
2472
+ const dataResult = await this.#client.execute({
2473
+ sql: `SELECT id, content, role, type, "createdAt", "resourceId", "thread_id" FROM ${storage.TABLE_MESSAGES} ${whereClause} ${orderByStatement} LIMIT ? OFFSET ?`,
2474
+ args: [...queryParams, limitValue, offset]
2475
+ });
2476
+ const messages = (dataResult.rows || []).map((row) => this.parseRow(row));
2477
+ if (total === 0 && messages.length === 0 && (!include || include.length === 0)) {
1953
2478
  return {
1954
- threads: [],
2479
+ messages: [],
1955
2480
  total: 0,
1956
2481
  page,
1957
2482
  perPage: perPageForResponse,
1958
2483
  hasMore: false
1959
2484
  };
1960
2485
  }
1961
- const limitValue = perPageInput === false ? total : perPage;
1962
- const dataResult = await this.client.execute({
1963
- sql: `SELECT * ${baseQuery} ORDER BY "${field}" ${direction} LIMIT ? OFFSET ?`,
1964
- args: [...queryParams, limitValue, offset]
2486
+ const messageIds = new Set(messages.map((m) => m.id));
2487
+ if (include && include.length > 0) {
2488
+ const includeMessages = await this._getIncludedMessages({ include });
2489
+ if (includeMessages) {
2490
+ for (const includeMsg of includeMessages) {
2491
+ if (!messageIds.has(includeMsg.id)) {
2492
+ messages.push(includeMsg);
2493
+ messageIds.add(includeMsg.id);
2494
+ }
2495
+ }
2496
+ }
2497
+ }
2498
+ const list = new agent.MessageList().add(messages, "memory");
2499
+ let finalMessages = list.get.all.db();
2500
+ finalMessages = finalMessages.sort((a, b) => {
2501
+ const isDateField = field === "createdAt" || field === "updatedAt";
2502
+ const aValue = isDateField ? new Date(a[field]).getTime() : a[field];
2503
+ const bValue = isDateField ? new Date(b[field]).getTime() : b[field];
2504
+ if (typeof aValue === "number" && typeof bValue === "number") {
2505
+ return direction === "ASC" ? aValue - bValue : bValue - aValue;
2506
+ }
2507
+ return direction === "ASC" ? String(aValue).localeCompare(String(bValue)) : String(bValue).localeCompare(String(aValue));
1965
2508
  });
1966
- const threads = (dataResult.rows || []).map(mapRowToStorageThreadType);
1967
- return {
1968
- threads,
1969
- total,
1970
- page,
1971
- perPage: perPageForResponse,
1972
- hasMore: perPageInput === false ? false : offset + perPage < total
1973
- };
1974
- } catch (error$1) {
1975
- const mastraError = new error.MastraError(
1976
- {
1977
- id: storage.createStorageErrorId("LIBSQL", "LIST_THREADS_BY_RESOURCE_ID", "FAILED"),
1978
- domain: error.ErrorDomain.STORAGE,
1979
- category: error.ErrorCategory.THIRD_PARTY,
1980
- details: { resourceId }
1981
- },
1982
- error$1
2509
+ const threadIdSet = new Set(threadIds);
2510
+ const returnedThreadMessageIds = new Set(
2511
+ finalMessages.filter((m) => m.threadId && threadIdSet.has(m.threadId)).map((m) => m.id)
1983
2512
  );
1984
- this.logger?.trackException?.(mastraError);
1985
- this.logger?.error?.(mastraError.toString());
2513
+ const allThreadMessagesReturned = returnedThreadMessageIds.size >= total;
2514
+ const hasMore = perPageInput !== false && !allThreadMessagesReturned && offset + perPage < total;
1986
2515
  return {
1987
- threads: [],
1988
- total: 0,
2516
+ messages: finalMessages,
2517
+ total,
1989
2518
  page,
1990
2519
  perPage: perPageForResponse,
1991
- hasMore: false
2520
+ hasMore
1992
2521
  };
1993
- }
1994
- }
1995
- async saveThread({ thread }) {
1996
- try {
1997
- await this.operations.insert({
1998
- tableName: storage.TABLE_THREADS,
1999
- record: {
2000
- ...thread,
2001
- metadata: JSON.stringify(thread.metadata)
2002
- }
2003
- });
2004
- return thread;
2005
2522
  } catch (error$1) {
2006
2523
  const mastraError = new error.MastraError(
2007
2524
  {
2008
- id: storage.createStorageErrorId("LIBSQL", "SAVE_THREAD", "FAILED"),
2009
- domain: error.ErrorDomain.STORAGE,
2010
- category: error.ErrorCategory.THIRD_PARTY,
2011
- details: { threadId: thread.id }
2012
- },
2013
- error$1
2014
- );
2015
- this.logger?.trackException?.(mastraError);
2016
- this.logger?.error?.(mastraError.toString());
2017
- throw mastraError;
2018
- }
2019
- }
2020
- async updateThread({
2021
- id,
2022
- title,
2023
- metadata
2024
- }) {
2025
- const thread = await this.getThreadById({ threadId: id });
2026
- if (!thread) {
2027
- throw new error.MastraError({
2028
- id: storage.createStorageErrorId("LIBSQL", "UPDATE_THREAD", "NOT_FOUND"),
2029
- domain: error.ErrorDomain.STORAGE,
2030
- category: error.ErrorCategory.USER,
2031
- text: `Thread ${id} not found`,
2032
- details: {
2033
- status: 404,
2034
- threadId: id
2035
- }
2036
- });
2037
- }
2038
- const updatedThread = {
2039
- ...thread,
2040
- title,
2041
- metadata: {
2042
- ...thread.metadata,
2043
- ...metadata
2044
- }
2045
- };
2046
- try {
2047
- await this.client.execute({
2048
- sql: `UPDATE ${storage.TABLE_THREADS} SET title = ?, metadata = ? WHERE id = ?`,
2049
- args: [title, JSON.stringify(updatedThread.metadata), id]
2050
- });
2051
- return updatedThread;
2052
- } catch (error$1) {
2053
- throw new error.MastraError(
2054
- {
2055
- id: storage.createStorageErrorId("LIBSQL", "UPDATE_THREAD", "FAILED"),
2525
+ id: storage.createStorageErrorId("LIBSQL", "LIST_MESSAGES", "FAILED"),
2056
2526
  domain: error.ErrorDomain.STORAGE,
2057
- category: error.ErrorCategory.THIRD_PARTY,
2058
- text: `Failed to update thread ${id}`,
2059
- details: { threadId: id }
2527
+ category: error.ErrorCategory.THIRD_PARTY,
2528
+ details: {
2529
+ threadId: Array.isArray(threadId) ? threadId.join(",") : threadId,
2530
+ resourceId: resourceId ?? ""
2531
+ }
2060
2532
  },
2061
2533
  error$1
2062
2534
  );
2535
+ this.logger?.error?.(mastraError.toString());
2536
+ this.logger?.trackException?.(mastraError);
2537
+ return {
2538
+ messages: [],
2539
+ total: 0,
2540
+ page,
2541
+ perPage: perPageForResponse,
2542
+ hasMore: false
2543
+ };
2063
2544
  }
2064
2545
  }
2065
- async deleteThread({ threadId }) {
2546
+ async saveMessages({ messages }) {
2547
+ if (messages.length === 0) return { messages };
2066
2548
  try {
2067
- await this.client.execute({
2068
- sql: `DELETE FROM ${storage.TABLE_MESSAGES} WHERE thread_id = ?`,
2069
- args: [threadId]
2549
+ const threadId = messages[0]?.threadId;
2550
+ if (!threadId) {
2551
+ throw new Error("Thread ID is required");
2552
+ }
2553
+ const batchStatements = messages.map((message) => {
2554
+ const time = message.createdAt || /* @__PURE__ */ new Date();
2555
+ if (!message.threadId) {
2556
+ throw new Error(
2557
+ `Expected to find a threadId for message, but couldn't find one. An unexpected error has occurred.`
2558
+ );
2559
+ }
2560
+ if (!message.resourceId) {
2561
+ throw new Error(
2562
+ `Expected to find a resourceId for message, but couldn't find one. An unexpected error has occurred.`
2563
+ );
2564
+ }
2565
+ return {
2566
+ sql: `INSERT INTO "${storage.TABLE_MESSAGES}" (id, thread_id, content, role, type, "createdAt", "resourceId")
2567
+ VALUES (?, ?, ?, ?, ?, ?, ?)
2568
+ ON CONFLICT(id) DO UPDATE SET
2569
+ thread_id=excluded.thread_id,
2570
+ content=excluded.content,
2571
+ role=excluded.role,
2572
+ type=excluded.type,
2573
+ "resourceId"=excluded."resourceId"
2574
+ `,
2575
+ args: [
2576
+ message.id,
2577
+ message.threadId,
2578
+ typeof message.content === "object" ? JSON.stringify(message.content) : message.content,
2579
+ message.role,
2580
+ message.type || "v2",
2581
+ time instanceof Date ? time.toISOString() : time,
2582
+ message.resourceId
2583
+ ]
2584
+ };
2070
2585
  });
2071
- await this.client.execute({
2072
- sql: `DELETE FROM ${storage.TABLE_THREADS} WHERE id = ?`,
2073
- args: [threadId]
2586
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2587
+ batchStatements.push({
2588
+ sql: `UPDATE "${storage.TABLE_THREADS}" SET "updatedAt" = ? WHERE id = ?`,
2589
+ args: [now, threadId]
2074
2590
  });
2591
+ const BATCH_SIZE = 50;
2592
+ const messageStatements = batchStatements.slice(0, -1);
2593
+ const threadUpdateStatement = batchStatements[batchStatements.length - 1];
2594
+ for (let i = 0; i < messageStatements.length; i += BATCH_SIZE) {
2595
+ const batch = messageStatements.slice(i, i + BATCH_SIZE);
2596
+ if (batch.length > 0) {
2597
+ await this.#client.batch(batch, "write");
2598
+ }
2599
+ }
2600
+ if (threadUpdateStatement) {
2601
+ await this.#client.execute(threadUpdateStatement);
2602
+ }
2603
+ const list = new agent.MessageList().add(messages, "memory");
2604
+ return { messages: list.get.all.db() };
2075
2605
  } catch (error$1) {
2076
2606
  throw new error.MastraError(
2077
2607
  {
2078
- id: storage.createStorageErrorId("LIBSQL", "DELETE_THREAD", "FAILED"),
2608
+ id: storage.createStorageErrorId("LIBSQL", "SAVE_MESSAGES", "FAILED"),
2079
2609
  domain: error.ErrorDomain.STORAGE,
2080
- category: error.ErrorCategory.THIRD_PARTY,
2081
- details: { threadId }
2610
+ category: error.ErrorCategory.THIRD_PARTY
2082
2611
  },
2083
2612
  error$1
2084
2613
  );
2085
2614
  }
2086
2615
  }
2087
- };
2088
- function createExecuteWriteOperationWithRetry({
2089
- logger,
2090
- maxRetries,
2091
- initialBackoffMs
2092
- }) {
2093
- return async function executeWriteOperationWithRetry(operationFn, operationDescription) {
2094
- let retries = 0;
2095
- while (true) {
2096
- try {
2097
- return await operationFn();
2098
- } catch (error) {
2099
- if (error.message && (error.message.includes("SQLITE_BUSY") || error.message.includes("database is locked")) && retries < maxRetries) {
2100
- retries++;
2101
- const backoffTime = initialBackoffMs * Math.pow(2, retries - 1);
2102
- logger.warn(
2103
- `LibSQLStore: Encountered SQLITE_BUSY during ${operationDescription}. Retrying (${retries}/${maxRetries}) in ${backoffTime}ms...`
2104
- );
2105
- await new Promise((resolve) => setTimeout(resolve, backoffTime));
2106
- } else {
2107
- logger.error(`LibSQLStore: Error during ${operationDescription} after ${retries} retries: ${error}`);
2108
- throw error;
2109
- }
2110
- }
2111
- }
2112
- };
2113
- }
2114
- function prepareStatement({ tableName, record }) {
2115
- const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
2116
- const columns = Object.keys(record).map((col) => utils.parseSqlIdentifier(col, "column name"));
2117
- const values = Object.values(record).map((v) => {
2118
- if (typeof v === `undefined` || v === null) {
2119
- return null;
2120
- }
2121
- if (v instanceof Date) {
2122
- return v.toISOString();
2616
+ async updateMessages({
2617
+ messages
2618
+ }) {
2619
+ if (messages.length === 0) {
2620
+ return [];
2123
2621
  }
2124
- return typeof v === "object" ? JSON.stringify(v) : v;
2125
- });
2126
- const placeholders = values.map(() => "?").join(", ");
2127
- return {
2128
- sql: `INSERT OR REPLACE INTO ${parsedTableName} (${columns.join(", ")}) VALUES (${placeholders})`,
2129
- args: values
2130
- };
2131
- }
2132
- function prepareUpdateStatement({
2133
- tableName,
2134
- updates,
2135
- keys
2136
- }) {
2137
- const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
2138
- const schema = storage.TABLE_SCHEMAS[tableName];
2139
- const updateColumns = Object.keys(updates).map((col) => utils.parseSqlIdentifier(col, "column name"));
2140
- const updateValues = Object.values(updates).map(transformToSqlValue);
2141
- const setClause = updateColumns.map((col) => `${col} = ?`).join(", ");
2142
- const whereClause = prepareWhereClause(keys, schema);
2143
- return {
2144
- sql: `UPDATE ${parsedTableName} SET ${setClause}${whereClause.sql}`,
2145
- args: [...updateValues, ...whereClause.args]
2146
- };
2147
- }
2148
- function transformToSqlValue(value) {
2149
- if (typeof value === "undefined" || value === null) {
2150
- return null;
2151
- }
2152
- if (value instanceof Date) {
2153
- return value.toISOString();
2154
- }
2155
- return typeof value === "object" ? JSON.stringify(value) : value;
2156
- }
2157
- function prepareDeleteStatement({ tableName, keys }) {
2158
- const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
2159
- const whereClause = prepareWhereClause(keys, storage.TABLE_SCHEMAS[tableName]);
2160
- return {
2161
- sql: `DELETE FROM ${parsedTableName}${whereClause.sql}`,
2162
- args: whereClause.args
2163
- };
2164
- }
2165
- function prepareWhereClause(filters, schema) {
2166
- const conditions = [];
2167
- const args = [];
2168
- for (const [columnName, filterValue] of Object.entries(filters)) {
2169
- const column = schema[columnName];
2170
- if (!column) {
2171
- throw new Error(`Unknown column: ${columnName}`);
2622
+ const messageIds = messages.map((m) => m.id);
2623
+ const placeholders = messageIds.map(() => "?").join(",");
2624
+ const selectSql = `SELECT * FROM ${storage.TABLE_MESSAGES} WHERE id IN (${placeholders})`;
2625
+ const existingResult = await this.#client.execute({ sql: selectSql, args: messageIds });
2626
+ const existingMessages = existingResult.rows.map((row) => this.parseRow(row));
2627
+ if (existingMessages.length === 0) {
2628
+ return [];
2172
2629
  }
2173
- const parsedColumn = utils.parseSqlIdentifier(columnName, "column name");
2174
- const result = buildCondition2(parsedColumn, filterValue);
2175
- conditions.push(result.condition);
2176
- args.push(...result.args);
2177
- }
2178
- return {
2179
- sql: conditions.length > 0 ? ` WHERE ${conditions.join(" AND ")}` : "",
2180
- args
2181
- };
2182
- }
2183
- function buildCondition2(columnName, filterValue) {
2184
- if (filterValue === null) {
2185
- return { condition: `${columnName} IS NULL`, args: [] };
2186
- }
2187
- if (typeof filterValue === "object" && filterValue !== null && ("startAt" in filterValue || "endAt" in filterValue)) {
2188
- return buildDateRangeCondition(columnName, filterValue);
2189
- }
2190
- return {
2191
- condition: `${columnName} = ?`,
2192
- args: [transformToSqlValue(filterValue)]
2193
- };
2194
- }
2195
- function buildDateRangeCondition(columnName, range) {
2196
- const conditions = [];
2197
- const args = [];
2198
- if (range.startAt !== void 0) {
2199
- conditions.push(`${columnName} >= ?`);
2200
- args.push(transformToSqlValue(range.startAt));
2201
- }
2202
- if (range.endAt !== void 0) {
2203
- conditions.push(`${columnName} <= ?`);
2204
- args.push(transformToSqlValue(range.endAt));
2205
- }
2206
- if (conditions.length === 0) {
2207
- throw new Error("Date range must specify at least startAt or endAt");
2208
- }
2209
- return {
2210
- condition: conditions.join(" AND "),
2211
- args
2212
- };
2213
- }
2214
- function buildDateRangeFilter(dateRange, columnName = "createdAt") {
2215
- if (!dateRange?.start && !dateRange?.end) {
2216
- return {};
2217
- }
2218
- const filter = {};
2219
- if (dateRange.start) {
2220
- filter.startAt = new Date(dateRange.start).toISOString();
2221
- }
2222
- if (dateRange.end) {
2223
- filter.endAt = new Date(dateRange.end).toISOString();
2224
- }
2225
- return { [columnName]: filter };
2226
- }
2227
- function transformFromSqlRow({
2228
- tableName,
2229
- sqlRow
2230
- }) {
2231
- const result = {};
2232
- const jsonColumns = new Set(
2233
- Object.keys(storage.TABLE_SCHEMAS[tableName]).filter((key) => storage.TABLE_SCHEMAS[tableName][key].type === "jsonb").map((key) => key)
2234
- );
2235
- const dateColumns = new Set(
2236
- Object.keys(storage.TABLE_SCHEMAS[tableName]).filter((key) => storage.TABLE_SCHEMAS[tableName][key].type === "timestamp").map((key) => key)
2237
- );
2238
- for (const [key, value] of Object.entries(sqlRow)) {
2239
- if (value === null || value === void 0) {
2240
- result[key] = value;
2241
- continue;
2630
+ const batchStatements = [];
2631
+ const threadIdsToUpdate = /* @__PURE__ */ new Set();
2632
+ const columnMapping = {
2633
+ threadId: "thread_id"
2634
+ };
2635
+ for (const existingMessage of existingMessages) {
2636
+ const updatePayload = messages.find((m) => m.id === existingMessage.id);
2637
+ if (!updatePayload) continue;
2638
+ const { id, ...fieldsToUpdate } = updatePayload;
2639
+ if (Object.keys(fieldsToUpdate).length === 0) continue;
2640
+ threadIdsToUpdate.add(existingMessage.threadId);
2641
+ if (updatePayload.threadId && updatePayload.threadId !== existingMessage.threadId) {
2642
+ threadIdsToUpdate.add(updatePayload.threadId);
2643
+ }
2644
+ const setClauses = [];
2645
+ const args = [];
2646
+ const updatableFields = { ...fieldsToUpdate };
2647
+ if (updatableFields.content) {
2648
+ const newContent = {
2649
+ ...existingMessage.content,
2650
+ ...updatableFields.content,
2651
+ // Deep merge metadata if it exists on both
2652
+ ...existingMessage.content?.metadata && updatableFields.content.metadata ? {
2653
+ metadata: {
2654
+ ...existingMessage.content.metadata,
2655
+ ...updatableFields.content.metadata
2656
+ }
2657
+ } : {}
2658
+ };
2659
+ setClauses.push(`${utils.parseSqlIdentifier("content", "column name")} = ?`);
2660
+ args.push(JSON.stringify(newContent));
2661
+ delete updatableFields.content;
2662
+ }
2663
+ for (const key in updatableFields) {
2664
+ if (Object.prototype.hasOwnProperty.call(updatableFields, key)) {
2665
+ const dbKey = columnMapping[key] || key;
2666
+ setClauses.push(`${utils.parseSqlIdentifier(dbKey, "column name")} = ?`);
2667
+ let value = updatableFields[key];
2668
+ if (typeof value === "object" && value !== null) {
2669
+ value = JSON.stringify(value);
2670
+ }
2671
+ args.push(value);
2672
+ }
2673
+ }
2674
+ if (setClauses.length === 0) continue;
2675
+ args.push(id);
2676
+ const sql = `UPDATE ${storage.TABLE_MESSAGES} SET ${setClauses.join(", ")} WHERE id = ?`;
2677
+ batchStatements.push({ sql, args });
2242
2678
  }
2243
- if (dateColumns.has(key) && typeof value === "string") {
2244
- result[key] = new Date(value);
2245
- continue;
2679
+ if (batchStatements.length === 0) {
2680
+ return existingMessages;
2246
2681
  }
2247
- if (jsonColumns.has(key) && typeof value === "string") {
2248
- result[key] = storage.safelyParseJSON(value);
2249
- continue;
2682
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2683
+ for (const threadId of threadIdsToUpdate) {
2684
+ if (threadId) {
2685
+ batchStatements.push({
2686
+ sql: `UPDATE ${storage.TABLE_THREADS} SET updatedAt = ? WHERE id = ?`,
2687
+ args: [now, threadId]
2688
+ });
2689
+ }
2250
2690
  }
2251
- result[key] = value;
2252
- }
2253
- return result;
2254
- }
2255
-
2256
- // src/storage/domains/observability/index.ts
2257
- var ObservabilityLibSQL = class extends storage.ObservabilityStorage {
2258
- operations;
2259
- constructor({ operations }) {
2260
- super();
2261
- this.operations = operations;
2691
+ await this.#client.batch(batchStatements, "write");
2692
+ const updatedResult = await this.#client.execute({ sql: selectSql, args: messageIds });
2693
+ return updatedResult.rows.map((row) => this.parseRow(row));
2262
2694
  }
2263
- async createSpan(span) {
2695
+ async deleteMessages(messageIds) {
2696
+ if (!messageIds || messageIds.length === 0) {
2697
+ return;
2698
+ }
2264
2699
  try {
2265
- const now = (/* @__PURE__ */ new Date()).toISOString();
2266
- const record = {
2267
- ...span,
2268
- createdAt: now,
2269
- updatedAt: now
2270
- };
2271
- return this.operations.insert({ tableName: storage.TABLE_SPANS, record });
2700
+ const BATCH_SIZE = 100;
2701
+ const threadIds = /* @__PURE__ */ new Set();
2702
+ const tx = await this.#client.transaction("write");
2703
+ try {
2704
+ for (let i = 0; i < messageIds.length; i += BATCH_SIZE) {
2705
+ const batch = messageIds.slice(i, i + BATCH_SIZE);
2706
+ const placeholders = batch.map(() => "?").join(",");
2707
+ const result = await tx.execute({
2708
+ sql: `SELECT DISTINCT thread_id FROM "${storage.TABLE_MESSAGES}" WHERE id IN (${placeholders})`,
2709
+ args: batch
2710
+ });
2711
+ result.rows?.forEach((row) => {
2712
+ if (row.thread_id) threadIds.add(row.thread_id);
2713
+ });
2714
+ await tx.execute({
2715
+ sql: `DELETE FROM "${storage.TABLE_MESSAGES}" WHERE id IN (${placeholders})`,
2716
+ args: batch
2717
+ });
2718
+ }
2719
+ if (threadIds.size > 0) {
2720
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2721
+ for (const threadId of threadIds) {
2722
+ await tx.execute({
2723
+ sql: `UPDATE "${storage.TABLE_THREADS}" SET "updatedAt" = ? WHERE id = ?`,
2724
+ args: [now, threadId]
2725
+ });
2726
+ }
2727
+ }
2728
+ await tx.commit();
2729
+ } catch (error) {
2730
+ await tx.rollback();
2731
+ throw error;
2732
+ }
2272
2733
  } catch (error$1) {
2273
2734
  throw new error.MastraError(
2274
2735
  {
2275
- id: storage.createStorageErrorId("LIBSQL", "CREATE_SPAN", "FAILED"),
2736
+ id: storage.createStorageErrorId("LIBSQL", "DELETE_MESSAGES", "FAILED"),
2276
2737
  domain: error.ErrorDomain.STORAGE,
2277
- category: error.ErrorCategory.USER,
2278
- details: {
2279
- spanId: span.spanId,
2280
- traceId: span.traceId,
2281
- spanType: span.spanType,
2282
- spanName: span.name
2283
- }
2738
+ category: error.ErrorCategory.THIRD_PARTY,
2739
+ details: { messageIds: messageIds.join(", ") }
2284
2740
  },
2285
2741
  error$1
2286
2742
  );
2287
2743
  }
2288
2744
  }
2289
- async getTrace(traceId) {
2745
+ async getResourceById({ resourceId }) {
2746
+ const result = await this.#db.select({
2747
+ tableName: storage.TABLE_RESOURCES,
2748
+ keys: { id: resourceId }
2749
+ });
2750
+ if (!result) {
2751
+ return null;
2752
+ }
2753
+ return {
2754
+ ...result,
2755
+ // Ensure workingMemory is always returned as a string, even if auto-parsed as JSON
2756
+ workingMemory: result.workingMemory && typeof result.workingMemory === "object" ? JSON.stringify(result.workingMemory) : result.workingMemory,
2757
+ metadata: typeof result.metadata === "string" ? JSON.parse(result.metadata) : result.metadata,
2758
+ createdAt: new Date(result.createdAt),
2759
+ updatedAt: new Date(result.updatedAt)
2760
+ };
2761
+ }
2762
+ async saveResource({ resource }) {
2763
+ await this.#db.insert({
2764
+ tableName: storage.TABLE_RESOURCES,
2765
+ record: {
2766
+ ...resource
2767
+ // metadata is handled by prepareStatement which stringifies jsonb columns
2768
+ }
2769
+ });
2770
+ return resource;
2771
+ }
2772
+ async updateResource({
2773
+ resourceId,
2774
+ workingMemory,
2775
+ metadata
2776
+ }) {
2777
+ const existingResource = await this.getResourceById({ resourceId });
2778
+ if (!existingResource) {
2779
+ const newResource = {
2780
+ id: resourceId,
2781
+ workingMemory,
2782
+ metadata: metadata || {},
2783
+ createdAt: /* @__PURE__ */ new Date(),
2784
+ updatedAt: /* @__PURE__ */ new Date()
2785
+ };
2786
+ return this.saveResource({ resource: newResource });
2787
+ }
2788
+ const updatedResource = {
2789
+ ...existingResource,
2790
+ workingMemory: workingMemory !== void 0 ? workingMemory : existingResource.workingMemory,
2791
+ metadata: {
2792
+ ...existingResource.metadata,
2793
+ ...metadata
2794
+ },
2795
+ updatedAt: /* @__PURE__ */ new Date()
2796
+ };
2797
+ const updates = [];
2798
+ const values = [];
2799
+ if (workingMemory !== void 0) {
2800
+ updates.push("workingMemory = ?");
2801
+ values.push(workingMemory);
2802
+ }
2803
+ if (metadata) {
2804
+ updates.push("metadata = jsonb(?)");
2805
+ values.push(JSON.stringify(updatedResource.metadata));
2806
+ }
2807
+ updates.push("updatedAt = ?");
2808
+ values.push(updatedResource.updatedAt.toISOString());
2809
+ values.push(resourceId);
2810
+ await this.#client.execute({
2811
+ sql: `UPDATE ${storage.TABLE_RESOURCES} SET ${updates.join(", ")} WHERE id = ?`,
2812
+ args: values
2813
+ });
2814
+ return updatedResource;
2815
+ }
2816
+ async getThreadById({ threadId }) {
2290
2817
  try {
2291
- const spans = await this.operations.loadMany({
2292
- tableName: storage.TABLE_SPANS,
2293
- whereClause: { sql: " WHERE traceId = ?", args: [traceId] },
2294
- orderBy: "startedAt DESC"
2818
+ const result = await this.#db.select({
2819
+ tableName: storage.TABLE_THREADS,
2820
+ keys: { id: threadId }
2295
2821
  });
2296
- if (!spans || spans.length === 0) {
2822
+ if (!result) {
2297
2823
  return null;
2298
2824
  }
2299
2825
  return {
2300
- traceId,
2301
- spans: spans.map((span) => transformFromSqlRow({ tableName: storage.TABLE_SPANS, sqlRow: span }))
2826
+ ...result,
2827
+ metadata: typeof result.metadata === "string" ? JSON.parse(result.metadata) : result.metadata,
2828
+ createdAt: new Date(result.createdAt),
2829
+ updatedAt: new Date(result.updatedAt)
2302
2830
  };
2303
2831
  } catch (error$1) {
2304
2832
  throw new error.MastraError(
2305
2833
  {
2306
- id: storage.createStorageErrorId("LIBSQL", "GET_TRACE", "FAILED"),
2834
+ id: storage.createStorageErrorId("LIBSQL", "GET_THREAD_BY_ID", "FAILED"),
2307
2835
  domain: error.ErrorDomain.STORAGE,
2308
- category: error.ErrorCategory.USER,
2309
- details: {
2310
- traceId
2311
- }
2836
+ category: error.ErrorCategory.THIRD_PARTY,
2837
+ details: { threadId }
2312
2838
  },
2313
2839
  error$1
2314
2840
  );
2315
2841
  }
2316
2842
  }
2317
- async updateSpan({
2318
- spanId,
2319
- traceId,
2320
- updates
2321
- }) {
2843
+ async listThreads(args) {
2844
+ const { page = 0, perPage: perPageInput, orderBy, filter } = args;
2322
2845
  try {
2323
- await this.operations.update({
2324
- tableName: storage.TABLE_SPANS,
2325
- keys: { spanId, traceId },
2326
- data: { ...updates, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }
2327
- });
2846
+ this.validatePaginationInput(page, perPageInput ?? 100);
2328
2847
  } catch (error$1) {
2329
2848
  throw new error.MastraError(
2330
2849
  {
2331
- id: storage.createStorageErrorId("LIBSQL", "UPDATE_SPAN", "FAILED"),
2850
+ id: storage.createStorageErrorId("LIBSQL", "LIST_THREADS", "INVALID_PAGE"),
2332
2851
  domain: error.ErrorDomain.STORAGE,
2333
2852
  category: error.ErrorCategory.USER,
2334
- details: {
2335
- spanId,
2336
- traceId
2337
- }
2853
+ details: { page, ...perPageInput !== void 0 && { perPage: perPageInput } }
2338
2854
  },
2339
- error$1
2855
+ error$1 instanceof Error ? error$1 : new Error("Invalid pagination parameters")
2340
2856
  );
2341
2857
  }
2342
- }
2343
- async getTracesPaginated({
2344
- filters,
2345
- pagination
2346
- }) {
2347
- const page = pagination?.page ?? 0;
2348
- const perPage = pagination?.perPage ?? 10;
2349
- const { entityId, entityType, ...actualFilters } = filters || {};
2350
- const filtersWithDateRange = {
2351
- ...actualFilters,
2352
- ...buildDateRangeFilter(pagination?.dateRange, "startedAt"),
2353
- parentSpanId: null
2354
- };
2355
- const whereClause = prepareWhereClause(filtersWithDateRange, storage.SPAN_SCHEMA);
2356
- let actualWhereClause = whereClause.sql || "";
2357
- if (entityId && entityType) {
2358
- const statement = `name = ?`;
2359
- let name = "";
2360
- if (entityType === "workflow") {
2361
- name = `workflow run: '${entityId}'`;
2362
- } else if (entityType === "agent") {
2363
- name = `agent run: '${entityId}'`;
2364
- } else {
2365
- const error$1 = new error.MastraError({
2366
- id: storage.createStorageErrorId("LIBSQL", "GET_TRACES_PAGINATED", "INVALID_ENTITY_TYPE"),
2367
- domain: error.ErrorDomain.STORAGE,
2368
- category: error.ErrorCategory.USER,
2369
- details: {
2370
- entityType
2371
- },
2372
- text: `Cannot filter by entity type: ${entityType}`
2373
- });
2374
- this.logger?.trackException(error$1);
2375
- throw error$1;
2376
- }
2377
- whereClause.args.push(name);
2378
- if (actualWhereClause) {
2379
- actualWhereClause += ` AND ${statement}`;
2380
- } else {
2381
- actualWhereClause += `WHERE ${statement}`;
2382
- }
2383
- }
2384
- const orderBy = "startedAt DESC";
2385
- let count = 0;
2858
+ const perPage = storage.normalizePerPage(perPageInput, 100);
2386
2859
  try {
2387
- count = await this.operations.loadTotalCount({
2388
- tableName: storage.TABLE_SPANS,
2389
- whereClause: { sql: actualWhereClause, args: whereClause.args }
2390
- });
2860
+ this.validateMetadataKeys(filter?.metadata);
2391
2861
  } catch (error$1) {
2392
2862
  throw new error.MastraError(
2393
2863
  {
2394
- id: storage.createStorageErrorId("LIBSQL", "GET_TRACES_PAGINATED", "COUNT_FAILED"),
2864
+ id: storage.createStorageErrorId("LIBSQL", "LIST_THREADS", "INVALID_METADATA_KEY"),
2395
2865
  domain: error.ErrorDomain.STORAGE,
2396
- category: error.ErrorCategory.USER
2866
+ category: error.ErrorCategory.USER,
2867
+ details: { metadataKeys: filter?.metadata ? Object.keys(filter.metadata).join(", ") : "" }
2397
2868
  },
2398
- error$1
2869
+ error$1 instanceof Error ? error$1 : new Error("Invalid metadata key")
2399
2870
  );
2400
2871
  }
2401
- if (count === 0) {
2402
- return {
2403
- pagination: {
2872
+ const { offset, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
2873
+ const { field, direction } = this.parseOrderBy(orderBy);
2874
+ try {
2875
+ const whereClauses = [];
2876
+ const queryParams = [];
2877
+ if (filter?.resourceId) {
2878
+ whereClauses.push("resourceId = ?");
2879
+ queryParams.push(filter.resourceId);
2880
+ }
2881
+ if (filter?.metadata && Object.keys(filter.metadata).length > 0) {
2882
+ for (const [key, value] of Object.entries(filter.metadata)) {
2883
+ if (value === null) {
2884
+ whereClauses.push(`json_extract(metadata, '$.${key}') IS NULL`);
2885
+ } else if (typeof value === "boolean") {
2886
+ whereClauses.push(`json_extract(metadata, '$.${key}') = ?`);
2887
+ queryParams.push(value ? 1 : 0);
2888
+ } else if (typeof value === "number") {
2889
+ whereClauses.push(`json_extract(metadata, '$.${key}') = ?`);
2890
+ queryParams.push(value);
2891
+ } else if (typeof value === "string") {
2892
+ whereClauses.push(`json_extract(metadata, '$.${key}') = ?`);
2893
+ queryParams.push(value);
2894
+ } else {
2895
+ throw new error.MastraError({
2896
+ id: storage.createStorageErrorId("LIBSQL", "LIST_THREADS", "INVALID_METADATA_VALUE"),
2897
+ domain: error.ErrorDomain.STORAGE,
2898
+ category: error.ErrorCategory.USER,
2899
+ text: `Metadata filter value for key "${key}" must be a scalar type (string, number, boolean, or null), got ${typeof value}`,
2900
+ details: { key, valueType: typeof value }
2901
+ });
2902
+ }
2903
+ }
2904
+ }
2905
+ const whereClause = whereClauses.length > 0 ? `WHERE ${whereClauses.join(" AND ")}` : "";
2906
+ const baseQuery = `FROM ${storage.TABLE_THREADS} ${whereClause}`;
2907
+ const mapRowToStorageThreadType = (row) => ({
2908
+ id: row.id,
2909
+ resourceId: row.resourceId,
2910
+ title: row.title,
2911
+ createdAt: new Date(row.createdAt),
2912
+ updatedAt: new Date(row.updatedAt),
2913
+ metadata: typeof row.metadata === "string" ? JSON.parse(row.metadata) : row.metadata
2914
+ });
2915
+ const countResult = await this.#client.execute({
2916
+ sql: `SELECT COUNT(*) as count ${baseQuery}`,
2917
+ args: queryParams
2918
+ });
2919
+ const total = Number(countResult.rows?.[0]?.count ?? 0);
2920
+ if (total === 0) {
2921
+ return {
2922
+ threads: [],
2404
2923
  total: 0,
2405
2924
  page,
2406
- perPage,
2925
+ perPage: perPageForResponse,
2407
2926
  hasMore: false
2408
- },
2409
- spans: []
2410
- };
2411
- }
2412
- try {
2413
- const spans = await this.operations.loadMany({
2414
- tableName: storage.TABLE_SPANS,
2415
- whereClause: {
2416
- sql: actualWhereClause,
2417
- args: whereClause.args
2418
- },
2419
- orderBy,
2420
- offset: page * perPage,
2421
- limit: perPage
2927
+ };
2928
+ }
2929
+ const limitValue = perPageInput === false ? total : perPage;
2930
+ const dataResult = await this.#client.execute({
2931
+ sql: `SELECT ${buildSelectColumns(storage.TABLE_THREADS)} ${baseQuery} ORDER BY "${field}" ${direction} LIMIT ? OFFSET ?`,
2932
+ args: [...queryParams, limitValue, offset]
2422
2933
  });
2934
+ const threads = (dataResult.rows || []).map(mapRowToStorageThreadType);
2423
2935
  return {
2424
- pagination: {
2425
- total: count,
2426
- page,
2427
- perPage,
2428
- hasMore: spans.length === perPage
2429
- },
2430
- spans: spans.map((span) => transformFromSqlRow({ tableName: storage.TABLE_SPANS, sqlRow: span }))
2936
+ threads,
2937
+ total,
2938
+ page,
2939
+ perPage: perPageForResponse,
2940
+ hasMore: perPageInput === false ? false : offset + perPage < total
2431
2941
  };
2432
2942
  } catch (error$1) {
2433
- throw new error.MastraError(
2943
+ if (error$1 instanceof error.MastraError && error$1.category === error.ErrorCategory.USER) {
2944
+ throw error$1;
2945
+ }
2946
+ const mastraError = new error.MastraError(
2434
2947
  {
2435
- id: storage.createStorageErrorId("LIBSQL", "GET_TRACES_PAGINATED", "FAILED"),
2948
+ id: storage.createStorageErrorId("LIBSQL", "LIST_THREADS", "FAILED"),
2436
2949
  domain: error.ErrorDomain.STORAGE,
2437
- category: error.ErrorCategory.USER
2950
+ category: error.ErrorCategory.THIRD_PARTY,
2951
+ details: {
2952
+ ...filter?.resourceId && { resourceId: filter.resourceId },
2953
+ hasMetadataFilter: !!filter?.metadata
2954
+ }
2438
2955
  },
2439
2956
  error$1
2440
2957
  );
2958
+ this.logger?.trackException?.(mastraError);
2959
+ this.logger?.error?.(mastraError.toString());
2960
+ return {
2961
+ threads: [],
2962
+ total: 0,
2963
+ page,
2964
+ perPage: perPageForResponse,
2965
+ hasMore: false
2966
+ };
2441
2967
  }
2442
2968
  }
2443
- async batchCreateSpans(args) {
2969
+ async saveThread({ thread }) {
2444
2970
  try {
2445
- const now = (/* @__PURE__ */ new Date()).toISOString();
2446
- return this.operations.batchInsert({
2447
- tableName: storage.TABLE_SPANS,
2448
- records: args.records.map((record) => ({
2449
- ...record,
2450
- createdAt: now,
2451
- updatedAt: now
2452
- }))
2971
+ await this.#db.insert({
2972
+ tableName: storage.TABLE_THREADS,
2973
+ record: {
2974
+ ...thread
2975
+ // metadata is handled by prepareStatement which stringifies jsonb columns
2976
+ }
2453
2977
  });
2978
+ return thread;
2454
2979
  } catch (error$1) {
2455
- throw new error.MastraError(
2980
+ const mastraError = new error.MastraError(
2456
2981
  {
2457
- id: storage.createStorageErrorId("LIBSQL", "BATCH_CREATE_SPANS", "FAILED"),
2982
+ id: storage.createStorageErrorId("LIBSQL", "SAVE_THREAD", "FAILED"),
2458
2983
  domain: error.ErrorDomain.STORAGE,
2459
- category: error.ErrorCategory.USER
2984
+ category: error.ErrorCategory.THIRD_PARTY,
2985
+ details: { threadId: thread.id }
2460
2986
  },
2461
2987
  error$1
2462
2988
  );
2989
+ this.logger?.trackException?.(mastraError);
2990
+ this.logger?.error?.(mastraError.toString());
2991
+ throw mastraError;
2463
2992
  }
2464
2993
  }
2465
- async batchUpdateSpans(args) {
2994
+ async updateThread({
2995
+ id,
2996
+ title,
2997
+ metadata
2998
+ }) {
2999
+ const thread = await this.getThreadById({ threadId: id });
3000
+ if (!thread) {
3001
+ throw new error.MastraError({
3002
+ id: storage.createStorageErrorId("LIBSQL", "UPDATE_THREAD", "NOT_FOUND"),
3003
+ domain: error.ErrorDomain.STORAGE,
3004
+ category: error.ErrorCategory.USER,
3005
+ text: `Thread ${id} not found`,
3006
+ details: {
3007
+ status: 404,
3008
+ threadId: id
3009
+ }
3010
+ });
3011
+ }
3012
+ const updatedThread = {
3013
+ ...thread,
3014
+ title,
3015
+ metadata: {
3016
+ ...thread.metadata,
3017
+ ...metadata
3018
+ }
3019
+ };
2466
3020
  try {
2467
- return this.operations.batchUpdate({
2468
- tableName: storage.TABLE_SPANS,
2469
- updates: args.records.map((record) => ({
2470
- keys: { spanId: record.spanId, traceId: record.traceId },
2471
- data: { ...record.updates, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }
2472
- }))
3021
+ await this.#client.execute({
3022
+ sql: `UPDATE ${storage.TABLE_THREADS} SET title = ?, metadata = jsonb(?) WHERE id = ?`,
3023
+ args: [title, JSON.stringify(updatedThread.metadata), id]
2473
3024
  });
3025
+ return updatedThread;
2474
3026
  } catch (error$1) {
2475
3027
  throw new error.MastraError(
2476
3028
  {
2477
- id: storage.createStorageErrorId("LIBSQL", "BATCH_UPDATE_SPANS", "FAILED"),
3029
+ id: storage.createStorageErrorId("LIBSQL", "UPDATE_THREAD", "FAILED"),
2478
3030
  domain: error.ErrorDomain.STORAGE,
2479
- category: error.ErrorCategory.USER
3031
+ category: error.ErrorCategory.THIRD_PARTY,
3032
+ text: `Failed to update thread ${id}`,
3033
+ details: { threadId: id }
2480
3034
  },
2481
3035
  error$1
2482
3036
  );
2483
3037
  }
2484
3038
  }
2485
- async batchDeleteTraces(args) {
3039
+ async deleteThread({ threadId }) {
2486
3040
  try {
2487
- const keys = args.traceIds.map((traceId) => ({ traceId }));
2488
- return this.operations.batchDelete({
2489
- tableName: storage.TABLE_SPANS,
2490
- keys
3041
+ await this.#client.execute({
3042
+ sql: `DELETE FROM ${storage.TABLE_MESSAGES} WHERE thread_id = ?`,
3043
+ args: [threadId]
3044
+ });
3045
+ await this.#client.execute({
3046
+ sql: `DELETE FROM ${storage.TABLE_THREADS} WHERE id = ?`,
3047
+ args: [threadId]
2491
3048
  });
2492
3049
  } catch (error$1) {
2493
3050
  throw new error.MastraError(
2494
3051
  {
2495
- id: storage.createStorageErrorId("LIBSQL", "BATCH_DELETE_TRACES", "FAILED"),
3052
+ id: storage.createStorageErrorId("LIBSQL", "DELETE_THREAD", "FAILED"),
2496
3053
  domain: error.ErrorDomain.STORAGE,
2497
- category: error.ErrorCategory.USER
3054
+ category: error.ErrorCategory.THIRD_PARTY,
3055
+ details: { threadId }
2498
3056
  },
2499
3057
  error$1
2500
3058
  );
2501
3059
  }
2502
3060
  }
2503
- };
2504
- var StoreOperationsLibSQL = class extends storage.StoreOperations {
2505
- client;
2506
- /**
2507
- * Maximum number of retries for write operations if an SQLITE_BUSY error occurs.
2508
- * @default 5
2509
- */
2510
- maxRetries;
2511
- /**
2512
- * Initial backoff time in milliseconds for retrying write operations on SQLITE_BUSY.
2513
- * The backoff time will double with each retry (exponential backoff).
2514
- * @default 100
2515
- */
2516
- initialBackoffMs;
2517
- constructor({
2518
- client,
2519
- maxRetries,
2520
- initialBackoffMs
2521
- }) {
2522
- super();
2523
- this.client = client;
2524
- this.maxRetries = maxRetries ?? 5;
2525
- this.initialBackoffMs = initialBackoffMs ?? 100;
2526
- }
2527
- async hasColumn(table, column) {
2528
- const result = await this.client.execute({
2529
- sql: `PRAGMA table_info(${table})`
2530
- });
2531
- return (await result.rows)?.some((row) => row.name === column);
2532
- }
2533
- getCreateTableSQL(tableName, schema) {
2534
- const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
2535
- const columns = Object.entries(schema).map(([name, col]) => {
2536
- const parsedColumnName = utils.parseSqlIdentifier(name, "column name");
2537
- const type = this.getSqlType(col.type);
2538
- const nullable = col.nullable ? "" : "NOT NULL";
2539
- const primaryKey = col.primaryKey ? "PRIMARY KEY" : "";
2540
- return `${parsedColumnName} ${type} ${nullable} ${primaryKey}`.trim();
2541
- });
2542
- if (tableName === storage.TABLE_WORKFLOW_SNAPSHOT) {
2543
- const stmnt = `CREATE TABLE IF NOT EXISTS ${parsedTableName} (
2544
- ${columns.join(",\n")},
2545
- PRIMARY KEY (workflow_name, run_id)
2546
- )`;
2547
- return stmnt;
2548
- }
2549
- if (tableName === storage.TABLE_SPANS) {
2550
- const stmnt = `CREATE TABLE IF NOT EXISTS ${parsedTableName} (
2551
- ${columns.join(",\n")},
2552
- PRIMARY KEY (traceId, spanId)
2553
- )`;
2554
- return stmnt;
2555
- }
2556
- return `CREATE TABLE IF NOT EXISTS ${parsedTableName} (${columns.join(", ")})`;
2557
- }
2558
- async createTable({
2559
- tableName,
2560
- schema
2561
- }) {
3061
+ async cloneThread(args) {
3062
+ const { sourceThreadId, newThreadId: providedThreadId, resourceId, title, metadata, options } = args;
3063
+ const sourceThread = await this.getThreadById({ threadId: sourceThreadId });
3064
+ if (!sourceThread) {
3065
+ throw new error.MastraError({
3066
+ id: storage.createStorageErrorId("LIBSQL", "CLONE_THREAD", "SOURCE_NOT_FOUND"),
3067
+ domain: error.ErrorDomain.STORAGE,
3068
+ category: error.ErrorCategory.USER,
3069
+ text: `Source thread with id ${sourceThreadId} not found`,
3070
+ details: { sourceThreadId }
3071
+ });
3072
+ }
3073
+ const newThreadId = providedThreadId || crypto.randomUUID();
3074
+ const existingThread = await this.getThreadById({ threadId: newThreadId });
3075
+ if (existingThread) {
3076
+ throw new error.MastraError({
3077
+ id: storage.createStorageErrorId("LIBSQL", "CLONE_THREAD", "THREAD_EXISTS"),
3078
+ domain: error.ErrorDomain.STORAGE,
3079
+ category: error.ErrorCategory.USER,
3080
+ text: `Thread with id ${newThreadId} already exists`,
3081
+ details: { newThreadId }
3082
+ });
3083
+ }
2562
3084
  try {
2563
- this.logger.debug(`Creating database table`, { tableName, operation: "schema init" });
2564
- const sql = this.getCreateTableSQL(tableName, schema);
2565
- await this.client.execute(sql);
3085
+ let messageQuery = `SELECT id, content, role, type, "createdAt", thread_id, "resourceId"
3086
+ FROM "${storage.TABLE_MESSAGES}" WHERE thread_id = ?`;
3087
+ const messageParams = [sourceThreadId];
3088
+ if (options?.messageFilter?.startDate) {
3089
+ messageQuery += ` AND "createdAt" >= ?`;
3090
+ messageParams.push(
3091
+ options.messageFilter.startDate instanceof Date ? options.messageFilter.startDate.toISOString() : options.messageFilter.startDate
3092
+ );
3093
+ }
3094
+ if (options?.messageFilter?.endDate) {
3095
+ messageQuery += ` AND "createdAt" <= ?`;
3096
+ messageParams.push(
3097
+ options.messageFilter.endDate instanceof Date ? options.messageFilter.endDate.toISOString() : options.messageFilter.endDate
3098
+ );
3099
+ }
3100
+ if (options?.messageFilter?.messageIds && options.messageFilter.messageIds.length > 0) {
3101
+ messageQuery += ` AND id IN (${options.messageFilter.messageIds.map(() => "?").join(", ")})`;
3102
+ messageParams.push(...options.messageFilter.messageIds);
3103
+ }
3104
+ messageQuery += ` ORDER BY "createdAt" ASC`;
3105
+ if (options?.messageLimit && options.messageLimit > 0) {
3106
+ const limitQuery = `SELECT * FROM (${messageQuery.replace('ORDER BY "createdAt" ASC', 'ORDER BY "createdAt" DESC')} LIMIT ?) ORDER BY "createdAt" ASC`;
3107
+ messageParams.push(options.messageLimit);
3108
+ messageQuery = limitQuery;
3109
+ }
3110
+ const sourceMessagesResult = await this.#client.execute({ sql: messageQuery, args: messageParams });
3111
+ const sourceMessages = sourceMessagesResult.rows || [];
3112
+ const now = /* @__PURE__ */ new Date();
3113
+ const nowStr = now.toISOString();
3114
+ const lastMessageId = sourceMessages.length > 0 ? sourceMessages[sourceMessages.length - 1].id : void 0;
3115
+ const cloneMetadata = {
3116
+ sourceThreadId,
3117
+ clonedAt: now,
3118
+ ...lastMessageId && { lastMessageId }
3119
+ };
3120
+ const newThread = {
3121
+ id: newThreadId,
3122
+ resourceId: resourceId || sourceThread.resourceId,
3123
+ title: title || (sourceThread.title ? `Clone of ${sourceThread.title}` : void 0),
3124
+ metadata: {
3125
+ ...metadata,
3126
+ clone: cloneMetadata
3127
+ },
3128
+ createdAt: now,
3129
+ updatedAt: now
3130
+ };
3131
+ const tx = await this.#client.transaction("write");
3132
+ try {
3133
+ await tx.execute({
3134
+ sql: `INSERT INTO "${storage.TABLE_THREADS}" (id, "resourceId", title, metadata, "createdAt", "updatedAt")
3135
+ VALUES (?, ?, ?, jsonb(?), ?, ?)`,
3136
+ args: [
3137
+ newThread.id,
3138
+ newThread.resourceId,
3139
+ newThread.title || null,
3140
+ JSON.stringify(newThread.metadata),
3141
+ nowStr,
3142
+ nowStr
3143
+ ]
3144
+ });
3145
+ const clonedMessages = [];
3146
+ const targetResourceId = resourceId || sourceThread.resourceId;
3147
+ for (const sourceMsg of sourceMessages) {
3148
+ const newMessageId = crypto.randomUUID();
3149
+ const contentStr = sourceMsg.content;
3150
+ let parsedContent;
3151
+ try {
3152
+ parsedContent = JSON.parse(contentStr);
3153
+ } catch {
3154
+ parsedContent = { format: 2, parts: [{ type: "text", text: contentStr }] };
3155
+ }
3156
+ await tx.execute({
3157
+ sql: `INSERT INTO "${storage.TABLE_MESSAGES}" (id, thread_id, content, role, type, "createdAt", "resourceId")
3158
+ VALUES (?, ?, ?, ?, ?, ?, ?)`,
3159
+ args: [
3160
+ newMessageId,
3161
+ newThreadId,
3162
+ contentStr,
3163
+ sourceMsg.role,
3164
+ sourceMsg.type || "v2",
3165
+ sourceMsg.createdAt,
3166
+ targetResourceId
3167
+ ]
3168
+ });
3169
+ clonedMessages.push({
3170
+ id: newMessageId,
3171
+ threadId: newThreadId,
3172
+ content: parsedContent,
3173
+ role: sourceMsg.role,
3174
+ type: sourceMsg.type || void 0,
3175
+ createdAt: new Date(sourceMsg.createdAt),
3176
+ resourceId: targetResourceId
3177
+ });
3178
+ }
3179
+ await tx.commit();
3180
+ return {
3181
+ thread: newThread,
3182
+ clonedMessages
3183
+ };
3184
+ } catch (error) {
3185
+ await tx.rollback();
3186
+ throw error;
3187
+ }
2566
3188
  } catch (error$1) {
3189
+ if (error$1 instanceof error.MastraError) {
3190
+ throw error$1;
3191
+ }
2567
3192
  throw new error.MastraError(
2568
3193
  {
2569
- id: storage.createStorageErrorId("LIBSQL", "CREATE_TABLE", "FAILED"),
3194
+ id: storage.createStorageErrorId("LIBSQL", "CLONE_THREAD", "FAILED"),
2570
3195
  domain: error.ErrorDomain.STORAGE,
2571
3196
  category: error.ErrorCategory.THIRD_PARTY,
2572
- details: {
2573
- tableName
2574
- }
3197
+ details: { sourceThreadId, newThreadId }
2575
3198
  },
2576
3199
  error$1
2577
3200
  );
2578
3201
  }
2579
3202
  }
2580
- getSqlType(type) {
2581
- switch (type) {
2582
- case "bigint":
2583
- return "INTEGER";
2584
- // SQLite uses INTEGER for all integer sizes
2585
- case "timestamp":
2586
- return "TEXT";
2587
- // Store timestamps as ISO strings in SQLite
2588
- // jsonb falls through to base class which returns 'JSONB'
2589
- // SQLite's flexible type system treats JSONB as TEXT affinity
2590
- default:
2591
- return super.getSqlType(type);
2592
- }
2593
- }
2594
- async doInsert({
2595
- tableName,
2596
- record
2597
- }) {
2598
- await this.client.execute(
2599
- prepareStatement({
2600
- tableName,
2601
- record
2602
- })
2603
- );
2604
- }
2605
- insert(args) {
2606
- const executeWriteOperationWithRetry = createExecuteWriteOperationWithRetry({
2607
- logger: this.logger,
2608
- maxRetries: this.maxRetries,
2609
- initialBackoffMs: this.initialBackoffMs
2610
- });
2611
- return executeWriteOperationWithRetry(() => this.doInsert(args), `insert into table ${args.tableName}`);
2612
- }
2613
- async load({ tableName, keys }) {
2614
- const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
2615
- const parsedKeys = Object.keys(keys).map((key) => utils.parseSqlIdentifier(key, "column name"));
2616
- const conditions = parsedKeys.map((key) => `${key} = ?`).join(" AND ");
2617
- const values = Object.values(keys);
2618
- const result = await this.client.execute({
2619
- sql: `SELECT * FROM ${parsedTableName} WHERE ${conditions} ORDER BY createdAt DESC LIMIT 1`,
2620
- args: values
2621
- });
2622
- if (!result.rows || result.rows.length === 0) {
2623
- return null;
2624
- }
2625
- const row = result.rows[0];
2626
- const parsed = Object.fromEntries(
2627
- Object.entries(row || {}).map(([k, v]) => {
2628
- try {
2629
- return [k, typeof v === "string" ? v.startsWith("{") || v.startsWith("[") ? JSON.parse(v) : v : v];
2630
- } catch {
2631
- return [k, v];
2632
- }
2633
- })
2634
- );
2635
- return parsed;
3203
+ };
3204
+ var ObservabilityLibSQL = class extends storage.ObservabilityStorage {
3205
+ #db;
3206
+ constructor(config) {
3207
+ super();
3208
+ const client = resolveClient(config);
3209
+ this.#db = new LibSQLDB({ client, maxRetries: config.maxRetries, initialBackoffMs: config.initialBackoffMs });
2636
3210
  }
2637
- async loadMany({
2638
- tableName,
2639
- whereClause,
2640
- orderBy,
2641
- offset,
2642
- limit,
2643
- args
2644
- }) {
2645
- const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
2646
- let statement = `SELECT * FROM ${parsedTableName}`;
2647
- if (whereClause?.sql) {
2648
- statement += `${whereClause.sql}`;
2649
- }
2650
- if (orderBy) {
2651
- statement += ` ORDER BY ${orderBy}`;
2652
- }
2653
- if (limit) {
2654
- statement += ` LIMIT ${limit}`;
2655
- }
2656
- if (offset) {
2657
- statement += ` OFFSET ${offset}`;
2658
- }
2659
- const result = await this.client.execute({
2660
- sql: statement,
2661
- args: [...whereClause?.args ?? [], ...args ?? []]
2662
- });
2663
- return result.rows;
3211
+ async init() {
3212
+ await this.#db.createTable({ tableName: storage.TABLE_SPANS, schema: storage.SPAN_SCHEMA });
2664
3213
  }
2665
- async loadTotalCount({
2666
- tableName,
2667
- whereClause
2668
- }) {
2669
- const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
2670
- const statement = `SELECT COUNT(*) as count FROM ${parsedTableName} ${whereClause ? `${whereClause.sql}` : ""}`;
2671
- const result = await this.client.execute({
2672
- sql: statement,
2673
- args: whereClause?.args ?? []
2674
- });
2675
- if (!result.rows || result.rows.length === 0) {
2676
- return 0;
2677
- }
2678
- return result.rows[0]?.count ?? 0;
3214
+ async dangerouslyClearAll() {
3215
+ await this.#db.deleteData({ tableName: storage.TABLE_SPANS });
2679
3216
  }
2680
- update(args) {
2681
- const executeWriteOperationWithRetry = createExecuteWriteOperationWithRetry({
2682
- logger: this.logger,
2683
- maxRetries: this.maxRetries,
2684
- initialBackoffMs: this.initialBackoffMs
2685
- });
2686
- return executeWriteOperationWithRetry(() => this.executeUpdate(args), `update table ${args.tableName}`);
3217
+ /**
3218
+ * Manually run the spans migration to deduplicate and add the unique constraint.
3219
+ * This is intended to be called from the CLI when duplicates are detected.
3220
+ *
3221
+ * @returns Migration result with status and details
3222
+ */
3223
+ async migrateSpans() {
3224
+ return this.#db.migrateSpans();
2687
3225
  }
2688
- async executeUpdate({
2689
- tableName,
2690
- keys,
2691
- data
2692
- }) {
2693
- await this.client.execute(prepareUpdateStatement({ tableName, updates: data, keys }));
3226
+ /**
3227
+ * Check migration status for the spans table.
3228
+ * Returns information about whether migration is needed.
3229
+ */
3230
+ async checkSpansMigrationStatus() {
3231
+ return this.#db.checkSpansMigrationStatus();
2694
3232
  }
2695
- async doBatchInsert({
2696
- tableName,
2697
- records
2698
- }) {
2699
- if (records.length === 0) return;
2700
- const batchStatements = records.map((r) => prepareStatement({ tableName, record: r }));
2701
- await this.client.batch(batchStatements, "write");
3233
+ get tracingStrategy() {
3234
+ return {
3235
+ preferred: "batch-with-updates",
3236
+ supported: ["batch-with-updates", "insert-only"]
3237
+ };
2702
3238
  }
2703
- batchInsert(args) {
2704
- const executeWriteOperationWithRetry = createExecuteWriteOperationWithRetry({
2705
- logger: this.logger,
2706
- maxRetries: this.maxRetries,
2707
- initialBackoffMs: this.initialBackoffMs
2708
- });
2709
- return executeWriteOperationWithRetry(
2710
- () => this.doBatchInsert(args),
2711
- `batch insert into table ${args.tableName}`
2712
- ).catch((error$1) => {
3239
+ async createSpan(args) {
3240
+ const { span } = args;
3241
+ try {
3242
+ const startedAt = span.startedAt instanceof Date ? span.startedAt.toISOString() : span.startedAt;
3243
+ const endedAt = span.endedAt instanceof Date ? span.endedAt.toISOString() : span.endedAt;
3244
+ const now = (/* @__PURE__ */ new Date()).toISOString();
3245
+ const record = {
3246
+ ...span,
3247
+ startedAt,
3248
+ endedAt,
3249
+ createdAt: now,
3250
+ updatedAt: now
3251
+ };
3252
+ return this.#db.insert({ tableName: storage.TABLE_SPANS, record });
3253
+ } catch (error$1) {
2713
3254
  throw new error.MastraError(
2714
3255
  {
2715
- id: storage.createStorageErrorId("LIBSQL", "BATCH_INSERT", "FAILED"),
3256
+ id: storage.createStorageErrorId("LIBSQL", "CREATE_SPAN", "FAILED"),
2716
3257
  domain: error.ErrorDomain.STORAGE,
2717
- category: error.ErrorCategory.THIRD_PARTY,
3258
+ category: error.ErrorCategory.USER,
2718
3259
  details: {
2719
- tableName: args.tableName
3260
+ spanId: span.spanId,
3261
+ traceId: span.traceId,
3262
+ spanType: span.spanType,
3263
+ name: span.name
2720
3264
  }
2721
3265
  },
2722
3266
  error$1
2723
3267
  );
2724
- });
3268
+ }
2725
3269
  }
2726
- /**
2727
- * Public batch update method with retry logic
2728
- */
2729
- batchUpdate(args) {
2730
- const executeWriteOperationWithRetry = createExecuteWriteOperationWithRetry({
2731
- logger: this.logger,
2732
- maxRetries: this.maxRetries,
2733
- initialBackoffMs: this.initialBackoffMs
2734
- });
2735
- return executeWriteOperationWithRetry(
2736
- () => this.executeBatchUpdate(args),
2737
- `batch update in table ${args.tableName}`
2738
- ).catch((error$1) => {
3270
+ async getSpan(args) {
3271
+ const { traceId, spanId } = args;
3272
+ try {
3273
+ const rows = await this.#db.selectMany({
3274
+ tableName: storage.TABLE_SPANS,
3275
+ whereClause: { sql: " WHERE traceId = ? AND spanId = ?", args: [traceId, spanId] },
3276
+ limit: 1
3277
+ });
3278
+ if (!rows || rows.length === 0) {
3279
+ return null;
3280
+ }
3281
+ return {
3282
+ span: transformFromSqlRow({ tableName: storage.TABLE_SPANS, sqlRow: rows[0] })
3283
+ };
3284
+ } catch (error$1) {
2739
3285
  throw new error.MastraError(
2740
3286
  {
2741
- id: storage.createStorageErrorId("LIBSQL", "BATCH_UPDATE", "FAILED"),
3287
+ id: storage.createStorageErrorId("LIBSQL", "GET_SPAN", "FAILED"),
2742
3288
  domain: error.ErrorDomain.STORAGE,
2743
- category: error.ErrorCategory.THIRD_PARTY,
2744
- details: {
2745
- tableName: args.tableName
2746
- }
3289
+ category: error.ErrorCategory.USER,
3290
+ details: { traceId, spanId }
2747
3291
  },
2748
3292
  error$1
2749
3293
  );
2750
- });
3294
+ }
2751
3295
  }
2752
- /**
2753
- * Updates multiple records in batch. Each record can be updated based on single or composite keys.
2754
- */
2755
- async executeBatchUpdate({
2756
- tableName,
2757
- updates
2758
- }) {
2759
- if (updates.length === 0) return;
2760
- const batchStatements = updates.map(
2761
- ({ keys, data }) => prepareUpdateStatement({
2762
- tableName,
2763
- updates: data,
2764
- keys
2765
- })
2766
- );
2767
- await this.client.batch(batchStatements, "write");
3296
+ async getRootSpan(args) {
3297
+ const { traceId } = args;
3298
+ try {
3299
+ const rows = await this.#db.selectMany({
3300
+ tableName: storage.TABLE_SPANS,
3301
+ whereClause: { sql: " WHERE traceId = ? AND parentSpanId IS NULL", args: [traceId] },
3302
+ limit: 1
3303
+ });
3304
+ if (!rows || rows.length === 0) {
3305
+ return null;
3306
+ }
3307
+ return {
3308
+ span: transformFromSqlRow({ tableName: storage.TABLE_SPANS, sqlRow: rows[0] })
3309
+ };
3310
+ } catch (error$1) {
3311
+ throw new error.MastraError(
3312
+ {
3313
+ id: storage.createStorageErrorId("LIBSQL", "GET_ROOT_SPAN", "FAILED"),
3314
+ domain: error.ErrorDomain.STORAGE,
3315
+ category: error.ErrorCategory.USER,
3316
+ details: { traceId }
3317
+ },
3318
+ error$1
3319
+ );
3320
+ }
2768
3321
  }
2769
- /**
2770
- * Public batch delete method with retry logic
2771
- */
2772
- batchDelete({ tableName, keys }) {
2773
- const executeWriteOperationWithRetry = createExecuteWriteOperationWithRetry({
2774
- logger: this.logger,
2775
- maxRetries: this.maxRetries,
2776
- initialBackoffMs: this.initialBackoffMs
2777
- });
2778
- return executeWriteOperationWithRetry(
2779
- () => this.executeBatchDelete({ tableName, keys }),
2780
- `batch delete from table ${tableName}`
2781
- ).catch((error$1) => {
3322
+ async getTrace(args) {
3323
+ const { traceId } = args;
3324
+ try {
3325
+ const spans = await this.#db.selectMany({
3326
+ tableName: storage.TABLE_SPANS,
3327
+ whereClause: { sql: " WHERE traceId = ?", args: [traceId] },
3328
+ orderBy: "startedAt ASC"
3329
+ });
3330
+ if (!spans || spans.length === 0) {
3331
+ return null;
3332
+ }
3333
+ return {
3334
+ traceId,
3335
+ spans: spans.map((span) => transformFromSqlRow({ tableName: storage.TABLE_SPANS, sqlRow: span }))
3336
+ };
3337
+ } catch (error$1) {
2782
3338
  throw new error.MastraError(
2783
3339
  {
2784
- id: storage.createStorageErrorId("LIBSQL", "BATCH_DELETE", "FAILED"),
3340
+ id: storage.createStorageErrorId("LIBSQL", "GET_TRACE", "FAILED"),
2785
3341
  domain: error.ErrorDomain.STORAGE,
2786
- category: error.ErrorCategory.THIRD_PARTY,
3342
+ category: error.ErrorCategory.USER,
2787
3343
  details: {
2788
- tableName
3344
+ traceId
2789
3345
  }
2790
3346
  },
2791
3347
  error$1
2792
3348
  );
2793
- });
2794
- }
2795
- /**
2796
- * Deletes multiple records in batch. Each record can be deleted based on single or composite keys.
2797
- */
2798
- async executeBatchDelete({
2799
- tableName,
2800
- keys
2801
- }) {
2802
- if (keys.length === 0) return;
2803
- const batchStatements = keys.map(
2804
- (keyObj) => prepareDeleteStatement({
2805
- tableName,
2806
- keys: keyObj
2807
- })
2808
- );
2809
- await this.client.batch(batchStatements, "write");
3349
+ }
2810
3350
  }
2811
- /**
2812
- * Alters table schema to add columns if they don't exist
2813
- * @param tableName Name of the table
2814
- * @param schema Schema of the table
2815
- * @param ifNotExists Array of column names to add if they don't exist
2816
- */
2817
- async alterTable({
2818
- tableName,
2819
- schema,
2820
- ifNotExists
2821
- }) {
2822
- const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
3351
+ async updateSpan(args) {
3352
+ const { traceId, spanId, updates } = args;
2823
3353
  try {
2824
- const pragmaQuery = `PRAGMA table_info(${parsedTableName})`;
2825
- const result = await this.client.execute(pragmaQuery);
2826
- const existingColumnNames = new Set(result.rows.map((row) => row.name.toLowerCase()));
2827
- for (const columnName of ifNotExists) {
2828
- if (!existingColumnNames.has(columnName.toLowerCase()) && schema[columnName]) {
2829
- const columnDef = schema[columnName];
2830
- const sqlType = this.getSqlType(columnDef.type);
2831
- const nullable = columnDef.nullable === false ? "NOT NULL" : "";
2832
- const defaultValue = columnDef.nullable === false ? this.getDefaultValue(columnDef.type) : "";
2833
- const alterSql = `ALTER TABLE ${parsedTableName} ADD COLUMN "${columnName}" ${sqlType} ${nullable} ${defaultValue}`.trim();
2834
- await this.client.execute(alterSql);
2835
- this.logger?.debug?.(`Added column ${columnName} to table ${parsedTableName}`);
2836
- }
3354
+ const data = { ...updates };
3355
+ if (data.endedAt instanceof Date) {
3356
+ data.endedAt = data.endedAt.toISOString();
2837
3357
  }
3358
+ if (data.startedAt instanceof Date) {
3359
+ data.startedAt = data.startedAt.toISOString();
3360
+ }
3361
+ data.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
3362
+ await this.#db.update({
3363
+ tableName: storage.TABLE_SPANS,
3364
+ keys: { spanId, traceId },
3365
+ data
3366
+ });
2838
3367
  } catch (error$1) {
2839
3368
  throw new error.MastraError(
2840
3369
  {
2841
- id: storage.createStorageErrorId("LIBSQL", "ALTER_TABLE", "FAILED"),
3370
+ id: storage.createStorageErrorId("LIBSQL", "UPDATE_SPAN", "FAILED"),
2842
3371
  domain: error.ErrorDomain.STORAGE,
2843
- category: error.ErrorCategory.THIRD_PARTY,
3372
+ category: error.ErrorCategory.USER,
2844
3373
  details: {
2845
- tableName
3374
+ spanId,
3375
+ traceId
3376
+ }
3377
+ },
3378
+ error$1
3379
+ );
3380
+ }
3381
+ }
3382
+ async listTraces(args) {
3383
+ const { filters, pagination, orderBy } = storage.listTracesArgsSchema.parse(args);
3384
+ const { page, perPage } = pagination;
3385
+ const tableName = utils.parseSqlIdentifier(storage.TABLE_SPANS, "table name");
3386
+ try {
3387
+ const conditions = ["parentSpanId IS NULL"];
3388
+ const queryArgs = [];
3389
+ if (filters) {
3390
+ if (filters.startedAt?.start) {
3391
+ conditions.push(`startedAt >= ?`);
3392
+ queryArgs.push(filters.startedAt.start.toISOString());
3393
+ }
3394
+ if (filters.startedAt?.end) {
3395
+ conditions.push(`startedAt <= ?`);
3396
+ queryArgs.push(filters.startedAt.end.toISOString());
3397
+ }
3398
+ if (filters.endedAt?.start) {
3399
+ conditions.push(`endedAt >= ?`);
3400
+ queryArgs.push(filters.endedAt.start.toISOString());
3401
+ }
3402
+ if (filters.endedAt?.end) {
3403
+ conditions.push(`endedAt <= ?`);
3404
+ queryArgs.push(filters.endedAt.end.toISOString());
3405
+ }
3406
+ if (filters.spanType !== void 0) {
3407
+ conditions.push(`spanType = ?`);
3408
+ queryArgs.push(filters.spanType);
3409
+ }
3410
+ if (filters.entityType !== void 0) {
3411
+ conditions.push(`entityType = ?`);
3412
+ queryArgs.push(filters.entityType);
3413
+ }
3414
+ if (filters.entityId !== void 0) {
3415
+ conditions.push(`entityId = ?`);
3416
+ queryArgs.push(filters.entityId);
3417
+ }
3418
+ if (filters.entityName !== void 0) {
3419
+ conditions.push(`entityName = ?`);
3420
+ queryArgs.push(filters.entityName);
3421
+ }
3422
+ if (filters.userId !== void 0) {
3423
+ conditions.push(`userId = ?`);
3424
+ queryArgs.push(filters.userId);
3425
+ }
3426
+ if (filters.organizationId !== void 0) {
3427
+ conditions.push(`organizationId = ?`);
3428
+ queryArgs.push(filters.organizationId);
3429
+ }
3430
+ if (filters.resourceId !== void 0) {
3431
+ conditions.push(`resourceId = ?`);
3432
+ queryArgs.push(filters.resourceId);
3433
+ }
3434
+ if (filters.runId !== void 0) {
3435
+ conditions.push(`runId = ?`);
3436
+ queryArgs.push(filters.runId);
3437
+ }
3438
+ if (filters.sessionId !== void 0) {
3439
+ conditions.push(`sessionId = ?`);
3440
+ queryArgs.push(filters.sessionId);
3441
+ }
3442
+ if (filters.threadId !== void 0) {
3443
+ conditions.push(`threadId = ?`);
3444
+ queryArgs.push(filters.threadId);
3445
+ }
3446
+ if (filters.requestId !== void 0) {
3447
+ conditions.push(`requestId = ?`);
3448
+ queryArgs.push(filters.requestId);
3449
+ }
3450
+ if (filters.environment !== void 0) {
3451
+ conditions.push(`environment = ?`);
3452
+ queryArgs.push(filters.environment);
3453
+ }
3454
+ if (filters.source !== void 0) {
3455
+ conditions.push(`source = ?`);
3456
+ queryArgs.push(filters.source);
3457
+ }
3458
+ if (filters.serviceName !== void 0) {
3459
+ conditions.push(`serviceName = ?`);
3460
+ queryArgs.push(filters.serviceName);
3461
+ }
3462
+ if (filters.scope != null) {
3463
+ for (const [key, value] of Object.entries(filters.scope)) {
3464
+ if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(key)) {
3465
+ throw new error.MastraError({
3466
+ id: storage.createStorageErrorId("LIBSQL", "LIST_TRACES", "INVALID_FILTER_KEY"),
3467
+ domain: error.ErrorDomain.STORAGE,
3468
+ category: error.ErrorCategory.USER,
3469
+ details: { key }
3470
+ });
3471
+ }
3472
+ conditions.push(`json_extract(scope, '$.${key}') = ?`);
3473
+ queryArgs.push(typeof value === "string" ? value : JSON.stringify(value));
3474
+ }
3475
+ }
3476
+ if (filters.metadata != null) {
3477
+ for (const [key, value] of Object.entries(filters.metadata)) {
3478
+ if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(key)) {
3479
+ throw new error.MastraError({
3480
+ id: storage.createStorageErrorId("LIBSQL", "LIST_TRACES", "INVALID_FILTER_KEY"),
3481
+ domain: error.ErrorDomain.STORAGE,
3482
+ category: error.ErrorCategory.USER,
3483
+ details: { key }
3484
+ });
3485
+ }
3486
+ conditions.push(`json_extract(metadata, '$.${key}') = ?`);
3487
+ queryArgs.push(typeof value === "string" ? value : JSON.stringify(value));
3488
+ }
3489
+ }
3490
+ if (filters.tags != null && filters.tags.length > 0) {
3491
+ for (const tag of filters.tags) {
3492
+ conditions.push(`EXISTS (SELECT 1 FROM json_each(${tableName}.tags) WHERE value = ?)`);
3493
+ queryArgs.push(tag);
2846
3494
  }
3495
+ }
3496
+ if (filters.status !== void 0) {
3497
+ switch (filters.status) {
3498
+ case storage.TraceStatus.ERROR:
3499
+ conditions.push(`error IS NOT NULL`);
3500
+ break;
3501
+ case storage.TraceStatus.RUNNING:
3502
+ conditions.push(`endedAt IS NULL AND error IS NULL`);
3503
+ break;
3504
+ case storage.TraceStatus.SUCCESS:
3505
+ conditions.push(`endedAt IS NOT NULL AND error IS NULL`);
3506
+ break;
3507
+ }
3508
+ }
3509
+ if (filters.hasChildError !== void 0) {
3510
+ if (filters.hasChildError) {
3511
+ conditions.push(`EXISTS (
3512
+ SELECT 1 FROM ${tableName} c
3513
+ WHERE c.traceId = ${tableName}.traceId AND c.error IS NOT NULL
3514
+ )`);
3515
+ } else {
3516
+ conditions.push(`NOT EXISTS (
3517
+ SELECT 1 FROM ${tableName} c
3518
+ WHERE c.traceId = ${tableName}.traceId AND c.error IS NOT NULL
3519
+ )`);
3520
+ }
3521
+ }
3522
+ }
3523
+ const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
3524
+ const sortField = orderBy.field;
3525
+ const sortDirection = orderBy.direction;
3526
+ let orderByClause;
3527
+ if (sortField === "endedAt") {
3528
+ orderByClause = sortDirection === "DESC" ? `CASE WHEN ${sortField} IS NULL THEN 0 ELSE 1 END, ${sortField} DESC` : `CASE WHEN ${sortField} IS NULL THEN 1 ELSE 0 END, ${sortField} ASC`;
3529
+ } else {
3530
+ orderByClause = `${sortField} ${sortDirection}`;
3531
+ }
3532
+ const count = await this.#db.selectTotalCount({
3533
+ tableName: storage.TABLE_SPANS,
3534
+ whereClause: { sql: whereClause, args: queryArgs }
3535
+ });
3536
+ if (count === 0) {
3537
+ return {
3538
+ pagination: {
3539
+ total: 0,
3540
+ page,
3541
+ perPage,
3542
+ hasMore: false
3543
+ },
3544
+ spans: []
3545
+ };
3546
+ }
3547
+ const spans = await this.#db.selectMany({
3548
+ tableName: storage.TABLE_SPANS,
3549
+ whereClause: { sql: whereClause, args: queryArgs },
3550
+ orderBy: orderByClause,
3551
+ offset: page * perPage,
3552
+ limit: perPage
3553
+ });
3554
+ return {
3555
+ pagination: {
3556
+ total: count,
3557
+ page,
3558
+ perPage,
3559
+ hasMore: (page + 1) * perPage < count
3560
+ },
3561
+ spans: spans.map((span) => transformFromSqlRow({ tableName: storage.TABLE_SPANS, sqlRow: span }))
3562
+ };
3563
+ } catch (error$1) {
3564
+ throw new error.MastraError(
3565
+ {
3566
+ id: storage.createStorageErrorId("LIBSQL", "LIST_TRACES", "FAILED"),
3567
+ domain: error.ErrorDomain.STORAGE,
3568
+ category: error.ErrorCategory.USER
3569
+ },
3570
+ error$1
3571
+ );
3572
+ }
3573
+ }
3574
+ async batchCreateSpans(args) {
3575
+ try {
3576
+ const now = (/* @__PURE__ */ new Date()).toISOString();
3577
+ const records = args.records.map((record) => {
3578
+ const startedAt = record.startedAt instanceof Date ? record.startedAt.toISOString() : record.startedAt;
3579
+ const endedAt = record.endedAt instanceof Date ? record.endedAt.toISOString() : record.endedAt;
3580
+ return {
3581
+ ...record,
3582
+ startedAt,
3583
+ endedAt,
3584
+ createdAt: now,
3585
+ updatedAt: now
3586
+ };
3587
+ });
3588
+ return this.#db.batchInsert({
3589
+ tableName: storage.TABLE_SPANS,
3590
+ records
3591
+ });
3592
+ } catch (error$1) {
3593
+ throw new error.MastraError(
3594
+ {
3595
+ id: storage.createStorageErrorId("LIBSQL", "BATCH_CREATE_SPANS", "FAILED"),
3596
+ domain: error.ErrorDomain.STORAGE,
3597
+ category: error.ErrorCategory.USER
2847
3598
  },
2848
3599
  error$1
2849
3600
  );
2850
3601
  }
2851
3602
  }
2852
- async clearTable({ tableName }) {
2853
- const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
3603
+ async batchUpdateSpans(args) {
3604
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2854
3605
  try {
2855
- await this.client.execute(`DELETE FROM ${parsedTableName}`);
2856
- } catch (e) {
2857
- const mastraError = new error.MastraError(
3606
+ return this.#db.batchUpdate({
3607
+ tableName: storage.TABLE_SPANS,
3608
+ updates: args.records.map((record) => {
3609
+ const data = { ...record.updates };
3610
+ if (data.endedAt instanceof Date) {
3611
+ data.endedAt = data.endedAt.toISOString();
3612
+ }
3613
+ if (data.startedAt instanceof Date) {
3614
+ data.startedAt = data.startedAt.toISOString();
3615
+ }
3616
+ data.updatedAt = now;
3617
+ return {
3618
+ keys: { spanId: record.spanId, traceId: record.traceId },
3619
+ data
3620
+ };
3621
+ })
3622
+ });
3623
+ } catch (error$1) {
3624
+ throw new error.MastraError(
2858
3625
  {
2859
- id: storage.createStorageErrorId("LIBSQL", "CLEAR_TABLE", "FAILED"),
3626
+ id: storage.createStorageErrorId("LIBSQL", "BATCH_UPDATE_SPANS", "FAILED"),
2860
3627
  domain: error.ErrorDomain.STORAGE,
2861
- category: error.ErrorCategory.THIRD_PARTY,
2862
- details: {
2863
- tableName
2864
- }
3628
+ category: error.ErrorCategory.USER
2865
3629
  },
2866
- e
3630
+ error$1
2867
3631
  );
2868
- this.logger?.trackException?.(mastraError);
2869
- this.logger?.error?.(mastraError.toString());
2870
3632
  }
2871
3633
  }
2872
- async dropTable({ tableName }) {
2873
- const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
3634
+ async batchDeleteTraces(args) {
2874
3635
  try {
2875
- await this.client.execute(`DROP TABLE IF EXISTS ${parsedTableName}`);
2876
- } catch (e) {
3636
+ const keys = args.traceIds.map((traceId) => ({ traceId }));
3637
+ return this.#db.batchDelete({
3638
+ tableName: storage.TABLE_SPANS,
3639
+ keys
3640
+ });
3641
+ } catch (error$1) {
2877
3642
  throw new error.MastraError(
2878
3643
  {
2879
- id: storage.createStorageErrorId("LIBSQL", "DROP_TABLE", "FAILED"),
3644
+ id: storage.createStorageErrorId("LIBSQL", "BATCH_DELETE_TRACES", "FAILED"),
2880
3645
  domain: error.ErrorDomain.STORAGE,
2881
- category: error.ErrorCategory.THIRD_PARTY,
2882
- details: {
2883
- tableName
2884
- }
3646
+ category: error.ErrorCategory.USER
2885
3647
  },
2886
- e
3648
+ error$1
2887
3649
  );
2888
3650
  }
2889
3651
  }
2890
3652
  };
2891
3653
  var ScoresLibSQL = class extends storage.ScoresStorage {
2892
- operations;
2893
- client;
2894
- constructor({ client, operations }) {
3654
+ #db;
3655
+ #client;
3656
+ constructor(config) {
2895
3657
  super();
2896
- this.operations = operations;
2897
- this.client = client;
3658
+ const client = resolveClient(config);
3659
+ this.#client = client;
3660
+ this.#db = new LibSQLDB({ client, maxRetries: config.maxRetries, initialBackoffMs: config.initialBackoffMs });
3661
+ }
3662
+ async init() {
3663
+ await this.#db.createTable({ tableName: storage.TABLE_SCORERS, schema: storage.SCORERS_SCHEMA });
3664
+ await this.#db.alterTable({
3665
+ tableName: storage.TABLE_SCORERS,
3666
+ schema: storage.SCORERS_SCHEMA,
3667
+ ifNotExists: ["spanId", "requestContext"]
3668
+ });
3669
+ }
3670
+ async dangerouslyClearAll() {
3671
+ await this.#db.deleteData({ tableName: storage.TABLE_SCORERS });
2898
3672
  }
2899
3673
  async listScoresByRunId({
2900
3674
  runId,
@@ -2902,7 +3676,7 @@ var ScoresLibSQL = class extends storage.ScoresStorage {
2902
3676
  }) {
2903
3677
  try {
2904
3678
  const { page, perPage: perPageInput } = pagination;
2905
- const countResult = await this.client.execute({
3679
+ const countResult = await this.#client.execute({
2906
3680
  sql: `SELECT COUNT(*) as count FROM ${storage.TABLE_SCORERS} WHERE runId = ?`,
2907
3681
  args: [runId]
2908
3682
  });
@@ -2922,8 +3696,8 @@ var ScoresLibSQL = class extends storage.ScoresStorage {
2922
3696
  const { offset: start, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
2923
3697
  const limitValue = perPageInput === false ? total : perPage;
2924
3698
  const end = perPageInput === false ? total : start + perPage;
2925
- const result = await this.client.execute({
2926
- sql: `SELECT * FROM ${storage.TABLE_SCORERS} WHERE runId = ? ORDER BY createdAt DESC LIMIT ? OFFSET ?`,
3699
+ const result = await this.#client.execute({
3700
+ sql: `SELECT ${buildSelectColumns(storage.TABLE_SCORERS)} FROM ${storage.TABLE_SCORERS} WHERE runId = ? ORDER BY createdAt DESC LIMIT ? OFFSET ?`,
2927
3701
  args: [runId, limitValue, start]
2928
3702
  });
2929
3703
  const scores = result.rows?.map((row) => this.transformScoreRow(row)) ?? [];
@@ -2975,7 +3749,7 @@ var ScoresLibSQL = class extends storage.ScoresStorage {
2975
3749
  queryParams.push(source);
2976
3750
  }
2977
3751
  const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
2978
- const countResult = await this.client.execute({
3752
+ const countResult = await this.#client.execute({
2979
3753
  sql: `SELECT COUNT(*) as count FROM ${storage.TABLE_SCORERS} ${whereClause}`,
2980
3754
  args: queryParams
2981
3755
  });
@@ -2995,8 +3769,8 @@ var ScoresLibSQL = class extends storage.ScoresStorage {
2995
3769
  const { offset: start, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
2996
3770
  const limitValue = perPageInput === false ? total : perPage;
2997
3771
  const end = perPageInput === false ? total : start + perPage;
2998
- const result = await this.client.execute({
2999
- sql: `SELECT * FROM ${storage.TABLE_SCORERS} ${whereClause} ORDER BY createdAt DESC LIMIT ? OFFSET ?`,
3772
+ const result = await this.#client.execute({
3773
+ sql: `SELECT ${buildSelectColumns(storage.TABLE_SCORERS)} FROM ${storage.TABLE_SCORERS} ${whereClause} ORDER BY createdAt DESC LIMIT ? OFFSET ?`,
3000
3774
  args: [...queryParams, limitValue, start]
3001
3775
  });
3002
3776
  const scores = result.rows?.map((row) => this.transformScoreRow(row)) ?? [];
@@ -3022,16 +3796,13 @@ var ScoresLibSQL = class extends storage.ScoresStorage {
3022
3796
  }
3023
3797
  /**
3024
3798
  * LibSQL-specific score row transformation.
3025
- * Maps additionalLLMContext column to additionalContext field.
3026
3799
  */
3027
3800
  transformScoreRow(row) {
3028
- return storage.transformScoreRow(row, {
3029
- fieldMappings: { additionalContext: "additionalLLMContext" }
3030
- });
3801
+ return storage.transformScoreRow(row);
3031
3802
  }
3032
3803
  async getScoreById({ id }) {
3033
- const result = await this.client.execute({
3034
- sql: `SELECT * FROM ${storage.TABLE_SCORERS} WHERE id = ?`,
3804
+ const result = await this.#client.execute({
3805
+ sql: `SELECT ${buildSelectColumns(storage.TABLE_SCORERS)} FROM ${storage.TABLE_SCORERS} WHERE id = ?`,
3035
3806
  args: [id]
3036
3807
  });
3037
3808
  return result.rows?.[0] ? this.transformScoreRow(result.rows[0]) : null;
@@ -3047,7 +3818,7 @@ var ScoresLibSQL = class extends storage.ScoresStorage {
3047
3818
  domain: error.ErrorDomain.STORAGE,
3048
3819
  category: error.ErrorCategory.USER,
3049
3820
  details: {
3050
- scorer: score.scorer?.id ?? "unknown",
3821
+ scorer: typeof score.scorer?.id === "string" ? score.scorer.id : String(score.scorer?.id ?? "unknown"),
3051
3822
  entityId: score.entityId ?? "unknown",
3052
3823
  entityType: score.entityType ?? "unknown",
3053
3824
  traceId: score.traceId ?? "",
@@ -3060,7 +3831,7 @@ var ScoresLibSQL = class extends storage.ScoresStorage {
3060
3831
  try {
3061
3832
  const id = crypto.randomUUID();
3062
3833
  const now = /* @__PURE__ */ new Date();
3063
- await this.operations.insert({
3834
+ await this.#db.insert({
3064
3835
  tableName: storage.TABLE_SCORERS,
3065
3836
  record: {
3066
3837
  ...parsedScore,
@@ -3088,7 +3859,7 @@ var ScoresLibSQL = class extends storage.ScoresStorage {
3088
3859
  }) {
3089
3860
  try {
3090
3861
  const { page, perPage: perPageInput } = pagination;
3091
- const countResult = await this.client.execute({
3862
+ const countResult = await this.#client.execute({
3092
3863
  sql: `SELECT COUNT(*) as count FROM ${storage.TABLE_SCORERS} WHERE entityId = ? AND entityType = ?`,
3093
3864
  args: [entityId, entityType]
3094
3865
  });
@@ -3108,8 +3879,8 @@ var ScoresLibSQL = class extends storage.ScoresStorage {
3108
3879
  const { offset: start, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
3109
3880
  const limitValue = perPageInput === false ? total : perPage;
3110
3881
  const end = perPageInput === false ? total : start + perPage;
3111
- const result = await this.client.execute({
3112
- sql: `SELECT * FROM ${storage.TABLE_SCORERS} WHERE entityId = ? AND entityType = ? ORDER BY createdAt DESC LIMIT ? OFFSET ?`,
3882
+ const result = await this.#client.execute({
3883
+ sql: `SELECT ${buildSelectColumns(storage.TABLE_SCORERS)} FROM ${storage.TABLE_SCORERS} WHERE entityId = ? AND entityType = ? ORDER BY createdAt DESC LIMIT ? OFFSET ?`,
3113
3884
  args: [entityId, entityType, limitValue, start]
3114
3885
  });
3115
3886
  const scores = result.rows?.map((row) => this.transformScoreRow(row)) ?? [];
@@ -3142,15 +3913,15 @@ var ScoresLibSQL = class extends storage.ScoresStorage {
3142
3913
  const { page, perPage: perPageInput } = pagination;
3143
3914
  const perPage = storage.normalizePerPage(perPageInput, 100);
3144
3915
  const { offset: start, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
3145
- const countSQLResult = await this.client.execute({
3916
+ const countSQLResult = await this.#client.execute({
3146
3917
  sql: `SELECT COUNT(*) as count FROM ${storage.TABLE_SCORERS} WHERE traceId = ? AND spanId = ?`,
3147
3918
  args: [traceId, spanId]
3148
3919
  });
3149
3920
  const total = Number(countSQLResult.rows?.[0]?.count ?? 0);
3150
3921
  const limitValue = perPageInput === false ? total : perPage;
3151
3922
  const end = perPageInput === false ? total : start + perPage;
3152
- const result = await this.client.execute({
3153
- sql: `SELECT * FROM ${storage.TABLE_SCORERS} WHERE traceId = ? AND spanId = ? ORDER BY createdAt DESC LIMIT ? OFFSET ?`,
3923
+ const result = await this.#client.execute({
3924
+ sql: `SELECT ${buildSelectColumns(storage.TABLE_SCORERS)} FROM ${storage.TABLE_SCORERS} WHERE traceId = ? AND spanId = ? ORDER BY createdAt DESC LIMIT ? OFFSET ?`,
3154
3925
  args: [traceId, spanId, limitValue, start]
3155
3926
  });
3156
3927
  const scores = result.rows?.map((row) => this.transformScoreRow(row)) ?? [];
@@ -3175,56 +3946,68 @@ var ScoresLibSQL = class extends storage.ScoresStorage {
3175
3946
  }
3176
3947
  }
3177
3948
  };
3178
- function parseWorkflowRun(row) {
3179
- let parsedSnapshot = row.snapshot;
3180
- if (typeof parsedSnapshot === "string") {
3181
- try {
3182
- parsedSnapshot = JSON.parse(row.snapshot);
3183
- } catch (e) {
3184
- console.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
3185
- }
3186
- }
3187
- return {
3188
- workflowName: row.workflow_name,
3189
- runId: row.run_id,
3190
- snapshot: parsedSnapshot,
3191
- resourceId: row.resourceId,
3192
- createdAt: new Date(row.createdAt),
3193
- updatedAt: new Date(row.updatedAt)
3194
- };
3195
- }
3196
3949
  var WorkflowsLibSQL = class extends storage.WorkflowsStorage {
3197
- operations;
3198
- client;
3199
- maxRetries;
3200
- initialBackoffMs;
3201
- constructor({
3202
- operations,
3203
- client,
3204
- maxRetries = 5,
3205
- initialBackoffMs = 500
3206
- }) {
3950
+ #db;
3951
+ #client;
3952
+ executeWithRetry;
3953
+ constructor(config) {
3207
3954
  super();
3208
- this.operations = operations;
3209
- this.client = client;
3210
- this.maxRetries = maxRetries;
3211
- this.initialBackoffMs = initialBackoffMs;
3955
+ const client = resolveClient(config);
3956
+ const maxRetries = config.maxRetries ?? 5;
3957
+ const initialBackoffMs = config.initialBackoffMs ?? 500;
3958
+ this.#client = client;
3959
+ this.#db = new LibSQLDB({ client, maxRetries, initialBackoffMs });
3960
+ this.executeWithRetry = createExecuteWriteOperationWithRetry({
3961
+ logger: this.logger,
3962
+ maxRetries,
3963
+ initialBackoffMs
3964
+ });
3212
3965
  this.setupPragmaSettings().catch(
3213
3966
  (err) => this.logger.warn("LibSQL Workflows: Failed to setup PRAGMA settings.", err)
3214
3967
  );
3215
3968
  }
3969
+ parseWorkflowRun(row) {
3970
+ let parsedSnapshot = row.snapshot;
3971
+ if (typeof parsedSnapshot === "string") {
3972
+ try {
3973
+ parsedSnapshot = JSON.parse(row.snapshot);
3974
+ } catch (e) {
3975
+ this.logger.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
3976
+ }
3977
+ }
3978
+ return {
3979
+ workflowName: row.workflow_name,
3980
+ runId: row.run_id,
3981
+ snapshot: parsedSnapshot,
3982
+ resourceId: row.resourceId,
3983
+ createdAt: new Date(row.createdAt),
3984
+ updatedAt: new Date(row.updatedAt)
3985
+ };
3986
+ }
3987
+ async init() {
3988
+ const schema = storage.TABLE_SCHEMAS[storage.TABLE_WORKFLOW_SNAPSHOT];
3989
+ await this.#db.createTable({ tableName: storage.TABLE_WORKFLOW_SNAPSHOT, schema });
3990
+ await this.#db.alterTable({
3991
+ tableName: storage.TABLE_WORKFLOW_SNAPSHOT,
3992
+ schema,
3993
+ ifNotExists: ["resourceId"]
3994
+ });
3995
+ }
3996
+ async dangerouslyClearAll() {
3997
+ await this.#db.deleteData({ tableName: storage.TABLE_WORKFLOW_SNAPSHOT });
3998
+ }
3216
3999
  async setupPragmaSettings() {
3217
4000
  try {
3218
- await this.client.execute("PRAGMA busy_timeout = 10000;");
4001
+ await this.#client.execute("PRAGMA busy_timeout = 10000;");
3219
4002
  this.logger.debug("LibSQL Workflows: PRAGMA busy_timeout=10000 set.");
3220
4003
  try {
3221
- await this.client.execute("PRAGMA journal_mode = WAL;");
4004
+ await this.#client.execute("PRAGMA journal_mode = WAL;");
3222
4005
  this.logger.debug("LibSQL Workflows: PRAGMA journal_mode=WAL set.");
3223
4006
  } catch {
3224
4007
  this.logger.debug("LibSQL Workflows: WAL mode not supported, using default journal mode.");
3225
4008
  }
3226
4009
  try {
3227
- await this.client.execute("PRAGMA synchronous = NORMAL;");
4010
+ await this.#client.execute("PRAGMA synchronous = NORMAL;");
3228
4011
  this.logger.debug("LibSQL Workflows: PRAGMA synchronous=NORMAL set.");
3229
4012
  } catch {
3230
4013
  this.logger.debug("LibSQL Workflows: Failed to set synchronous mode.");
@@ -3233,44 +4016,6 @@ var WorkflowsLibSQL = class extends storage.WorkflowsStorage {
3233
4016
  this.logger.warn("LibSQL Workflows: Failed to set PRAGMA settings.", err);
3234
4017
  }
3235
4018
  }
3236
- async executeWithRetry(operation) {
3237
- let attempts = 0;
3238
- let backoff = this.initialBackoffMs;
3239
- while (attempts < this.maxRetries) {
3240
- try {
3241
- return await operation();
3242
- } catch (error) {
3243
- this.logger.debug("LibSQL Workflows: Error caught in retry loop", {
3244
- errorType: error.constructor.name,
3245
- errorCode: error.code,
3246
- errorMessage: error.message,
3247
- attempts,
3248
- maxRetries: this.maxRetries
3249
- });
3250
- 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");
3251
- if (isLockError) {
3252
- attempts++;
3253
- if (attempts >= this.maxRetries) {
3254
- this.logger.error(
3255
- `LibSQL Workflows: Operation failed after ${this.maxRetries} attempts due to database lock: ${error.message}`,
3256
- { error, attempts, maxRetries: this.maxRetries }
3257
- );
3258
- throw error;
3259
- }
3260
- this.logger.warn(
3261
- `LibSQL Workflows: Attempt ${attempts} failed due to database lock. Retrying in ${backoff}ms...`,
3262
- { errorMessage: error.message, attempts, backoff, maxRetries: this.maxRetries }
3263
- );
3264
- await new Promise((resolve) => setTimeout(resolve, backoff));
3265
- backoff *= 2;
3266
- } else {
3267
- this.logger.error("LibSQL Workflows: Non-lock error occurred, not retrying", { error });
3268
- throw error;
3269
- }
3270
- }
3271
- }
3272
- throw new Error("LibSQL Workflows: Max retries reached, but no error was re-thrown from the loop.");
3273
- }
3274
4019
  async updateWorkflowResults({
3275
4020
  workflowName,
3276
4021
  runId,
@@ -3279,10 +4024,10 @@ var WorkflowsLibSQL = class extends storage.WorkflowsStorage {
3279
4024
  requestContext
3280
4025
  }) {
3281
4026
  return this.executeWithRetry(async () => {
3282
- const tx = await this.client.transaction("write");
4027
+ const tx = await this.#client.transaction("write");
3283
4028
  try {
3284
4029
  const existingSnapshotResult = await tx.execute({
3285
- sql: `SELECT snapshot FROM ${storage.TABLE_WORKFLOW_SNAPSHOT} WHERE workflow_name = ? AND run_id = ?`,
4030
+ sql: `SELECT json(snapshot) as snapshot FROM ${storage.TABLE_WORKFLOW_SNAPSHOT} WHERE workflow_name = ? AND run_id = ?`,
3286
4031
  args: [workflowName, runId]
3287
4032
  });
3288
4033
  let snapshot;
@@ -3307,9 +4052,13 @@ var WorkflowsLibSQL = class extends storage.WorkflowsStorage {
3307
4052
  }
3308
4053
  snapshot.context[stepId] = result;
3309
4054
  snapshot.requestContext = { ...snapshot.requestContext, ...requestContext };
4055
+ const now = (/* @__PURE__ */ new Date()).toISOString();
3310
4056
  await tx.execute({
3311
- sql: `UPDATE ${storage.TABLE_WORKFLOW_SNAPSHOT} SET snapshot = ? WHERE workflow_name = ? AND run_id = ?`,
3312
- args: [JSON.stringify(snapshot), workflowName, runId]
4057
+ sql: `INSERT INTO ${storage.TABLE_WORKFLOW_SNAPSHOT} (workflow_name, run_id, snapshot, createdAt, updatedAt)
4058
+ VALUES (?, ?, jsonb(?), ?, ?)
4059
+ ON CONFLICT(workflow_name, run_id)
4060
+ DO UPDATE SET snapshot = excluded.snapshot, updatedAt = excluded.updatedAt`,
4061
+ args: [workflowName, runId, JSON.stringify(snapshot), now, now]
3313
4062
  });
3314
4063
  await tx.commit();
3315
4064
  return snapshot.context;
@@ -3319,7 +4068,7 @@ var WorkflowsLibSQL = class extends storage.WorkflowsStorage {
3319
4068
  }
3320
4069
  throw error;
3321
4070
  }
3322
- });
4071
+ }, "updateWorkflowResults");
3323
4072
  }
3324
4073
  async updateWorkflowState({
3325
4074
  workflowName,
@@ -3327,10 +4076,10 @@ var WorkflowsLibSQL = class extends storage.WorkflowsStorage {
3327
4076
  opts
3328
4077
  }) {
3329
4078
  return this.executeWithRetry(async () => {
3330
- const tx = await this.client.transaction("write");
4079
+ const tx = await this.#client.transaction("write");
3331
4080
  try {
3332
4081
  const existingSnapshotResult = await tx.execute({
3333
- sql: `SELECT snapshot FROM ${storage.TABLE_WORKFLOW_SNAPSHOT} WHERE workflow_name = ? AND run_id = ?`,
4082
+ sql: `SELECT json(snapshot) as snapshot FROM ${storage.TABLE_WORKFLOW_SNAPSHOT} WHERE workflow_name = ? AND run_id = ?`,
3334
4083
  args: [workflowName, runId]
3335
4084
  });
3336
4085
  if (!existingSnapshotResult.rows?.[0]) {
@@ -3345,7 +4094,7 @@ var WorkflowsLibSQL = class extends storage.WorkflowsStorage {
3345
4094
  }
3346
4095
  const updatedSnapshot = { ...snapshot, ...opts };
3347
4096
  await tx.execute({
3348
- sql: `UPDATE ${storage.TABLE_WORKFLOW_SNAPSHOT} SET snapshot = ? WHERE workflow_name = ? AND run_id = ?`,
4097
+ sql: `UPDATE ${storage.TABLE_WORKFLOW_SNAPSHOT} SET snapshot = jsonb(?) WHERE workflow_name = ? AND run_id = ?`,
3349
4098
  args: [JSON.stringify(updatedSnapshot), workflowName, runId]
3350
4099
  });
3351
4100
  await tx.commit();
@@ -3356,24 +4105,27 @@ var WorkflowsLibSQL = class extends storage.WorkflowsStorage {
3356
4105
  }
3357
4106
  throw error;
3358
4107
  }
3359
- });
4108
+ }, "updateWorkflowState");
3360
4109
  }
3361
4110
  async persistWorkflowSnapshot({
3362
4111
  workflowName,
3363
4112
  runId,
3364
4113
  resourceId,
3365
- snapshot
4114
+ snapshot,
4115
+ createdAt,
4116
+ updatedAt
3366
4117
  }) {
4118
+ const now = /* @__PURE__ */ new Date();
3367
4119
  const data = {
3368
4120
  workflow_name: workflowName,
3369
4121
  run_id: runId,
3370
4122
  resourceId,
3371
4123
  snapshot,
3372
- createdAt: /* @__PURE__ */ new Date(),
3373
- updatedAt: /* @__PURE__ */ new Date()
4124
+ createdAt: createdAt ?? now,
4125
+ updatedAt: updatedAt ?? now
3374
4126
  };
3375
4127
  this.logger.debug("Persisting workflow snapshot", { workflowName, runId, data });
3376
- await this.operations.insert({
4128
+ await this.#db.insert({
3377
4129
  tableName: storage.TABLE_WORKFLOW_SNAPSHOT,
3378
4130
  record: data
3379
4131
  });
@@ -3383,7 +4135,7 @@ var WorkflowsLibSQL = class extends storage.WorkflowsStorage {
3383
4135
  runId
3384
4136
  }) {
3385
4137
  this.logger.debug("Loading workflow snapshot", { workflowName, runId });
3386
- const d = await this.operations.load({
4138
+ const d = await this.#db.select({
3387
4139
  tableName: storage.TABLE_WORKFLOW_SNAPSHOT,
3388
4140
  keys: { workflow_name: workflowName, run_id: runId }
3389
4141
  });
@@ -3405,14 +4157,14 @@ var WorkflowsLibSQL = class extends storage.WorkflowsStorage {
3405
4157
  }
3406
4158
  const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
3407
4159
  try {
3408
- const result = await this.client.execute({
3409
- sql: `SELECT * FROM ${storage.TABLE_WORKFLOW_SNAPSHOT} ${whereClause} ORDER BY createdAt DESC LIMIT 1`,
4160
+ const result = await this.#client.execute({
4161
+ sql: `SELECT workflow_name, run_id, resourceId, json(snapshot) as snapshot, createdAt, updatedAt FROM ${storage.TABLE_WORKFLOW_SNAPSHOT} ${whereClause} ORDER BY createdAt DESC LIMIT 1`,
3410
4162
  args
3411
4163
  });
3412
4164
  if (!result.rows?.[0]) {
3413
4165
  return null;
3414
4166
  }
3415
- return parseWorkflowRun(result.rows[0]);
4167
+ return this.parseWorkflowRun(result.rows[0]);
3416
4168
  } catch (error$1) {
3417
4169
  throw new error.MastraError(
3418
4170
  {
@@ -3425,22 +4177,24 @@ var WorkflowsLibSQL = class extends storage.WorkflowsStorage {
3425
4177
  }
3426
4178
  }
3427
4179
  async deleteWorkflowRunById({ runId, workflowName }) {
3428
- try {
3429
- await this.client.execute({
3430
- sql: `DELETE FROM ${storage.TABLE_WORKFLOW_SNAPSHOT} WHERE workflow_name = ? AND run_id = ?`,
3431
- args: [workflowName, runId]
3432
- });
3433
- } catch (error$1) {
3434
- throw new error.MastraError(
3435
- {
3436
- id: storage.createStorageErrorId("LIBSQL", "DELETE_WORKFLOW_RUN_BY_ID", "FAILED"),
3437
- domain: error.ErrorDomain.STORAGE,
3438
- category: error.ErrorCategory.THIRD_PARTY,
3439
- details: { runId, workflowName }
3440
- },
3441
- error$1
3442
- );
3443
- }
4180
+ return this.executeWithRetry(async () => {
4181
+ try {
4182
+ await this.#client.execute({
4183
+ sql: `DELETE FROM ${storage.TABLE_WORKFLOW_SNAPSHOT} WHERE workflow_name = ? AND run_id = ?`,
4184
+ args: [workflowName, runId]
4185
+ });
4186
+ } catch (error$1) {
4187
+ throw new error.MastraError(
4188
+ {
4189
+ id: storage.createStorageErrorId("LIBSQL", "DELETE_WORKFLOW_RUN_BY_ID", "FAILED"),
4190
+ domain: error.ErrorDomain.STORAGE,
4191
+ category: error.ErrorCategory.THIRD_PARTY,
4192
+ details: { runId, workflowName }
4193
+ },
4194
+ error$1
4195
+ );
4196
+ }
4197
+ }, "deleteWorkflowRunById");
3444
4198
  }
3445
4199
  async listWorkflowRuns({
3446
4200
  workflowName,
@@ -3471,19 +4225,19 @@ var WorkflowsLibSQL = class extends storage.WorkflowsStorage {
3471
4225
  args.push(toDate.toISOString());
3472
4226
  }
3473
4227
  if (resourceId) {
3474
- const hasResourceId = await this.operations.hasColumn(storage.TABLE_WORKFLOW_SNAPSHOT, "resourceId");
4228
+ const hasResourceId = await this.#db.hasColumn(storage.TABLE_WORKFLOW_SNAPSHOT, "resourceId");
3475
4229
  if (hasResourceId) {
3476
4230
  conditions.push("resourceId = ?");
3477
4231
  args.push(resourceId);
3478
4232
  } else {
3479
- console.warn(`[${storage.TABLE_WORKFLOW_SNAPSHOT}] resourceId column not found. Skipping resourceId filter.`);
4233
+ this.logger.warn(`[${storage.TABLE_WORKFLOW_SNAPSHOT}] resourceId column not found. Skipping resourceId filter.`);
3480
4234
  }
3481
4235
  }
3482
4236
  const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
3483
4237
  let total = 0;
3484
4238
  const usePagination = typeof perPage === "number" && typeof page === "number";
3485
4239
  if (usePagination) {
3486
- const countResult = await this.client.execute({
4240
+ const countResult = await this.#client.execute({
3487
4241
  sql: `SELECT COUNT(*) as count FROM ${storage.TABLE_WORKFLOW_SNAPSHOT} ${whereClause}`,
3488
4242
  args
3489
4243
  });
@@ -3491,11 +4245,11 @@ var WorkflowsLibSQL = class extends storage.WorkflowsStorage {
3491
4245
  }
3492
4246
  const normalizedPerPage = usePagination ? storage.normalizePerPage(perPage, Number.MAX_SAFE_INTEGER) : 0;
3493
4247
  const offset = usePagination ? page * normalizedPerPage : 0;
3494
- const result = await this.client.execute({
3495
- sql: `SELECT * FROM ${storage.TABLE_WORKFLOW_SNAPSHOT} ${whereClause} ORDER BY createdAt DESC${usePagination ? ` LIMIT ? OFFSET ?` : ""}`,
4248
+ const result = await this.#client.execute({
4249
+ sql: `SELECT workflow_name, run_id, resourceId, json(snapshot) as snapshot, createdAt, updatedAt FROM ${storage.TABLE_WORKFLOW_SNAPSHOT} ${whereClause} ORDER BY createdAt DESC${usePagination ? ` LIMIT ? OFFSET ?` : ""}`,
3496
4250
  args: usePagination ? [...args, normalizedPerPage, offset] : args
3497
4251
  });
3498
- const runs = (result.rows || []).map((row) => parseWorkflowRun(row));
4252
+ const runs = (result.rows || []).map((row) => this.parseWorkflowRun(row));
3499
4253
  return { runs, total: total || runs.length };
3500
4254
  } catch (error$1) {
3501
4255
  throw new error.MastraError(
@@ -3511,7 +4265,7 @@ var WorkflowsLibSQL = class extends storage.WorkflowsStorage {
3511
4265
  };
3512
4266
 
3513
4267
  // src/storage/index.ts
3514
- var LibSQLStore = class extends storage.MastraStorage {
4268
+ var LibSQLStore = class extends storage.MastraCompositeStore {
3515
4269
  client;
3516
4270
  maxRetries;
3517
4271
  initialBackoffMs;
@@ -3538,18 +4292,17 @@ var LibSQLStore = class extends storage.MastraStorage {
3538
4292
  } else {
3539
4293
  this.client = config.client;
3540
4294
  }
3541
- const operations = new StoreOperationsLibSQL({
4295
+ const domainConfig = {
3542
4296
  client: this.client,
3543
4297
  maxRetries: this.maxRetries,
3544
4298
  initialBackoffMs: this.initialBackoffMs
3545
- });
3546
- const scores = new ScoresLibSQL({ client: this.client, operations });
3547
- const workflows = new WorkflowsLibSQL({ client: this.client, operations });
3548
- const memory = new MemoryLibSQL({ client: this.client, operations });
3549
- const observability = new ObservabilityLibSQL({ operations });
3550
- const agents = new AgentsLibSQL({ client: this.client, operations });
4299
+ };
4300
+ const scores = new ScoresLibSQL(domainConfig);
4301
+ const workflows = new WorkflowsLibSQL(domainConfig);
4302
+ const memory = new MemoryLibSQL(domainConfig);
4303
+ const observability = new ObservabilityLibSQL(domainConfig);
4304
+ const agents = new AgentsLibSQL(domainConfig);
3551
4305
  this.stores = {
3552
- operations,
3553
4306
  scores,
3554
4307
  workflows,
3555
4308
  memory,
@@ -3557,194 +4310,6 @@ var LibSQLStore = class extends storage.MastraStorage {
3557
4310
  agents
3558
4311
  };
3559
4312
  }
3560
- get supports() {
3561
- return {
3562
- selectByIncludeResourceScope: true,
3563
- resourceWorkingMemory: true,
3564
- hasColumn: true,
3565
- createTable: true,
3566
- deleteMessages: true,
3567
- observabilityInstance: true,
3568
- listScoresBySpan: true,
3569
- agents: true
3570
- };
3571
- }
3572
- async createTable({
3573
- tableName,
3574
- schema
3575
- }) {
3576
- await this.stores.operations.createTable({ tableName, schema });
3577
- }
3578
- /**
3579
- * Alters table schema to add columns if they don't exist
3580
- * @param tableName Name of the table
3581
- * @param schema Schema of the table
3582
- * @param ifNotExists Array of column names to add if they don't exist
3583
- */
3584
- async alterTable({
3585
- tableName,
3586
- schema,
3587
- ifNotExists
3588
- }) {
3589
- await this.stores.operations.alterTable({ tableName, schema, ifNotExists });
3590
- }
3591
- async clearTable({ tableName }) {
3592
- await this.stores.operations.clearTable({ tableName });
3593
- }
3594
- async dropTable({ tableName }) {
3595
- await this.stores.operations.dropTable({ tableName });
3596
- }
3597
- insert(args) {
3598
- return this.stores.operations.insert(args);
3599
- }
3600
- batchInsert(args) {
3601
- return this.stores.operations.batchInsert(args);
3602
- }
3603
- async load({ tableName, keys }) {
3604
- return this.stores.operations.load({ tableName, keys });
3605
- }
3606
- async getThreadById({ threadId }) {
3607
- return this.stores.memory.getThreadById({ threadId });
3608
- }
3609
- async saveThread({ thread }) {
3610
- return this.stores.memory.saveThread({ thread });
3611
- }
3612
- async updateThread({
3613
- id,
3614
- title,
3615
- metadata
3616
- }) {
3617
- return this.stores.memory.updateThread({ id, title, metadata });
3618
- }
3619
- async deleteThread({ threadId }) {
3620
- return this.stores.memory.deleteThread({ threadId });
3621
- }
3622
- async listMessagesById({ messageIds }) {
3623
- return this.stores.memory.listMessagesById({ messageIds });
3624
- }
3625
- async saveMessages(args) {
3626
- const result = await this.stores.memory.saveMessages({ messages: args.messages });
3627
- return { messages: result.messages };
3628
- }
3629
- async updateMessages({
3630
- messages
3631
- }) {
3632
- return this.stores.memory.updateMessages({ messages });
3633
- }
3634
- async deleteMessages(messageIds) {
3635
- return this.stores.memory.deleteMessages(messageIds);
3636
- }
3637
- async getScoreById({ id }) {
3638
- return this.stores.scores.getScoreById({ id });
3639
- }
3640
- async saveScore(score) {
3641
- return this.stores.scores.saveScore(score);
3642
- }
3643
- async listScoresByScorerId({
3644
- scorerId,
3645
- entityId,
3646
- entityType,
3647
- source,
3648
- pagination
3649
- }) {
3650
- return this.stores.scores.listScoresByScorerId({ scorerId, entityId, entityType, source, pagination });
3651
- }
3652
- async listScoresByRunId({
3653
- runId,
3654
- pagination
3655
- }) {
3656
- return this.stores.scores.listScoresByRunId({ runId, pagination });
3657
- }
3658
- async listScoresByEntityId({
3659
- entityId,
3660
- entityType,
3661
- pagination
3662
- }) {
3663
- return this.stores.scores.listScoresByEntityId({ entityId, entityType, pagination });
3664
- }
3665
- /**
3666
- * WORKFLOWS
3667
- */
3668
- async updateWorkflowResults({
3669
- workflowName,
3670
- runId,
3671
- stepId,
3672
- result,
3673
- requestContext
3674
- }) {
3675
- return this.stores.workflows.updateWorkflowResults({ workflowName, runId, stepId, result, requestContext });
3676
- }
3677
- async updateWorkflowState({
3678
- workflowName,
3679
- runId,
3680
- opts
3681
- }) {
3682
- return this.stores.workflows.updateWorkflowState({ workflowName, runId, opts });
3683
- }
3684
- async persistWorkflowSnapshot({
3685
- workflowName,
3686
- runId,
3687
- resourceId,
3688
- snapshot
3689
- }) {
3690
- return this.stores.workflows.persistWorkflowSnapshot({ workflowName, runId, resourceId, snapshot });
3691
- }
3692
- async loadWorkflowSnapshot({
3693
- workflowName,
3694
- runId
3695
- }) {
3696
- return this.stores.workflows.loadWorkflowSnapshot({ workflowName, runId });
3697
- }
3698
- async listWorkflowRuns(args = {}) {
3699
- return this.stores.workflows.listWorkflowRuns(args);
3700
- }
3701
- async getWorkflowRunById({
3702
- runId,
3703
- workflowName
3704
- }) {
3705
- return this.stores.workflows.getWorkflowRunById({ runId, workflowName });
3706
- }
3707
- async deleteWorkflowRunById({ runId, workflowName }) {
3708
- return this.stores.workflows.deleteWorkflowRunById({ runId, workflowName });
3709
- }
3710
- async getResourceById({ resourceId }) {
3711
- return this.stores.memory.getResourceById({ resourceId });
3712
- }
3713
- async saveResource({ resource }) {
3714
- return this.stores.memory.saveResource({ resource });
3715
- }
3716
- async updateResource({
3717
- resourceId,
3718
- workingMemory,
3719
- metadata
3720
- }) {
3721
- return this.stores.memory.updateResource({ resourceId, workingMemory, metadata });
3722
- }
3723
- async createSpan(span) {
3724
- return this.stores.observability.createSpan(span);
3725
- }
3726
- async updateSpan(params) {
3727
- return this.stores.observability.updateSpan(params);
3728
- }
3729
- async getTrace(traceId) {
3730
- return this.stores.observability.getTrace(traceId);
3731
- }
3732
- async getTracesPaginated(args) {
3733
- return this.stores.observability.getTracesPaginated(args);
3734
- }
3735
- async listScoresBySpan({
3736
- traceId,
3737
- spanId,
3738
- pagination
3739
- }) {
3740
- return this.stores.scores.listScoresBySpan({ traceId, spanId, pagination });
3741
- }
3742
- async batchCreateSpans(args) {
3743
- return this.stores.observability.batchCreateSpans(args);
3744
- }
3745
- async batchUpdateSpans(args) {
3746
- return this.stores.observability.batchUpdateSpans(args);
3747
- }
3748
4313
  };
3749
4314
 
3750
4315
  // src/vector/prompt.ts
@@ -3846,9 +4411,14 @@ Example Complex Query:
3846
4411
  ]
3847
4412
  }`;
3848
4413
 
4414
+ exports.AgentsLibSQL = AgentsLibSQL;
3849
4415
  exports.DefaultStorage = LibSQLStore;
3850
4416
  exports.LIBSQL_PROMPT = LIBSQL_PROMPT;
3851
4417
  exports.LibSQLStore = LibSQLStore;
3852
4418
  exports.LibSQLVector = LibSQLVector;
4419
+ exports.MemoryLibSQL = MemoryLibSQL;
4420
+ exports.ObservabilityLibSQL = ObservabilityLibSQL;
4421
+ exports.ScoresLibSQL = ScoresLibSQL;
4422
+ exports.WorkflowsLibSQL = WorkflowsLibSQL;
3853
4423
  //# sourceMappingURL=index.cjs.map
3854
4424
  //# sourceMappingURL=index.cjs.map