@mastra/upstash 0.11.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,6 +1,8 @@
1
1
  import { MessageList } from '@mastra/core/agent';
2
+ import type { MastraMessageContentV2, MastraMessageV2 } from '@mastra/core/agent';
3
+ import { ErrorCategory, ErrorDomain, MastraError } from '@mastra/core/error';
2
4
  import type { MetricResult, TestInfo } from '@mastra/core/eval';
3
- import type { StorageThreadType, MastraMessageV1, MastraMessageV2 } from '@mastra/core/memory';
5
+ import type { StorageThreadType, MastraMessageV1 } from '@mastra/core/memory';
4
6
  import {
5
7
  MastraStorage,
6
8
  TABLE_MESSAGES,
@@ -270,7 +272,17 @@ export class UpstashStore extends MastraStorage {
270
272
  // Transform to EvalRow format
271
273
  return filteredEvals.map(record => this.transformEvalRecord(record));
272
274
  } catch (error) {
273
- 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());
274
286
  return [];
275
287
  }
276
288
  }
@@ -285,8 +297,19 @@ export class UpstashStore extends MastraStorage {
285
297
  end: args.toDate,
286
298
  };
287
299
  }
288
- const { traces } = await this.getTracesPaginated(args);
289
- 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
+ }
290
313
  }
291
314
 
292
315
  public async getTracesPaginated(
@@ -388,7 +411,20 @@ export class UpstashStore extends MastraStorage {
388
411
  hasMore,
389
412
  };
390
413
  } catch (error) {
391
- 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());
392
428
  return {
393
429
  traces: [],
394
430
  total: 0,
@@ -408,7 +444,21 @@ export class UpstashStore extends MastraStorage {
408
444
  }): Promise<void> {
409
445
  // Redis is schemaless, so we don't need to create tables
410
446
  // But we can store the schema for reference
411
- 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
+ }
412
462
  }
413
463
 
414
464
  /**
@@ -427,13 +477,41 @@ export class UpstashStore extends MastraStorage {
427
477
 
428
478
  async clearTable({ tableName }: { tableName: TABLE_NAMES }): Promise<void> {
429
479
  const pattern = `${tableName}:*`;
430
- 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
+ }
431
495
  }
432
496
 
433
497
  async insert({ tableName, record }: { tableName: TABLE_NAMES; record: Record<string, any> }): Promise<void> {
434
498
  const { key, processedRecord } = this.processRecord(tableName, record);
435
499
 
436
- 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
+ }
437
515
  }
438
516
 
439
517
  async batchInsert(input: { tableName: TABLE_NAMES; records: Record<string, any>[] }): Promise<void> {
@@ -441,37 +519,79 @@ export class UpstashStore extends MastraStorage {
441
519
  if (!records.length) return;
442
520
 
443
521
  const batchSize = 1000;
444
- for (let i = 0; i < records.length; i += batchSize) {
445
- const batch = records.slice(i, i + batchSize);
446
- const pipeline = this.redis.pipeline();
447
- for (const record of batch) {
448
- const { key, processedRecord } = this.processRecord(tableName, record);
449
- 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();
450
531
  }
451
- 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
+ );
452
544
  }
453
545
  }
454
546
 
455
547
  async load<R>({ tableName, keys }: { tableName: TABLE_NAMES; keys: Record<string, string> }): Promise<R | null> {
456
548
  const key = this.getKey(tableName, keys);
457
- const data = await this.redis.get<R>(key);
458
- 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
+ }
459
565
  }
460
566
 
461
567
  async getThreadById({ threadId }: { threadId: string }): Promise<StorageThreadType | null> {
462
- const thread = await this.load<StorageThreadType>({
463
- tableName: TABLE_THREADS,
464
- keys: { id: threadId },
465
- });
568
+ try {
569
+ const thread = await this.load<StorageThreadType>({
570
+ tableName: TABLE_THREADS,
571
+ keys: { id: threadId },
572
+ });
466
573
 
467
- if (!thread) return null;
574
+ if (!thread) return null;
468
575
 
469
- return {
470
- ...thread,
471
- createdAt: this.ensureDate(thread.createdAt)!,
472
- updatedAt: this.ensureDate(thread.updatedAt)!,
473
- metadata: typeof thread.metadata === 'string' ? JSON.parse(thread.metadata) : thread.metadata,
474
- };
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
+ }
475
595
  }
476
596
 
477
597
  /**
@@ -506,7 +626,19 @@ export class UpstashStore extends MastraStorage {
506
626
  allThreads.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
507
627
  return allThreads;
508
628
  } catch (error) {
509
- 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());
510
642
  return [];
511
643
  }
512
644
  }
@@ -535,7 +667,21 @@ export class UpstashStore extends MastraStorage {
535
667
  hasMore,
536
668
  };
537
669
  } catch (error) {
538
- 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());
539
685
  return {
540
686
  threads: [],
541
687
  total: 0,
@@ -547,11 +693,28 @@ export class UpstashStore extends MastraStorage {
547
693
  }
548
694
 
549
695
  async saveThread({ thread }: { thread: StorageThreadType }): Promise<StorageThreadType> {
550
- await this.insert({
551
- tableName: TABLE_THREADS,
552
- record: thread,
553
- });
554
- 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
+ }
555
718
  }
556
719
 
557
720
  async updateThread({
@@ -565,7 +728,15 @@ export class UpstashStore extends MastraStorage {
565
728
  }): Promise<StorageThreadType> {
566
729
  const thread = await this.getThreadById({ threadId: id });
567
730
  if (!thread) {
568
- 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
+ });
569
740
  }
570
741
 
571
742
  const updatedThread = {
@@ -577,30 +748,58 @@ export class UpstashStore extends MastraStorage {
577
748
  },
578
749
  };
579
750
 
580
- await this.saveThread({ thread: updatedThread });
581
- 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
+ }
582
767
  }
583
768
 
584
769
  async deleteThread({ threadId }: { threadId: string }): Promise<void> {
585
770
  // Delete thread metadata and sorted set
586
771
  const threadKey = this.getKey(TABLE_THREADS, { id: threadId });
587
772
  const threadMessagesKey = this.getThreadMessagesKey(threadId);
588
- const messageIds: string[] = await this.redis.zrange(threadMessagesKey, 0, -1);
773
+ try {
774
+ const messageIds: string[] = await this.redis.zrange(threadMessagesKey, 0, -1);
589
775
 
590
- const pipeline = this.redis.pipeline();
591
- pipeline.del(threadKey);
592
- pipeline.del(threadMessagesKey);
776
+ const pipeline = this.redis.pipeline();
777
+ pipeline.del(threadKey);
778
+ pipeline.del(threadMessagesKey);
593
779
 
594
- for (let i = 0; i < messageIds.length; i++) {
595
- const messageId = messageIds[i];
596
- const messageKey = this.getMessageKey(threadId, messageId as string);
597
- pipeline.del(messageKey);
598
- }
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
+ }
599
785
 
600
- await pipeline.exec();
786
+ await pipeline.exec();
601
787
 
602
- // Bulk delete all message keys for this thread if any remain
603
- 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
+ }
604
803
  }
605
804
 
606
805
  async saveMessages(args: { messages: MastraMessageV1[]; format?: undefined | 'v1' }): Promise<MastraMessageV1[]>;
@@ -612,14 +811,25 @@ export class UpstashStore extends MastraStorage {
612
811
  if (messages.length === 0) return [];
613
812
 
614
813
  const threadId = messages[0]?.threadId;
615
- if (!threadId) {
616
- throw new Error('Thread ID is required');
617
- }
814
+ try {
815
+ if (!threadId) {
816
+ throw new Error('Thread ID is required');
817
+ }
618
818
 
619
- // Check if thread exists
620
- const thread = await this.getThreadById({ threadId });
621
- if (!thread) {
622
- 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
+ );
623
833
  }
624
834
 
625
835
  // Add an index to each message to maintain order
@@ -632,41 +842,55 @@ export class UpstashStore extends MastraStorage {
632
842
  const threadKey = this.getKey(TABLE_THREADS, { id: threadId });
633
843
  const existingThread = await this.redis.get<StorageThreadType>(threadKey);
634
844
 
635
- const batchSize = 1000;
636
- for (let i = 0; i < messagesWithIndex.length; i += batchSize) {
637
- const batch = messagesWithIndex.slice(i, i + batchSize);
638
- const pipeline = this.redis.pipeline();
639
-
640
- for (const message of batch) {
641
- const key = this.getMessageKey(message.threadId!, message.id);
642
- const createdAtScore = new Date(message.createdAt).getTime();
643
- const score = message._index !== undefined ? message._index : createdAtScore;
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
+ }
644
865
 
645
- // Store the message data
646
- pipeline.set(key, message);
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
+ }
647
874
 
648
- // Add to sorted set for this thread
649
- pipeline.zadd(this.getThreadMessagesKey(message.threadId!), {
650
- score,
651
- member: message.id,
652
- });
875
+ await pipeline.exec();
653
876
  }
654
877
 
655
- // Update the thread's updatedAt field (only in the first batch)
656
- if (i === 0 && existingThread) {
657
- const updatedThread = {
658
- ...existingThread,
659
- updatedAt: new Date(),
660
- };
661
- pipeline.set(threadKey, this.processRecord(TABLE_THREADS, updatedThread).processedRecord);
662
- }
663
-
664
- 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
+ );
665
893
  }
666
-
667
- const list = new MessageList().add(messages, 'memory');
668
- if (format === `v2`) return list.get.all.v2();
669
- return list.get.all.v1();
670
894
  }
671
895
 
672
896
  private async _getIncludedMessages(
@@ -733,89 +957,96 @@ export class UpstashStore extends MastraStorage {
733
957
  format,
734
958
  }: StorageGetMessagesArg & { format?: 'v1' | 'v2' }): Promise<MastraMessageV1[] | MastraMessageV2[]> {
735
959
  const threadMessagesKey = this.getThreadMessagesKey(threadId);
736
- const allMessageIds = await this.redis.zrange(threadMessagesKey, 0, -1);
737
- // When selectBy is undefined or selectBy.last is undefined, get ALL messages (not just 40)
738
- let limit: number;
739
- if (typeof selectBy?.last === 'number') {
740
- limit = Math.max(0, selectBy.last);
741
- } else if (selectBy?.last === false) {
742
- limit = 0;
743
- } else {
744
- // No limit specified - get all messages
745
- limit = Number.MAX_SAFE_INTEGER;
746
- }
747
-
748
- const messageIds = new Set<string>();
749
- 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 });
750
963
 
751
- if (limit === 0 && !selectBy?.include) {
752
- return [];
753
- }
964
+ const messageIds = new Set<string>();
965
+ const messageIdToThreadIds: Record<string, string> = {};
754
966
 
755
- // Then get the most recent messages (or all if no limit)
756
- if (limit === Number.MAX_SAFE_INTEGER) {
757
- // Get all messages
758
- const allIds = await this.redis.zrange(threadMessagesKey, 0, -1);
759
- allIds.forEach(id => {
760
- messageIds.add(id as string);
761
- messageIdToThreadIds[id as string] = threadId;
762
- });
763
- } else if (limit > 0) {
764
- // Get limited number of recent messages
765
- const latestIds = await this.redis.zrange(threadMessagesKey, -limit, -1);
766
- latestIds.forEach(id => {
767
- messageIds.add(id as string);
768
- messageIdToThreadIds[id as string] = threadId;
769
- });
770
- }
967
+ if (limit === 0 && !selectBy?.include) {
968
+ return [];
969
+ }
771
970
 
772
- 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
+ }
773
987
 
774
- // Fetch all needed messages in parallel
775
- const messages = [
776
- ...includedMessages,
777
- ...((
778
- await Promise.all(
779
- Array.from(messageIds).map(async id => {
780
- const tId = messageIdToThreadIds[id] || threadId;
781
- const byThreadId = await this.redis.get<MastraMessageV2 & { _index?: number }>(this.getMessageKey(tId, id));
782
- 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
+ ];
783
1007
 
784
- return null;
785
- }),
786
- )
787
- ).filter(msg => msg !== null) as (MastraMessageV2 & { _index?: number })[]),
788
- ];
1008
+ // Sort messages by their position in the sorted set
1009
+ messages.sort((a, b) => allMessageIds.indexOf(a!.id) - allMessageIds.indexOf(b!.id));
789
1010
 
790
- // Sort messages by their position in the sorted set
791
- 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
+ });
792
1017
 
793
- const seen = new Set<string>();
794
- const dedupedMessages = messages.filter(row => {
795
- if (seen.has(row.id)) return false;
796
- seen.add(row.id);
797
- return true;
798
- });
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
+ });
799
1025
 
800
- // Remove _index before returning and handle format conversion properly
801
- const prepared = dedupedMessages
802
- .filter(message => message !== null && message !== undefined)
803
- .map(message => {
804
- const { _index, ...messageWithoutIndex } = message as MastraMessageV2 & { _index?: number };
805
- return messageWithoutIndex as unknown as MastraMessageV1;
806
- });
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
+ }
807
1035
 
808
- // For backward compatibility, return messages directly without using MessageList
809
- // since MessageList has deduplication logic that can cause issues
810
- if (format === 'v2') {
811
- // Convert V1 format back to V2 format
812
- return prepared.map(msg => ({
813
- ...msg,
814
- content: msg.content || { format: 2, parts: [{ type: 'text', text: '' }] },
815
- })) 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
+ );
816
1049
  }
817
-
818
- return prepared;
819
1050
  }
820
1051
 
821
1052
  public async getMessagesPaginated(
@@ -830,10 +1061,10 @@ export class UpstashStore extends MastraStorage {
830
1061
  const threadMessagesKey = this.getThreadMessagesKey(threadId);
831
1062
  const messages: (MastraMessageV2 | MastraMessageV1)[] = [];
832
1063
 
833
- const includedMessages = await this._getIncludedMessages(threadId, selectBy);
834
- messages.push(...includedMessages);
835
-
836
1064
  try {
1065
+ const includedMessages = await this._getIncludedMessages(threadId, selectBy);
1066
+ messages.push(...includedMessages);
1067
+
837
1068
  const allMessageIds = await this.redis.zrange(threadMessagesKey, 0, -1);
838
1069
  if (allMessageIds.length === 0) {
839
1070
  return {
@@ -890,7 +1121,19 @@ export class UpstashStore extends MastraStorage {
890
1121
  hasMore,
891
1122
  };
892
1123
  } catch (error) {
893
- 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);
894
1137
  return {
895
1138
  messages: [],
896
1139
  total: 0,
@@ -908,17 +1151,33 @@ export class UpstashStore extends MastraStorage {
908
1151
  snapshot: WorkflowRunState;
909
1152
  }): Promise<void> {
910
1153
  const { namespace = 'workflows', workflowName, runId, snapshot } = params;
911
- await this.insert({
912
- tableName: TABLE_WORKFLOW_SNAPSHOT,
913
- record: {
914
- namespace,
915
- workflow_name: workflowName,
916
- run_id: runId,
917
- snapshot,
918
- createdAt: new Date(),
919
- updatedAt: new Date(),
920
- },
921
- });
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
+ }
922
1181
  }
923
1182
 
924
1183
  async loadWorkflowSnapshot(params: {
@@ -932,14 +1191,30 @@ export class UpstashStore extends MastraStorage {
932
1191
  workflow_name: workflowName,
933
1192
  run_id: runId,
934
1193
  });
935
- const data = await this.redis.get<{
936
- namespace: string;
937
- workflow_name: string;
938
- run_id: string;
939
- snapshot: WorkflowRunState;
940
- }>(key);
941
- if (!data) return null;
942
- 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
+ }
943
1218
  }
944
1219
 
945
1220
  /**
@@ -1062,7 +1337,20 @@ export class UpstashStore extends MastraStorage {
1062
1337
  };
1063
1338
  } catch (error) {
1064
1339
  const { page = 0, perPage = 100 } = options || {};
1065
- 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);
1066
1354
  return {
1067
1355
  evals: [],
1068
1356
  total: 0,
@@ -1145,8 +1433,19 @@ export class UpstashStore extends MastraStorage {
1145
1433
 
1146
1434
  return { runs, total };
1147
1435
  } catch (error) {
1148
- console.error('Error getting workflow runs:', error);
1149
- 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
+ );
1150
1449
  }
1151
1450
  }
1152
1451
 
@@ -1179,12 +1478,34 @@ export class UpstashStore extends MastraStorage {
1179
1478
  if (!data) return null;
1180
1479
  return this.parseWorkflowRun(data);
1181
1480
  } catch (error) {
1182
- console.error('Error getting workflow run by ID:', error);
1183
- 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
+ );
1184
1494
  }
1185
1495
  }
1186
1496
 
1187
1497
  async close(): Promise<void> {
1188
1498
  // No explicit cleanup needed for Upstash Redis
1189
1499
  }
1500
+
1501
+ async updateMessages(_args: {
1502
+ messages: Partial<Omit<MastraMessageV2, 'createdAt'>> &
1503
+ {
1504
+ id: string;
1505
+ content?: { metadata?: MastraMessageContentV2['metadata']; content?: MastraMessageContentV2['content'] };
1506
+ }[];
1507
+ }): Promise<MastraMessageV2[]> {
1508
+ this.logger.error('updateMessages is not yet implemented in UpstashStore');
1509
+ throw new Error('Method not implemented');
1510
+ }
1190
1511
  }