@mastra/libsql 0.10.4-alpha.0 → 0.10.4-alpha.2

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.
@@ -2,6 +2,7 @@ import { createClient } from '@libsql/client';
2
2
  import type { Client, InValue } from '@libsql/client';
3
3
  import { MessageList } from '@mastra/core/agent';
4
4
  import type { MastraMessageContentV2, MastraMessageV2 } from '@mastra/core/agent';
5
+ import { ErrorCategory, ErrorDomain, MastraError } from '@mastra/core/error';
5
6
  import type { MetricResult, TestInfo } from '@mastra/core/eval';
6
7
  import type { MastraMessageV1, StorageThreadType } from '@mastra/core/memory';
7
8
  import {
@@ -10,6 +11,7 @@ import {
10
11
  TABLE_MESSAGES,
11
12
  TABLE_THREADS,
12
13
  TABLE_TRACES,
14
+ TABLE_RESOURCES,
13
15
  TABLE_WORKFLOW_SNAPSHOT,
14
16
  } from '@mastra/core/storage';
15
17
  import type {
@@ -18,6 +20,7 @@ import type {
18
20
  PaginationInfo,
19
21
  StorageColumn,
20
22
  StorageGetMessagesArg,
23
+ StorageResourceType,
21
24
  TABLE_NAMES,
22
25
  WorkflowRun,
23
26
  WorkflowRuns,
@@ -83,9 +86,11 @@ export class LibSQLStore extends MastraStorage {
83
86
 
84
87
  public get supports(): {
85
88
  selectByIncludeResourceScope: boolean;
89
+ resourceWorkingMemory: boolean;
86
90
  } {
87
91
  return {
88
92
  selectByIncludeResourceScope: true,
93
+ resourceWorkingMemory: true,
89
94
  };
90
95
  }
91
96
 
@@ -128,8 +133,19 @@ export class LibSQLStore extends MastraStorage {
128
133
  const sql = this.getCreateTableSQL(tableName, schema);
129
134
  await this.client.execute(sql);
130
135
  } catch (error) {
131
- this.logger.error(`Error creating table ${tableName}: ${error}`);
132
- throw error;
136
+ // this.logger.error(`Error creating table ${tableName}: ${error}`);
137
+ // throw error;
138
+ throw new MastraError(
139
+ {
140
+ id: 'LIBSQL_STORE_CREATE_TABLE_FAILED',
141
+ domain: ErrorDomain.STORAGE,
142
+ category: ErrorCategory.THIRD_PARTY,
143
+ details: {
144
+ tableName,
145
+ },
146
+ },
147
+ error,
148
+ );
133
149
  }
134
150
  }
135
151
 
@@ -183,10 +199,17 @@ export class LibSQLStore extends MastraStorage {
183
199
  }
184
200
  }
185
201
  } catch (error) {
186
- this.logger?.error?.(
187
- `Error altering table ${tableName}: ${error instanceof Error ? error.message : String(error)}`,
202
+ throw new MastraError(
203
+ {
204
+ id: 'LIBSQL_STORE_ALTER_TABLE_FAILED',
205
+ domain: ErrorDomain.STORAGE,
206
+ category: ErrorCategory.THIRD_PARTY,
207
+ details: {
208
+ tableName,
209
+ },
210
+ },
211
+ error,
188
212
  );
189
- throw new Error(`Failed to alter table ${tableName}: ${error}`);
190
213
  }
191
214
  }
192
215
 
@@ -195,9 +218,19 @@ export class LibSQLStore extends MastraStorage {
195
218
  try {
196
219
  await this.client.execute(`DELETE FROM ${parsedTableName}`);
197
220
  } catch (e) {
198
- if (e instanceof Error) {
199
- this.logger.error(e.message);
200
- }
221
+ const mastraError = new MastraError(
222
+ {
223
+ id: 'LIBSQL_STORE_CLEAR_TABLE_FAILED',
224
+ domain: ErrorDomain.STORAGE,
225
+ category: ErrorCategory.THIRD_PARTY,
226
+ details: {
227
+ tableName,
228
+ },
229
+ },
230
+ e,
231
+ );
232
+ this.logger?.trackException?.(mastraError);
233
+ this.logger?.error?.(mastraError.toString());
201
234
  }
202
235
  }
203
236
 
@@ -277,7 +310,19 @@ export class LibSQLStore extends MastraStorage {
277
310
  return this.executeWriteOperationWithRetry(
278
311
  () => this.doBatchInsert(args),
279
312
  `batch insert into table ${args.tableName}`,
280
- );
313
+ ).catch(error => {
314
+ throw new MastraError(
315
+ {
316
+ id: 'LIBSQL_STORE_BATCH_INSERT_FAILED',
317
+ domain: ErrorDomain.STORAGE,
318
+ category: ErrorCategory.THIRD_PARTY,
319
+ details: {
320
+ tableName: args.tableName,
321
+ },
322
+ },
323
+ error,
324
+ );
325
+ });
281
326
  }
282
327
 
283
328
  private async doBatchInsert({
@@ -327,19 +372,31 @@ export class LibSQLStore extends MastraStorage {
327
372
  }
328
373
 
329
374
  async getThreadById({ threadId }: { threadId: string }): Promise<StorageThreadType | null> {
330
- const result = await this.load<StorageThreadType>({
331
- tableName: TABLE_THREADS,
332
- keys: { id: threadId },
333
- });
375
+ try {
376
+ const result = await this.load<StorageThreadType>({
377
+ tableName: TABLE_THREADS,
378
+ keys: { id: threadId },
379
+ });
334
380
 
335
- if (!result) {
336
- return null;
337
- }
381
+ if (!result) {
382
+ return null;
383
+ }
338
384
 
339
- return {
340
- ...result,
341
- metadata: typeof result.metadata === 'string' ? JSON.parse(result.metadata) : result.metadata,
342
- };
385
+ return {
386
+ ...result,
387
+ metadata: typeof result.metadata === 'string' ? JSON.parse(result.metadata) : result.metadata,
388
+ };
389
+ } catch (error) {
390
+ throw new MastraError(
391
+ {
392
+ id: 'LIBSQL_STORE_GET_THREAD_BY_ID_FAILED',
393
+ domain: ErrorDomain.STORAGE,
394
+ category: ErrorCategory.THIRD_PARTY,
395
+ details: { threadId },
396
+ },
397
+ error,
398
+ );
399
+ }
343
400
  }
344
401
 
345
402
  /**
@@ -372,7 +429,17 @@ export class LibSQLStore extends MastraStorage {
372
429
  }
373
430
  return result.rows.map(mapRowToStorageThreadType);
374
431
  } catch (error) {
375
- this.logger.error(`Error getting threads for resource ${resourceId}:`, error);
432
+ const mastraError = new MastraError(
433
+ {
434
+ id: 'LIBSQL_STORE_GET_THREADS_BY_RESOURCE_ID_FAILED',
435
+ domain: ErrorDomain.STORAGE,
436
+ category: ErrorCategory.THIRD_PARTY,
437
+ details: { resourceId },
438
+ },
439
+ error,
440
+ );
441
+ this.logger?.trackException?.(mastraError);
442
+ this.logger?.error?.(mastraError.toString());
376
443
  return [];
377
444
  }
378
445
  }
@@ -430,21 +497,46 @@ export class LibSQLStore extends MastraStorage {
430
497
  hasMore: currentOffset + threads.length < total,
431
498
  };
432
499
  } catch (error) {
433
- this.logger.error(`Error getting threads for resource ${resourceId}:`, error);
500
+ const mastraError = new MastraError(
501
+ {
502
+ id: 'LIBSQL_STORE_GET_THREADS_BY_RESOURCE_ID_FAILED',
503
+ domain: ErrorDomain.STORAGE,
504
+ category: ErrorCategory.THIRD_PARTY,
505
+ details: { resourceId },
506
+ },
507
+ error,
508
+ );
509
+ this.logger?.trackException?.(mastraError);
510
+ this.logger?.error?.(mastraError.toString());
434
511
  return { threads: [], total: 0, page, perPage, hasMore: false };
435
512
  }
436
513
  }
437
514
 
438
515
  async saveThread({ thread }: { thread: StorageThreadType }): Promise<StorageThreadType> {
439
- await this.insert({
440
- tableName: TABLE_THREADS,
441
- record: {
442
- ...thread,
443
- metadata: JSON.stringify(thread.metadata),
444
- },
445
- });
516
+ try {
517
+ await this.insert({
518
+ tableName: TABLE_THREADS,
519
+ record: {
520
+ ...thread,
521
+ metadata: JSON.stringify(thread.metadata),
522
+ },
523
+ });
446
524
 
447
- return thread;
525
+ return thread;
526
+ } catch (error) {
527
+ const mastraError = new MastraError(
528
+ {
529
+ id: 'LIBSQL_STORE_SAVE_THREAD_FAILED',
530
+ domain: ErrorDomain.STORAGE,
531
+ category: ErrorCategory.THIRD_PARTY,
532
+ details: { threadId: thread.id },
533
+ },
534
+ error,
535
+ );
536
+ this.logger?.trackException?.(mastraError);
537
+ this.logger?.error?.(mastraError.toString());
538
+ throw mastraError;
539
+ }
448
540
  }
449
541
 
450
542
  async updateThread({
@@ -458,7 +550,13 @@ export class LibSQLStore extends MastraStorage {
458
550
  }): Promise<StorageThreadType> {
459
551
  const thread = await this.getThreadById({ threadId: id });
460
552
  if (!thread) {
461
- throw new Error(`Thread ${id} not found`);
553
+ throw new MastraError({
554
+ id: 'LIBSQL_STORE_UPDATE_THREAD_FAILED_THREAD_NOT_FOUND',
555
+ domain: ErrorDomain.STORAGE,
556
+ category: ErrorCategory.USER,
557
+ text: `Thread ${id} not found`,
558
+ details: { threadId: id },
559
+ });
462
560
  }
463
561
 
464
562
  const updatedThread = {
@@ -470,24 +568,49 @@ export class LibSQLStore extends MastraStorage {
470
568
  },
471
569
  };
472
570
 
473
- await this.client.execute({
474
- sql: `UPDATE ${TABLE_THREADS} SET title = ?, metadata = ? WHERE id = ?`,
475
- args: [title, JSON.stringify(updatedThread.metadata), id],
476
- });
571
+ try {
572
+ await this.client.execute({
573
+ sql: `UPDATE ${TABLE_THREADS} SET title = ?, metadata = ? WHERE id = ?`,
574
+ args: [title, JSON.stringify(updatedThread.metadata), id],
575
+ });
477
576
 
478
- return updatedThread;
577
+ return updatedThread;
578
+ } catch (error) {
579
+ throw new MastraError(
580
+ {
581
+ id: 'LIBSQL_STORE_UPDATE_THREAD_FAILED',
582
+ domain: ErrorDomain.STORAGE,
583
+ category: ErrorCategory.THIRD_PARTY,
584
+ text: `Failed to update thread ${id}`,
585
+ details: { threadId: id },
586
+ },
587
+ error,
588
+ );
589
+ }
479
590
  }
480
591
 
481
592
  async deleteThread({ threadId }: { threadId: string }): Promise<void> {
482
593
  // Delete messages for this thread (manual step)
483
- await this.client.execute({
484
- sql: `DELETE FROM ${TABLE_MESSAGES} WHERE thread_id = ?`,
485
- args: [threadId],
486
- });
487
- await this.client.execute({
488
- sql: `DELETE FROM ${TABLE_THREADS} WHERE id = ?`,
489
- args: [threadId],
490
- });
594
+ try {
595
+ await this.client.execute({
596
+ sql: `DELETE FROM ${TABLE_MESSAGES} WHERE thread_id = ?`,
597
+ args: [threadId],
598
+ });
599
+ await this.client.execute({
600
+ sql: `DELETE FROM ${TABLE_THREADS} WHERE id = ?`,
601
+ args: [threadId],
602
+ });
603
+ } catch (error) {
604
+ throw new MastraError(
605
+ {
606
+ id: 'LIBSQL_STORE_DELETE_THREAD_FAILED',
607
+ domain: ErrorDomain.STORAGE,
608
+ category: ErrorCategory.THIRD_PARTY,
609
+ details: { threadId },
610
+ },
611
+ error,
612
+ );
613
+ }
491
614
  // TODO: Need to check if CASCADE is enabled so that messages will be automatically deleted due to CASCADE constraint
492
615
  }
493
616
 
@@ -577,8 +700,7 @@ export class LibSQLStore extends MastraStorage {
577
700
  }): Promise<MastraMessageV1[] | MastraMessageV2[]> {
578
701
  try {
579
702
  const messages: MastraMessageV2[] = [];
580
- const limit = typeof selectBy?.last === `number` ? selectBy.last : 40;
581
-
703
+ const limit = this.resolveMessageLimit({ last: selectBy?.last, defaultLimit: 40 });
582
704
  if (selectBy?.include?.length) {
583
705
  const includeMessages = await this._getIncludedMessages({ threadId, selectBy });
584
706
  if (includeMessages) {
@@ -612,8 +734,15 @@ export class LibSQLStore extends MastraStorage {
612
734
  if (format === `v2`) return list.get.all.v2();
613
735
  return list.get.all.v1();
614
736
  } catch (error) {
615
- this.logger.error('Error getting messages:', error as Error);
616
- throw error;
737
+ throw new MastraError(
738
+ {
739
+ id: 'LIBSQL_STORE_GET_MESSAGES_FAILED',
740
+ domain: ErrorDomain.STORAGE,
741
+ category: ErrorCategory.THIRD_PARTY,
742
+ details: { threadId },
743
+ },
744
+ error,
745
+ );
617
746
  }
618
747
  }
619
748
 
@@ -623,16 +752,30 @@ export class LibSQLStore extends MastraStorage {
623
752
  },
624
753
  ): Promise<PaginationInfo & { messages: MastraMessageV1[] | MastraMessageV2[] }> {
625
754
  const { threadId, format, selectBy } = args;
626
- const { page = 0, perPage = 40, dateRange } = selectBy?.pagination || {};
755
+ const { page = 0, perPage: perPageInput, dateRange } = selectBy?.pagination || {};
756
+ const perPage =
757
+ perPageInput !== undefined ? perPageInput : this.resolveMessageLimit({ last: selectBy?.last, defaultLimit: 40 });
627
758
  const fromDate = dateRange?.start;
628
759
  const toDate = dateRange?.end;
629
760
 
630
761
  const messages: MastraMessageV2[] = [];
631
762
 
632
763
  if (selectBy?.include?.length) {
633
- const includeMessages = await this._getIncludedMessages({ threadId, selectBy });
634
- if (includeMessages) {
635
- messages.push(...includeMessages);
764
+ try {
765
+ const includeMessages = await this._getIncludedMessages({ threadId, selectBy });
766
+ if (includeMessages) {
767
+ messages.push(...includeMessages);
768
+ }
769
+ } catch (error) {
770
+ throw new MastraError(
771
+ {
772
+ id: 'LIBSQL_STORE_GET_MESSAGES_PAGINATED_GET_INCLUDE_MESSAGES_FAILED',
773
+ domain: ErrorDomain.STORAGE,
774
+ category: ErrorCategory.THIRD_PARTY,
775
+ details: { threadId },
776
+ },
777
+ error,
778
+ );
636
779
  }
637
780
  }
638
781
 
@@ -658,7 +801,7 @@ export class LibSQLStore extends MastraStorage {
658
801
  });
659
802
  const total = Number(countResult.rows?.[0]?.count ?? 0);
660
803
 
661
- if (total === 0) {
804
+ if (total === 0 && messages.length === 0) {
662
805
  return {
663
806
  messages: [],
664
807
  total: 0,
@@ -668,9 +811,12 @@ export class LibSQLStore extends MastraStorage {
668
811
  };
669
812
  }
670
813
 
814
+ const excludeIds = messages.map(m => m.id);
815
+ const excludeIdsParam = excludeIds.map((_, idx) => `$${idx + queryParams.length + 1}`).join(', ');
816
+
671
817
  const dataResult = await this.client.execute({
672
- sql: `SELECT id, content, role, type, "createdAt", thread_id FROM ${TABLE_MESSAGES} ${whereClause} ORDER BY "createdAt" DESC LIMIT ? OFFSET ?`,
673
- args: [...queryParams, perPage, currentOffset],
818
+ sql: `SELECT id, content, role, type, "createdAt", "resourceId", "thread_id" FROM ${TABLE_MESSAGES} ${whereClause} ${excludeIds.length ? `AND id NOT IN (${excludeIdsParam})` : ''} ORDER BY "createdAt" DESC LIMIT ? OFFSET ?`,
819
+ args: [...queryParams, ...excludeIds, perPage, currentOffset],
674
820
  });
675
821
 
676
822
  messages.push(...(dataResult.rows || []).map((row: any) => this.parseRow(row)));
@@ -688,7 +834,17 @@ export class LibSQLStore extends MastraStorage {
688
834
  hasMore: currentOffset + messages.length < total,
689
835
  };
690
836
  } catch (error) {
691
- this.logger.error('Error getting paginated messages:', error as Error);
837
+ const mastraError = new MastraError(
838
+ {
839
+ id: 'LIBSQL_STORE_GET_MESSAGES_PAGINATED_FAILED',
840
+ domain: ErrorDomain.STORAGE,
841
+ category: ErrorCategory.THIRD_PARTY,
842
+ details: { threadId },
843
+ },
844
+ error,
845
+ );
846
+ this.logger?.trackException?.(mastraError);
847
+ this.logger?.error?.(mastraError.toString());
692
848
  return { messages: [], total: 0, page, perPage, hasMore: false };
693
849
  }
694
850
  }
@@ -724,7 +880,14 @@ export class LibSQLStore extends MastraStorage {
724
880
  }
725
881
  return {
726
882
  sql: `INSERT INTO ${TABLE_MESSAGES} (id, thread_id, content, role, type, createdAt, resourceId)
727
- VALUES (?, ?, ?, ?, ?, ?, ?)`,
883
+ VALUES (?, ?, ?, ?, ?, ?, ?)
884
+ ON CONFLICT(id) DO UPDATE SET
885
+ thread_id=excluded.thread_id,
886
+ content=excluded.content,
887
+ role=excluded.role,
888
+ type=excluded.type,
889
+ resourceId=excluded.resourceId
890
+ `,
728
891
  args: [
729
892
  message.id,
730
893
  message.threadId!,
@@ -750,8 +913,14 @@ export class LibSQLStore extends MastraStorage {
750
913
  if (format === `v2`) return list.get.all.v2();
751
914
  return list.get.all.v1();
752
915
  } catch (error) {
753
- this.logger.error('Failed to save messages in database: ' + (error as { message: string })?.message);
754
- throw error;
916
+ throw new MastraError(
917
+ {
918
+ id: 'LIBSQL_STORE_SAVE_MESSAGES_FAILED',
919
+ domain: ErrorDomain.STORAGE,
920
+ category: ErrorCategory.THIRD_PARTY,
921
+ },
922
+ error,
923
+ );
755
924
  }
756
925
  }
757
926
 
@@ -905,8 +1074,15 @@ export class LibSQLStore extends MastraStorage {
905
1074
  if (error instanceof Error && error.message.includes('no such table')) {
906
1075
  return [];
907
1076
  }
908
- this.logger.error('Failed to get evals for the specified agent: ' + (error as any)?.message);
909
- throw error;
1077
+ throw new MastraError(
1078
+ {
1079
+ id: 'LIBSQL_STORE_GET_EVALS_BY_AGENT_NAME_FAILED',
1080
+ domain: ErrorDomain.STORAGE,
1081
+ category: ErrorCategory.THIRD_PARTY,
1082
+ details: { agentName },
1083
+ },
1084
+ error,
1085
+ );
910
1086
  }
911
1087
  }
912
1088
 
@@ -946,37 +1122,48 @@ export class LibSQLStore extends MastraStorage {
946
1122
 
947
1123
  const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
948
1124
 
949
- const countResult = await this.client.execute({
950
- sql: `SELECT COUNT(*) as count FROM ${TABLE_EVALS} ${whereClause}`,
951
- args: queryParams,
952
- });
953
- const total = Number(countResult.rows?.[0]?.count ?? 0);
1125
+ try {
1126
+ const countResult = await this.client.execute({
1127
+ sql: `SELECT COUNT(*) as count FROM ${TABLE_EVALS} ${whereClause}`,
1128
+ args: queryParams,
1129
+ });
1130
+ const total = Number(countResult.rows?.[0]?.count ?? 0);
954
1131
 
955
- const currentOffset = page * perPage;
956
- const hasMore = currentOffset + perPage < total;
1132
+ const currentOffset = page * perPage;
1133
+ const hasMore = currentOffset + perPage < total;
1134
+
1135
+ if (total === 0) {
1136
+ return {
1137
+ evals: [],
1138
+ total: 0,
1139
+ page,
1140
+ perPage,
1141
+ hasMore: false,
1142
+ };
1143
+ }
1144
+
1145
+ const dataResult = await this.client.execute({
1146
+ sql: `SELECT * FROM ${TABLE_EVALS} ${whereClause} ORDER BY created_at DESC LIMIT ? OFFSET ?`,
1147
+ args: [...queryParams, perPage, currentOffset],
1148
+ });
957
1149
 
958
- if (total === 0) {
959
1150
  return {
960
- evals: [],
961
- total: 0,
1151
+ evals: dataResult.rows?.map(row => this.transformEvalRow(row)) ?? [],
1152
+ total,
962
1153
  page,
963
1154
  perPage,
964
- hasMore: false,
1155
+ hasMore,
965
1156
  };
1157
+ } catch (error) {
1158
+ throw new MastraError(
1159
+ {
1160
+ id: 'LIBSQL_STORE_GET_EVALS_FAILED',
1161
+ domain: ErrorDomain.STORAGE,
1162
+ category: ErrorCategory.THIRD_PARTY,
1163
+ },
1164
+ error,
1165
+ );
966
1166
  }
967
-
968
- const dataResult = await this.client.execute({
969
- sql: `SELECT * FROM ${TABLE_EVALS} ${whereClause} ORDER BY created_at DESC LIMIT ? OFFSET ?`,
970
- args: [...queryParams, perPage, currentOffset],
971
- });
972
-
973
- return {
974
- evals: dataResult.rows?.map(row => this.transformEvalRow(row)) ?? [],
975
- total,
976
- page,
977
- perPage,
978
- hasMore,
979
- };
980
1167
  }
981
1168
 
982
1169
  /**
@@ -998,8 +1185,19 @@ export class LibSQLStore extends MastraStorage {
998
1185
  end: args.toDate,
999
1186
  };
1000
1187
  }
1001
- const result = await this.getTracesPaginated(args);
1002
- return result.traces;
1188
+ try {
1189
+ const result = await this.getTracesPaginated(args);
1190
+ return result.traces;
1191
+ } catch (error) {
1192
+ throw new MastraError(
1193
+ {
1194
+ id: 'LIBSQL_STORE_GET_TRACES_FAILED',
1195
+ domain: ErrorDomain.STORAGE,
1196
+ category: ErrorCategory.THIRD_PARTY,
1197
+ },
1198
+ error,
1199
+ );
1200
+ }
1003
1201
  }
1004
1202
 
1005
1203
  public async getTracesPaginated(
@@ -1049,55 +1247,66 @@ export class LibSQLStore extends MastraStorage {
1049
1247
 
1050
1248
  const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
1051
1249
 
1052
- const countResult = await this.client.execute({
1053
- sql: `SELECT COUNT(*) as count FROM ${TABLE_TRACES} ${whereClause}`,
1054
- args: queryArgs,
1055
- });
1056
- const total = Number(countResult.rows?.[0]?.count ?? 0);
1250
+ try {
1251
+ const countResult = await this.client.execute({
1252
+ sql: `SELECT COUNT(*) as count FROM ${TABLE_TRACES} ${whereClause}`,
1253
+ args: queryArgs,
1254
+ });
1255
+ const total = Number(countResult.rows?.[0]?.count ?? 0);
1256
+
1257
+ if (total === 0) {
1258
+ return {
1259
+ traces: [],
1260
+ total: 0,
1261
+ page,
1262
+ perPage,
1263
+ hasMore: false,
1264
+ };
1265
+ }
1266
+
1267
+ const dataResult = await this.client.execute({
1268
+ sql: `SELECT * FROM ${TABLE_TRACES} ${whereClause} ORDER BY "startTime" DESC LIMIT ? OFFSET ?`,
1269
+ args: [...queryArgs, perPage, currentOffset],
1270
+ });
1271
+
1272
+ const traces =
1273
+ dataResult.rows?.map(
1274
+ row =>
1275
+ ({
1276
+ id: row.id,
1277
+ parentSpanId: row.parentSpanId,
1278
+ traceId: row.traceId,
1279
+ name: row.name,
1280
+ scope: row.scope,
1281
+ kind: row.kind,
1282
+ status: safelyParseJSON(row.status as string),
1283
+ events: safelyParseJSON(row.events as string),
1284
+ links: safelyParseJSON(row.links as string),
1285
+ attributes: safelyParseJSON(row.attributes as string),
1286
+ startTime: row.startTime,
1287
+ endTime: row.endTime,
1288
+ other: safelyParseJSON(row.other as string),
1289
+ createdAt: row.createdAt,
1290
+ }) as Trace,
1291
+ ) ?? [];
1057
1292
 
1058
- if (total === 0) {
1059
1293
  return {
1060
- traces: [],
1061
- total: 0,
1294
+ traces,
1295
+ total,
1062
1296
  page,
1063
1297
  perPage,
1064
- hasMore: false,
1298
+ hasMore: currentOffset + traces.length < total,
1065
1299
  };
1300
+ } catch (error) {
1301
+ throw new MastraError(
1302
+ {
1303
+ id: 'LIBSQL_STORE_GET_TRACES_PAGINATED_FAILED',
1304
+ domain: ErrorDomain.STORAGE,
1305
+ category: ErrorCategory.THIRD_PARTY,
1306
+ },
1307
+ error,
1308
+ );
1066
1309
  }
1067
-
1068
- const dataResult = await this.client.execute({
1069
- sql: `SELECT * FROM ${TABLE_TRACES} ${whereClause} ORDER BY "startTime" DESC LIMIT ? OFFSET ?`,
1070
- args: [...queryArgs, perPage, currentOffset],
1071
- });
1072
-
1073
- const traces =
1074
- dataResult.rows?.map(
1075
- row =>
1076
- ({
1077
- id: row.id,
1078
- parentSpanId: row.parentSpanId,
1079
- traceId: row.traceId,
1080
- name: row.name,
1081
- scope: row.scope,
1082
- kind: row.kind,
1083
- status: safelyParseJSON(row.status as string),
1084
- events: safelyParseJSON(row.events as string),
1085
- links: safelyParseJSON(row.links as string),
1086
- attributes: safelyParseJSON(row.attributes as string),
1087
- startTime: row.startTime,
1088
- endTime: row.endTime,
1089
- other: safelyParseJSON(row.other as string),
1090
- createdAt: row.createdAt,
1091
- }) as Trace,
1092
- ) ?? [];
1093
-
1094
- return {
1095
- traces,
1096
- total,
1097
- page,
1098
- perPage,
1099
- hasMore: currentOffset + traces.length < total,
1100
- };
1101
1310
  }
1102
1311
 
1103
1312
  async getWorkflowRuns({
@@ -1167,8 +1376,14 @@ export class LibSQLStore extends MastraStorage {
1167
1376
  // Use runs.length as total when not paginating
1168
1377
  return { runs, total: total || runs.length };
1169
1378
  } catch (error) {
1170
- console.error('Error getting workflow runs:', error);
1171
- throw error;
1379
+ throw new MastraError(
1380
+ {
1381
+ id: 'LIBSQL_STORE_GET_WORKFLOW_RUNS_FAILED',
1382
+ domain: ErrorDomain.STORAGE,
1383
+ category: ErrorCategory.THIRD_PARTY,
1384
+ },
1385
+ error,
1386
+ );
1172
1387
  }
1173
1388
  }
1174
1389
 
@@ -1194,16 +1409,117 @@ export class LibSQLStore extends MastraStorage {
1194
1409
 
1195
1410
  const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
1196
1411
 
1197
- const result = await this.client.execute({
1198
- sql: `SELECT * FROM ${TABLE_WORKFLOW_SNAPSHOT} ${whereClause}`,
1199
- args,
1412
+ try {
1413
+ const result = await this.client.execute({
1414
+ sql: `SELECT * FROM ${TABLE_WORKFLOW_SNAPSHOT} ${whereClause}`,
1415
+ args,
1416
+ });
1417
+
1418
+ if (!result.rows?.[0]) {
1419
+ return null;
1420
+ }
1421
+
1422
+ return this.parseWorkflowRun(result.rows[0]);
1423
+ } catch (error) {
1424
+ throw new MastraError(
1425
+ {
1426
+ id: 'LIBSQL_STORE_GET_WORKFLOW_RUN_BY_ID_FAILED',
1427
+ domain: ErrorDomain.STORAGE,
1428
+ category: ErrorCategory.THIRD_PARTY,
1429
+ },
1430
+ error,
1431
+ );
1432
+ }
1433
+ }
1434
+
1435
+ async getResourceById({ resourceId }: { resourceId: string }): Promise<StorageResourceType | null> {
1436
+ const result = await this.load<StorageResourceType>({
1437
+ tableName: TABLE_RESOURCES,
1438
+ keys: { id: resourceId },
1200
1439
  });
1201
1440
 
1202
- if (!result.rows?.[0]) {
1441
+ if (!result) {
1203
1442
  return null;
1204
1443
  }
1205
1444
 
1206
- return this.parseWorkflowRun(result.rows[0]);
1445
+ return {
1446
+ ...result,
1447
+ // Ensure workingMemory is always returned as a string, even if auto-parsed as JSON
1448
+ workingMemory:
1449
+ typeof result.workingMemory === 'object' ? JSON.stringify(result.workingMemory) : result.workingMemory,
1450
+ metadata: typeof result.metadata === 'string' ? JSON.parse(result.metadata) : result.metadata,
1451
+ };
1452
+ }
1453
+
1454
+ async saveResource({ resource }: { resource: StorageResourceType }): Promise<StorageResourceType> {
1455
+ await this.insert({
1456
+ tableName: TABLE_RESOURCES,
1457
+ record: {
1458
+ ...resource,
1459
+ metadata: JSON.stringify(resource.metadata),
1460
+ },
1461
+ });
1462
+
1463
+ return resource;
1464
+ }
1465
+
1466
+ async updateResource({
1467
+ resourceId,
1468
+ workingMemory,
1469
+ metadata,
1470
+ }: {
1471
+ resourceId: string;
1472
+ workingMemory?: string;
1473
+ metadata?: Record<string, unknown>;
1474
+ }): Promise<StorageResourceType> {
1475
+ const existingResource = await this.getResourceById({ resourceId });
1476
+
1477
+ if (!existingResource) {
1478
+ // Create new resource if it doesn't exist
1479
+ const newResource: StorageResourceType = {
1480
+ id: resourceId,
1481
+ workingMemory,
1482
+ metadata: metadata || {},
1483
+ createdAt: new Date(),
1484
+ updatedAt: new Date(),
1485
+ };
1486
+ return this.saveResource({ resource: newResource });
1487
+ }
1488
+
1489
+ const updatedResource = {
1490
+ ...existingResource,
1491
+ workingMemory: workingMemory !== undefined ? workingMemory : existingResource.workingMemory,
1492
+ metadata: {
1493
+ ...existingResource.metadata,
1494
+ ...metadata,
1495
+ },
1496
+ updatedAt: new Date(),
1497
+ };
1498
+
1499
+ const updates: string[] = [];
1500
+ const values: InValue[] = [];
1501
+
1502
+ if (workingMemory !== undefined) {
1503
+ updates.push('workingMemory = ?');
1504
+ values.push(workingMemory);
1505
+ }
1506
+
1507
+ if (metadata) {
1508
+ updates.push('metadata = ?');
1509
+ values.push(JSON.stringify(updatedResource.metadata));
1510
+ }
1511
+
1512
+ updates.push('updatedAt = ?');
1513
+ values.push(updatedResource.updatedAt.toISOString());
1514
+
1515
+ values.push(resourceId);
1516
+
1517
+ await this.client.execute({
1518
+ sql: `UPDATE ${TABLE_RESOURCES} SET ${updates.join(', ')} WHERE id = ?`,
1519
+ args: values,
1520
+ });
1521
+
1522
+ return updatedResource;
1207
1523
  }
1208
1524
 
1209
1525
  private async hasColumn(table: string, column: string): Promise<boolean> {