@mastra/upstash 0.11.1-alpha.0 → 0.11.1-alpha.1

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.
@@ -1,5 +1,6 @@
1
1
  import { MessageList } from '@mastra/core/agent';
2
2
  import type { MastraMessageContentV2, MastraMessageV2 } from '@mastra/core/agent';
3
+ import { ErrorCategory, ErrorDomain, MastraError } from '@mastra/core/error';
3
4
  import type { MetricResult, TestInfo } from '@mastra/core/eval';
4
5
  import type { StorageThreadType, MastraMessageV1 } from '@mastra/core/memory';
5
6
  import {
@@ -271,7 +272,17 @@ export class UpstashStore extends MastraStorage {
271
272
  // Transform to EvalRow format
272
273
  return filteredEvals.map(record => this.transformEvalRecord(record));
273
274
  } catch (error) {
274
- console.error('Failed to get evals for the specified agent:', error);
275
+ const mastraError = new MastraError(
276
+ {
277
+ id: 'STORAGE_UPSTASH_STORAGE_GET_EVALS_BY_AGENT_NAME_FAILED',
278
+ domain: ErrorDomain.STORAGE,
279
+ category: ErrorCategory.THIRD_PARTY,
280
+ details: { agentName },
281
+ },
282
+ error,
283
+ );
284
+ this.logger?.trackException(mastraError);
285
+ this.logger.error(mastraError.toString());
275
286
  return [];
276
287
  }
277
288
  }
@@ -286,8 +297,19 @@ export class UpstashStore extends MastraStorage {
286
297
  end: args.toDate,
287
298
  };
288
299
  }
289
- const { traces } = await this.getTracesPaginated(args);
290
- return traces;
300
+ try {
301
+ const { traces } = await this.getTracesPaginated(args);
302
+ return traces;
303
+ } catch (error) {
304
+ throw new MastraError(
305
+ {
306
+ id: 'STORAGE_UPSTASH_STORAGE_GET_TRACES_FAILED',
307
+ domain: ErrorDomain.STORAGE,
308
+ category: ErrorCategory.THIRD_PARTY,
309
+ },
310
+ error,
311
+ );
312
+ }
291
313
  }
292
314
 
293
315
  public async getTracesPaginated(
@@ -389,7 +411,20 @@ export class UpstashStore extends MastraStorage {
389
411
  hasMore,
390
412
  };
391
413
  } catch (error) {
392
- console.error('Failed to get traces:', error);
414
+ const mastraError = new MastraError(
415
+ {
416
+ id: 'STORAGE_UPSTASH_STORAGE_GET_TRACES_PAGINATED_FAILED',
417
+ domain: ErrorDomain.STORAGE,
418
+ category: ErrorCategory.THIRD_PARTY,
419
+ details: {
420
+ name: args.name || '',
421
+ scope: args.scope || '',
422
+ },
423
+ },
424
+ error,
425
+ );
426
+ this.logger?.trackException(mastraError);
427
+ this.logger.error(mastraError.toString());
393
428
  return {
394
429
  traces: [],
395
430
  total: 0,
@@ -409,7 +444,21 @@ export class UpstashStore extends MastraStorage {
409
444
  }): Promise<void> {
410
445
  // Redis is schemaless, so we don't need to create tables
411
446
  // But we can store the schema for reference
412
- await this.redis.set(`schema:${tableName}`, schema);
447
+ try {
448
+ await this.redis.set(`schema:${tableName}`, schema);
449
+ } catch (error) {
450
+ throw new MastraError(
451
+ {
452
+ id: 'STORAGE_UPSTASH_STORAGE_CREATE_TABLE_FAILED',
453
+ domain: ErrorDomain.STORAGE,
454
+ category: ErrorCategory.THIRD_PARTY,
455
+ details: {
456
+ tableName,
457
+ },
458
+ },
459
+ error,
460
+ );
461
+ }
413
462
  }
414
463
 
415
464
  /**
@@ -428,13 +477,41 @@ export class UpstashStore extends MastraStorage {
428
477
 
429
478
  async clearTable({ tableName }: { tableName: TABLE_NAMES }): Promise<void> {
430
479
  const pattern = `${tableName}:*`;
431
- await this.scanAndDelete(pattern);
480
+ try {
481
+ await this.scanAndDelete(pattern);
482
+ } catch (error) {
483
+ throw new MastraError(
484
+ {
485
+ id: 'STORAGE_UPSTASH_STORAGE_CLEAR_TABLE_FAILED',
486
+ domain: ErrorDomain.STORAGE,
487
+ category: ErrorCategory.THIRD_PARTY,
488
+ details: {
489
+ tableName,
490
+ },
491
+ },
492
+ error,
493
+ );
494
+ }
432
495
  }
433
496
 
434
497
  async insert({ tableName, record }: { tableName: TABLE_NAMES; record: Record<string, any> }): Promise<void> {
435
498
  const { key, processedRecord } = this.processRecord(tableName, record);
436
499
 
437
- await this.redis.set(key, processedRecord);
500
+ try {
501
+ await this.redis.set(key, processedRecord);
502
+ } catch (error) {
503
+ throw new MastraError(
504
+ {
505
+ id: 'STORAGE_UPSTASH_STORAGE_INSERT_FAILED',
506
+ domain: ErrorDomain.STORAGE,
507
+ category: ErrorCategory.THIRD_PARTY,
508
+ details: {
509
+ tableName,
510
+ },
511
+ },
512
+ error,
513
+ );
514
+ }
438
515
  }
439
516
 
440
517
  async batchInsert(input: { tableName: TABLE_NAMES; records: Record<string, any>[] }): Promise<void> {
@@ -442,37 +519,79 @@ export class UpstashStore extends MastraStorage {
442
519
  if (!records.length) return;
443
520
 
444
521
  const batchSize = 1000;
445
- for (let i = 0; i < records.length; i += batchSize) {
446
- const batch = records.slice(i, i + batchSize);
447
- const pipeline = this.redis.pipeline();
448
- for (const record of batch) {
449
- const { key, processedRecord } = this.processRecord(tableName, record);
450
- pipeline.set(key, processedRecord);
522
+ try {
523
+ for (let i = 0; i < records.length; i += batchSize) {
524
+ const batch = records.slice(i, i + batchSize);
525
+ const pipeline = this.redis.pipeline();
526
+ for (const record of batch) {
527
+ const { key, processedRecord } = this.processRecord(tableName, record);
528
+ pipeline.set(key, processedRecord);
529
+ }
530
+ await pipeline.exec();
451
531
  }
452
- await pipeline.exec();
532
+ } catch (error) {
533
+ throw new MastraError(
534
+ {
535
+ id: 'STORAGE_UPSTASH_STORAGE_BATCH_INSERT_FAILED',
536
+ domain: ErrorDomain.STORAGE,
537
+ category: ErrorCategory.THIRD_PARTY,
538
+ details: {
539
+ tableName,
540
+ },
541
+ },
542
+ error,
543
+ );
453
544
  }
454
545
  }
455
546
 
456
547
  async load<R>({ tableName, keys }: { tableName: TABLE_NAMES; keys: Record<string, string> }): Promise<R | null> {
457
548
  const key = this.getKey(tableName, keys);
458
- const data = await this.redis.get<R>(key);
459
- return data || null;
549
+ try {
550
+ const data = await this.redis.get<R>(key);
551
+ return data || null;
552
+ } catch (error) {
553
+ throw new MastraError(
554
+ {
555
+ id: 'STORAGE_UPSTASH_STORAGE_LOAD_FAILED',
556
+ domain: ErrorDomain.STORAGE,
557
+ category: ErrorCategory.THIRD_PARTY,
558
+ details: {
559
+ tableName,
560
+ },
561
+ },
562
+ error,
563
+ );
564
+ }
460
565
  }
461
566
 
462
567
  async getThreadById({ threadId }: { threadId: string }): Promise<StorageThreadType | null> {
463
- const thread = await this.load<StorageThreadType>({
464
- tableName: TABLE_THREADS,
465
- keys: { id: threadId },
466
- });
568
+ try {
569
+ const thread = await this.load<StorageThreadType>({
570
+ tableName: TABLE_THREADS,
571
+ keys: { id: threadId },
572
+ });
467
573
 
468
- if (!thread) return null;
574
+ if (!thread) return null;
469
575
 
470
- return {
471
- ...thread,
472
- createdAt: this.ensureDate(thread.createdAt)!,
473
- updatedAt: this.ensureDate(thread.updatedAt)!,
474
- metadata: typeof thread.metadata === 'string' ? JSON.parse(thread.metadata) : thread.metadata,
475
- };
576
+ return {
577
+ ...thread,
578
+ createdAt: this.ensureDate(thread.createdAt)!,
579
+ updatedAt: this.ensureDate(thread.updatedAt)!,
580
+ metadata: typeof thread.metadata === 'string' ? JSON.parse(thread.metadata) : thread.metadata,
581
+ };
582
+ } catch (error) {
583
+ throw new MastraError(
584
+ {
585
+ id: 'STORAGE_UPSTASH_STORAGE_GET_THREAD_BY_ID_FAILED',
586
+ domain: ErrorDomain.STORAGE,
587
+ category: ErrorCategory.THIRD_PARTY,
588
+ details: {
589
+ threadId,
590
+ },
591
+ },
592
+ error,
593
+ );
594
+ }
476
595
  }
477
596
 
478
597
  /**
@@ -507,7 +626,19 @@ export class UpstashStore extends MastraStorage {
507
626
  allThreads.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
508
627
  return allThreads;
509
628
  } catch (error) {
510
- console.error('Error in getThreadsByResourceId:', error);
629
+ const mastraError = new MastraError(
630
+ {
631
+ id: 'STORAGE_UPSTASH_STORAGE_GET_THREADS_BY_RESOURCE_ID_FAILED',
632
+ domain: ErrorDomain.STORAGE,
633
+ category: ErrorCategory.THIRD_PARTY,
634
+ details: {
635
+ resourceId,
636
+ },
637
+ },
638
+ error,
639
+ );
640
+ this.logger?.trackException(mastraError);
641
+ this.logger.error(mastraError.toString());
511
642
  return [];
512
643
  }
513
644
  }
@@ -536,7 +667,21 @@ export class UpstashStore extends MastraStorage {
536
667
  hasMore,
537
668
  };
538
669
  } catch (error) {
539
- console.error('Error in getThreadsByResourceIdPaginated:', error);
670
+ const mastraError = new MastraError(
671
+ {
672
+ id: 'STORAGE_UPSTASH_STORAGE_GET_THREADS_BY_RESOURCE_ID_PAGINATED_FAILED',
673
+ domain: ErrorDomain.STORAGE,
674
+ category: ErrorCategory.THIRD_PARTY,
675
+ details: {
676
+ resourceId,
677
+ page,
678
+ perPage,
679
+ },
680
+ },
681
+ error,
682
+ );
683
+ this.logger?.trackException(mastraError);
684
+ this.logger.error(mastraError.toString());
540
685
  return {
541
686
  threads: [],
542
687
  total: 0,
@@ -548,11 +693,28 @@ export class UpstashStore extends MastraStorage {
548
693
  }
549
694
 
550
695
  async saveThread({ thread }: { thread: StorageThreadType }): Promise<StorageThreadType> {
551
- await this.insert({
552
- tableName: TABLE_THREADS,
553
- record: thread,
554
- });
555
- return thread;
696
+ try {
697
+ await this.insert({
698
+ tableName: TABLE_THREADS,
699
+ record: thread,
700
+ });
701
+ return thread;
702
+ } catch (error) {
703
+ const mastraError = new MastraError(
704
+ {
705
+ id: 'STORAGE_UPSTASH_STORAGE_SAVE_THREAD_FAILED',
706
+ domain: ErrorDomain.STORAGE,
707
+ category: ErrorCategory.THIRD_PARTY,
708
+ details: {
709
+ threadId: thread.id,
710
+ },
711
+ },
712
+ error,
713
+ );
714
+ this.logger?.trackException(mastraError);
715
+ this.logger.error(mastraError.toString());
716
+ throw mastraError;
717
+ }
556
718
  }
557
719
 
558
720
  async updateThread({
@@ -566,7 +728,15 @@ export class UpstashStore extends MastraStorage {
566
728
  }): Promise<StorageThreadType> {
567
729
  const thread = await this.getThreadById({ threadId: id });
568
730
  if (!thread) {
569
- throw new Error(`Thread ${id} not found`);
731
+ throw new MastraError({
732
+ id: 'STORAGE_UPSTASH_STORAGE_UPDATE_THREAD_FAILED',
733
+ domain: ErrorDomain.STORAGE,
734
+ category: ErrorCategory.USER,
735
+ text: `Thread ${id} not found`,
736
+ details: {
737
+ threadId: id,
738
+ },
739
+ });
570
740
  }
571
741
 
572
742
  const updatedThread = {
@@ -578,30 +748,58 @@ export class UpstashStore extends MastraStorage {
578
748
  },
579
749
  };
580
750
 
581
- await this.saveThread({ thread: updatedThread });
582
- return updatedThread;
751
+ try {
752
+ await this.saveThread({ thread: updatedThread });
753
+ return updatedThread;
754
+ } catch (error) {
755
+ throw new MastraError(
756
+ {
757
+ id: 'STORAGE_UPSTASH_STORAGE_UPDATE_THREAD_FAILED',
758
+ domain: ErrorDomain.STORAGE,
759
+ category: ErrorCategory.THIRD_PARTY,
760
+ details: {
761
+ threadId: id,
762
+ },
763
+ },
764
+ error,
765
+ );
766
+ }
583
767
  }
584
768
 
585
769
  async deleteThread({ threadId }: { threadId: string }): Promise<void> {
586
770
  // Delete thread metadata and sorted set
587
771
  const threadKey = this.getKey(TABLE_THREADS, { id: threadId });
588
772
  const threadMessagesKey = this.getThreadMessagesKey(threadId);
589
- const messageIds: string[] = await this.redis.zrange(threadMessagesKey, 0, -1);
773
+ try {
774
+ const messageIds: string[] = await this.redis.zrange(threadMessagesKey, 0, -1);
590
775
 
591
- const pipeline = this.redis.pipeline();
592
- pipeline.del(threadKey);
593
- pipeline.del(threadMessagesKey);
776
+ const pipeline = this.redis.pipeline();
777
+ pipeline.del(threadKey);
778
+ pipeline.del(threadMessagesKey);
594
779
 
595
- for (let i = 0; i < messageIds.length; i++) {
596
- const messageId = messageIds[i];
597
- const messageKey = this.getMessageKey(threadId, messageId as string);
598
- pipeline.del(messageKey);
599
- }
780
+ for (let i = 0; i < messageIds.length; i++) {
781
+ const messageId = messageIds[i];
782
+ const messageKey = this.getMessageKey(threadId, messageId as string);
783
+ pipeline.del(messageKey);
784
+ }
600
785
 
601
- await pipeline.exec();
786
+ await pipeline.exec();
602
787
 
603
- // Bulk delete all message keys for this thread if any remain
604
- await this.scanAndDelete(this.getMessageKey(threadId, '*'));
788
+ // Bulk delete all message keys for this thread if any remain
789
+ await this.scanAndDelete(this.getMessageKey(threadId, '*'));
790
+ } catch (error) {
791
+ throw new MastraError(
792
+ {
793
+ id: 'STORAGE_UPSTASH_STORAGE_DELETE_THREAD_FAILED',
794
+ domain: ErrorDomain.STORAGE,
795
+ category: ErrorCategory.THIRD_PARTY,
796
+ details: {
797
+ threadId,
798
+ },
799
+ },
800
+ error,
801
+ );
802
+ }
605
803
  }
606
804
 
607
805
  async saveMessages(args: { messages: MastraMessageV1[]; format?: undefined | 'v1' }): Promise<MastraMessageV1[]>;
@@ -613,14 +811,25 @@ export class UpstashStore extends MastraStorage {
613
811
  if (messages.length === 0) return [];
614
812
 
615
813
  const threadId = messages[0]?.threadId;
616
- if (!threadId) {
617
- throw new Error('Thread ID is required');
618
- }
814
+ try {
815
+ if (!threadId) {
816
+ throw new Error('Thread ID is required');
817
+ }
619
818
 
620
- // Check if thread exists
621
- const thread = await this.getThreadById({ threadId });
622
- if (!thread) {
623
- throw new Error(`Thread ${threadId} not found`);
819
+ // Check if thread exists
820
+ const thread = await this.getThreadById({ threadId });
821
+ if (!thread) {
822
+ throw new Error(`Thread ${threadId} not found`);
823
+ }
824
+ } catch (error) {
825
+ throw new MastraError(
826
+ {
827
+ id: 'STORAGE_UPSTASH_STORAGE_SAVE_MESSAGES_INVALID_ARGS',
828
+ domain: ErrorDomain.STORAGE,
829
+ category: ErrorCategory.USER,
830
+ },
831
+ error,
832
+ );
624
833
  }
625
834
 
626
835
  // Add an index to each message to maintain order
@@ -633,41 +842,55 @@ export class UpstashStore extends MastraStorage {
633
842
  const threadKey = this.getKey(TABLE_THREADS, { id: threadId });
634
843
  const existingThread = await this.redis.get<StorageThreadType>(threadKey);
635
844
 
636
- const batchSize = 1000;
637
- for (let i = 0; i < messagesWithIndex.length; i += batchSize) {
638
- const batch = messagesWithIndex.slice(i, i + batchSize);
639
- const pipeline = this.redis.pipeline();
640
-
641
- for (const message of batch) {
642
- const key = this.getMessageKey(message.threadId!, message.id);
643
- const createdAtScore = new Date(message.createdAt).getTime();
644
- const score = message._index !== undefined ? message._index : createdAtScore;
645
-
646
- // Store the message data
647
- pipeline.set(key, message);
845
+ try {
846
+ const batchSize = 1000;
847
+ for (let i = 0; i < messagesWithIndex.length; i += batchSize) {
848
+ const batch = messagesWithIndex.slice(i, i + batchSize);
849
+ const pipeline = this.redis.pipeline();
850
+
851
+ for (const message of batch) {
852
+ const key = this.getMessageKey(message.threadId!, message.id);
853
+ const createdAtScore = new Date(message.createdAt).getTime();
854
+ const score = message._index !== undefined ? message._index : createdAtScore;
855
+
856
+ // Store the message data
857
+ pipeline.set(key, message);
858
+
859
+ // Add to sorted set for this thread
860
+ pipeline.zadd(this.getThreadMessagesKey(message.threadId!), {
861
+ score,
862
+ member: message.id,
863
+ });
864
+ }
648
865
 
649
- // Add to sorted set for this thread
650
- pipeline.zadd(this.getThreadMessagesKey(message.threadId!), {
651
- score,
652
- member: message.id,
653
- });
654
- }
866
+ // Update the thread's updatedAt field (only in the first batch)
867
+ if (i === 0 && existingThread) {
868
+ const updatedThread = {
869
+ ...existingThread,
870
+ updatedAt: new Date(),
871
+ };
872
+ pipeline.set(threadKey, this.processRecord(TABLE_THREADS, updatedThread).processedRecord);
873
+ }
655
874
 
656
- // Update the thread's updatedAt field (only in the first batch)
657
- if (i === 0 && existingThread) {
658
- const updatedThread = {
659
- ...existingThread,
660
- updatedAt: new Date(),
661
- };
662
- pipeline.set(threadKey, this.processRecord(TABLE_THREADS, updatedThread).processedRecord);
875
+ await pipeline.exec();
663
876
  }
664
877
 
665
- await pipeline.exec();
878
+ const list = new MessageList().add(messages, 'memory');
879
+ if (format === `v2`) return list.get.all.v2();
880
+ return list.get.all.v1();
881
+ } catch (error) {
882
+ throw new MastraError(
883
+ {
884
+ id: 'STORAGE_UPSTASH_STORAGE_SAVE_MESSAGES_FAILED',
885
+ domain: ErrorDomain.STORAGE,
886
+ category: ErrorCategory.THIRD_PARTY,
887
+ details: {
888
+ threadId,
889
+ },
890
+ },
891
+ error,
892
+ );
666
893
  }
667
-
668
- const list = new MessageList().add(messages, 'memory');
669
- if (format === `v2`) return list.get.all.v2();
670
- return list.get.all.v1();
671
894
  }
672
895
 
673
896
  private async _getIncludedMessages(
@@ -734,89 +957,96 @@ export class UpstashStore extends MastraStorage {
734
957
  format,
735
958
  }: StorageGetMessagesArg & { format?: 'v1' | 'v2' }): Promise<MastraMessageV1[] | MastraMessageV2[]> {
736
959
  const threadMessagesKey = this.getThreadMessagesKey(threadId);
737
- const allMessageIds = await this.redis.zrange(threadMessagesKey, 0, -1);
738
- // When selectBy is undefined or selectBy.last is undefined, get ALL messages (not just 40)
739
- let limit: number;
740
- if (typeof selectBy?.last === 'number') {
741
- limit = Math.max(0, selectBy.last);
742
- } else if (selectBy?.last === false) {
743
- limit = 0;
744
- } else {
745
- // No limit specified - get all messages
746
- limit = Number.MAX_SAFE_INTEGER;
747
- }
748
-
749
- const messageIds = new Set<string>();
750
- const messageIdToThreadIds: Record<string, string> = {};
960
+ try {
961
+ const allMessageIds = await this.redis.zrange(threadMessagesKey, 0, -1);
962
+ const limit = this.resolveMessageLimit({ last: selectBy?.last, defaultLimit: Number.MAX_SAFE_INTEGER });
751
963
 
752
- if (limit === 0 && !selectBy?.include) {
753
- return [];
754
- }
964
+ const messageIds = new Set<string>();
965
+ const messageIdToThreadIds: Record<string, string> = {};
755
966
 
756
- // Then get the most recent messages (or all if no limit)
757
- if (limit === Number.MAX_SAFE_INTEGER) {
758
- // Get all messages
759
- const allIds = await this.redis.zrange(threadMessagesKey, 0, -1);
760
- allIds.forEach(id => {
761
- messageIds.add(id as string);
762
- messageIdToThreadIds[id as string] = threadId;
763
- });
764
- } else if (limit > 0) {
765
- // Get limited number of recent messages
766
- const latestIds = await this.redis.zrange(threadMessagesKey, -limit, -1);
767
- latestIds.forEach(id => {
768
- messageIds.add(id as string);
769
- messageIdToThreadIds[id as string] = threadId;
770
- });
771
- }
967
+ if (limit === 0 && !selectBy?.include) {
968
+ return [];
969
+ }
772
970
 
773
- const includedMessages = await this._getIncludedMessages(threadId, selectBy);
971
+ // Then get the most recent messages (or all if no limit)
972
+ if (limit === Number.MAX_SAFE_INTEGER) {
973
+ // Get all messages
974
+ const allIds = await this.redis.zrange(threadMessagesKey, 0, -1);
975
+ allIds.forEach(id => {
976
+ messageIds.add(id as string);
977
+ messageIdToThreadIds[id as string] = threadId;
978
+ });
979
+ } else if (limit > 0) {
980
+ // Get limited number of recent messages
981
+ const latestIds = await this.redis.zrange(threadMessagesKey, -limit, -1);
982
+ latestIds.forEach(id => {
983
+ messageIds.add(id as string);
984
+ messageIdToThreadIds[id as string] = threadId;
985
+ });
986
+ }
774
987
 
775
- // Fetch all needed messages in parallel
776
- const messages = [
777
- ...includedMessages,
778
- ...((
779
- await Promise.all(
780
- Array.from(messageIds).map(async id => {
781
- const tId = messageIdToThreadIds[id] || threadId;
782
- const byThreadId = await this.redis.get<MastraMessageV2 & { _index?: number }>(this.getMessageKey(tId, id));
783
- if (byThreadId) return byThreadId;
988
+ const includedMessages = await this._getIncludedMessages(threadId, selectBy);
989
+
990
+ // Fetch all needed messages in parallel
991
+ const messages = [
992
+ ...includedMessages,
993
+ ...((
994
+ await Promise.all(
995
+ Array.from(messageIds).map(async id => {
996
+ const tId = messageIdToThreadIds[id] || threadId;
997
+ const byThreadId = await this.redis.get<MastraMessageV2 & { _index?: number }>(
998
+ this.getMessageKey(tId, id),
999
+ );
1000
+ if (byThreadId) return byThreadId;
1001
+
1002
+ return null;
1003
+ }),
1004
+ )
1005
+ ).filter(msg => msg !== null) as (MastraMessageV2 & { _index?: number })[]),
1006
+ ];
784
1007
 
785
- return null;
786
- }),
787
- )
788
- ).filter(msg => msg !== null) as (MastraMessageV2 & { _index?: number })[]),
789
- ];
1008
+ // Sort messages by their position in the sorted set
1009
+ messages.sort((a, b) => allMessageIds.indexOf(a!.id) - allMessageIds.indexOf(b!.id));
790
1010
 
791
- // Sort messages by their position in the sorted set
792
- messages.sort((a, b) => allMessageIds.indexOf(a!.id) - allMessageIds.indexOf(b!.id));
1011
+ const seen = new Set<string>();
1012
+ const dedupedMessages = messages.filter(row => {
1013
+ if (seen.has(row.id)) return false;
1014
+ seen.add(row.id);
1015
+ return true;
1016
+ });
793
1017
 
794
- const seen = new Set<string>();
795
- const dedupedMessages = messages.filter(row => {
796
- if (seen.has(row.id)) return false;
797
- seen.add(row.id);
798
- return true;
799
- });
1018
+ // Remove _index before returning and handle format conversion properly
1019
+ const prepared = dedupedMessages
1020
+ .filter(message => message !== null && message !== undefined)
1021
+ .map(message => {
1022
+ const { _index, ...messageWithoutIndex } = message as MastraMessageV2 & { _index?: number };
1023
+ return messageWithoutIndex as unknown as MastraMessageV1;
1024
+ });
800
1025
 
801
- // Remove _index before returning and handle format conversion properly
802
- const prepared = dedupedMessages
803
- .filter(message => message !== null && message !== undefined)
804
- .map(message => {
805
- const { _index, ...messageWithoutIndex } = message as MastraMessageV2 & { _index?: number };
806
- return messageWithoutIndex as unknown as MastraMessageV1;
807
- });
1026
+ // For backward compatibility, return messages directly without using MessageList
1027
+ // since MessageList has deduplication logic that can cause issues
1028
+ if (format === 'v2') {
1029
+ // Convert V1 format back to V2 format
1030
+ return prepared.map(msg => ({
1031
+ ...msg,
1032
+ content: msg.content || { format: 2, parts: [{ type: 'text', text: '' }] },
1033
+ })) as MastraMessageV2[];
1034
+ }
808
1035
 
809
- // For backward compatibility, return messages directly without using MessageList
810
- // since MessageList has deduplication logic that can cause issues
811
- if (format === 'v2') {
812
- // Convert V1 format back to V2 format
813
- return prepared.map(msg => ({
814
- ...msg,
815
- content: msg.content || { format: 2, parts: [{ type: 'text', text: '' }] },
816
- })) as MastraMessageV2[];
1036
+ return prepared;
1037
+ } catch (error) {
1038
+ throw new MastraError(
1039
+ {
1040
+ id: 'STORAGE_UPSTASH_STORAGE_GET_MESSAGES_FAILED',
1041
+ domain: ErrorDomain.STORAGE,
1042
+ category: ErrorCategory.THIRD_PARTY,
1043
+ details: {
1044
+ threadId,
1045
+ },
1046
+ },
1047
+ error,
1048
+ );
817
1049
  }
818
-
819
- return prepared;
820
1050
  }
821
1051
 
822
1052
  public async getMessagesPaginated(
@@ -831,10 +1061,10 @@ export class UpstashStore extends MastraStorage {
831
1061
  const threadMessagesKey = this.getThreadMessagesKey(threadId);
832
1062
  const messages: (MastraMessageV2 | MastraMessageV1)[] = [];
833
1063
 
834
- const includedMessages = await this._getIncludedMessages(threadId, selectBy);
835
- messages.push(...includedMessages);
836
-
837
1064
  try {
1065
+ const includedMessages = await this._getIncludedMessages(threadId, selectBy);
1066
+ messages.push(...includedMessages);
1067
+
838
1068
  const allMessageIds = await this.redis.zrange(threadMessagesKey, 0, -1);
839
1069
  if (allMessageIds.length === 0) {
840
1070
  return {
@@ -891,7 +1121,19 @@ export class UpstashStore extends MastraStorage {
891
1121
  hasMore,
892
1122
  };
893
1123
  } catch (error) {
894
- console.error('Failed to get paginated messages:', error);
1124
+ const mastraError = new MastraError(
1125
+ {
1126
+ id: 'STORAGE_UPSTASH_STORAGE_GET_MESSAGES_PAGINATED_FAILED',
1127
+ domain: ErrorDomain.STORAGE,
1128
+ category: ErrorCategory.THIRD_PARTY,
1129
+ details: {
1130
+ threadId,
1131
+ },
1132
+ },
1133
+ error,
1134
+ );
1135
+ this.logger.error(mastraError.toString());
1136
+ this.logger?.trackException(mastraError);
895
1137
  return {
896
1138
  messages: [],
897
1139
  total: 0,
@@ -909,17 +1151,33 @@ export class UpstashStore extends MastraStorage {
909
1151
  snapshot: WorkflowRunState;
910
1152
  }): Promise<void> {
911
1153
  const { namespace = 'workflows', workflowName, runId, snapshot } = params;
912
- await this.insert({
913
- tableName: TABLE_WORKFLOW_SNAPSHOT,
914
- record: {
915
- namespace,
916
- workflow_name: workflowName,
917
- run_id: runId,
918
- snapshot,
919
- createdAt: new Date(),
920
- updatedAt: new Date(),
921
- },
922
- });
1154
+ try {
1155
+ await this.insert({
1156
+ tableName: TABLE_WORKFLOW_SNAPSHOT,
1157
+ record: {
1158
+ namespace,
1159
+ workflow_name: workflowName,
1160
+ run_id: runId,
1161
+ snapshot,
1162
+ createdAt: new Date(),
1163
+ updatedAt: new Date(),
1164
+ },
1165
+ });
1166
+ } catch (error) {
1167
+ throw new MastraError(
1168
+ {
1169
+ id: 'STORAGE_UPSTASH_STORAGE_PERSIST_WORKFLOW_SNAPSHOT_FAILED',
1170
+ domain: ErrorDomain.STORAGE,
1171
+ category: ErrorCategory.THIRD_PARTY,
1172
+ details: {
1173
+ namespace,
1174
+ workflowName,
1175
+ runId,
1176
+ },
1177
+ },
1178
+ error,
1179
+ );
1180
+ }
923
1181
  }
924
1182
 
925
1183
  async loadWorkflowSnapshot(params: {
@@ -933,14 +1191,30 @@ export class UpstashStore extends MastraStorage {
933
1191
  workflow_name: workflowName,
934
1192
  run_id: runId,
935
1193
  });
936
- const data = await this.redis.get<{
937
- namespace: string;
938
- workflow_name: string;
939
- run_id: string;
940
- snapshot: WorkflowRunState;
941
- }>(key);
942
- if (!data) return null;
943
- return data.snapshot;
1194
+ try {
1195
+ const data = await this.redis.get<{
1196
+ namespace: string;
1197
+ workflow_name: string;
1198
+ run_id: string;
1199
+ snapshot: WorkflowRunState;
1200
+ }>(key);
1201
+ if (!data) return null;
1202
+ return data.snapshot;
1203
+ } catch (error) {
1204
+ throw new MastraError(
1205
+ {
1206
+ id: 'STORAGE_UPSTASH_STORAGE_LOAD_WORKFLOW_SNAPSHOT_FAILED',
1207
+ domain: ErrorDomain.STORAGE,
1208
+ category: ErrorCategory.THIRD_PARTY,
1209
+ details: {
1210
+ namespace,
1211
+ workflowName,
1212
+ runId,
1213
+ },
1214
+ },
1215
+ error,
1216
+ );
1217
+ }
944
1218
  }
945
1219
 
946
1220
  /**
@@ -1063,7 +1337,20 @@ export class UpstashStore extends MastraStorage {
1063
1337
  };
1064
1338
  } catch (error) {
1065
1339
  const { page = 0, perPage = 100 } = options || {};
1066
- console.error('Failed to get evals:', error);
1340
+ const mastraError = new MastraError(
1341
+ {
1342
+ id: 'STORAGE_UPSTASH_STORAGE_GET_EVALS_FAILED',
1343
+ domain: ErrorDomain.STORAGE,
1344
+ category: ErrorCategory.THIRD_PARTY,
1345
+ details: {
1346
+ page,
1347
+ perPage,
1348
+ },
1349
+ },
1350
+ error,
1351
+ );
1352
+ this.logger.error(mastraError.toString());
1353
+ this.logger?.trackException(mastraError);
1067
1354
  return {
1068
1355
  evals: [],
1069
1356
  total: 0,
@@ -1146,8 +1433,19 @@ export class UpstashStore extends MastraStorage {
1146
1433
 
1147
1434
  return { runs, total };
1148
1435
  } catch (error) {
1149
- console.error('Error getting workflow runs:', error);
1150
- throw error;
1436
+ throw new MastraError(
1437
+ {
1438
+ id: 'STORAGE_UPSTASH_STORAGE_GET_WORKFLOW_RUNS_FAILED',
1439
+ domain: ErrorDomain.STORAGE,
1440
+ category: ErrorCategory.THIRD_PARTY,
1441
+ details: {
1442
+ namespace,
1443
+ workflowName: workflowName || '',
1444
+ resourceId: resourceId || '',
1445
+ },
1446
+ },
1447
+ error,
1448
+ );
1151
1449
  }
1152
1450
  }
1153
1451
 
@@ -1180,8 +1478,19 @@ export class UpstashStore extends MastraStorage {
1180
1478
  if (!data) return null;
1181
1479
  return this.parseWorkflowRun(data);
1182
1480
  } catch (error) {
1183
- console.error('Error getting workflow run by ID:', error);
1184
- throw error;
1481
+ throw new MastraError(
1482
+ {
1483
+ id: 'STORAGE_UPSTASH_STORAGE_GET_WORKFLOW_RUN_BY_ID_FAILED',
1484
+ domain: ErrorDomain.STORAGE,
1485
+ category: ErrorCategory.THIRD_PARTY,
1486
+ details: {
1487
+ namespace,
1488
+ runId,
1489
+ workflowName: workflowName || '',
1490
+ },
1491
+ },
1492
+ error,
1493
+ );
1185
1494
  }
1186
1495
  }
1187
1496