@mastra/dynamodb 0.0.0-pass-headers-for-create-mastra-client-20250530010057 → 0.0.0-playground-studio-cloud-20251031080052

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 (51) hide show
  1. package/CHANGELOG.md +1074 -0
  2. package/LICENSE.md +11 -42
  3. package/README.md +0 -4
  4. package/dist/entities/eval.d.ts +102 -0
  5. package/dist/entities/eval.d.ts.map +1 -0
  6. package/dist/entities/index.d.ts +761 -0
  7. package/dist/entities/index.d.ts.map +1 -0
  8. package/dist/entities/message.d.ts +100 -0
  9. package/dist/entities/message.d.ts.map +1 -0
  10. package/dist/entities/resource.d.ts +54 -0
  11. package/dist/entities/resource.d.ts.map +1 -0
  12. package/dist/entities/score.d.ts +244 -0
  13. package/dist/entities/score.d.ts.map +1 -0
  14. package/dist/entities/thread.d.ts +69 -0
  15. package/dist/entities/thread.d.ts.map +1 -0
  16. package/dist/entities/trace.d.ts +127 -0
  17. package/dist/entities/trace.d.ts.map +1 -0
  18. package/dist/entities/utils.d.ts +21 -0
  19. package/dist/entities/utils.d.ts.map +1 -0
  20. package/dist/entities/workflow-snapshot.d.ts +74 -0
  21. package/dist/entities/workflow-snapshot.d.ts.map +1 -0
  22. package/dist/index.cjs +2182 -508
  23. package/dist/index.cjs.map +1 -0
  24. package/dist/index.d.ts +2 -0
  25. package/dist/index.d.ts.map +1 -0
  26. package/dist/index.js +2142 -468
  27. package/dist/index.js.map +1 -0
  28. package/dist/storage/domains/legacy-evals/index.d.ts +19 -0
  29. package/dist/storage/domains/legacy-evals/index.d.ts.map +1 -0
  30. package/dist/storage/domains/memory/index.d.ts +89 -0
  31. package/dist/storage/domains/memory/index.d.ts.map +1 -0
  32. package/dist/storage/domains/operations/index.d.ts +69 -0
  33. package/dist/storage/domains/operations/index.d.ts.map +1 -0
  34. package/dist/storage/domains/score/index.d.ts +51 -0
  35. package/dist/storage/domains/score/index.d.ts.map +1 -0
  36. package/dist/storage/domains/workflows/index.d.ts +51 -0
  37. package/dist/storage/domains/workflows/index.d.ts.map +1 -0
  38. package/dist/storage/index.d.ts +244 -0
  39. package/dist/storage/index.d.ts.map +1 -0
  40. package/package.json +43 -19
  41. package/src/entities/eval.ts +0 -102
  42. package/src/entities/index.ts +0 -23
  43. package/src/entities/message.ts +0 -143
  44. package/src/entities/thread.ts +0 -66
  45. package/src/entities/trace.ts +0 -129
  46. package/src/entities/utils.ts +0 -51
  47. package/src/entities/workflow-snapshot.ts +0 -56
  48. package/src/index.ts +0 -1
  49. package/src/storage/docker-compose.yml +0 -16
  50. package/src/storage/index.test.ts +0 -1053
  51. package/src/storage/index.ts +0 -1059
package/dist/index.js CHANGED
@@ -1,8 +1,10 @@
1
1
  import { DynamoDBClient, DescribeTableCommand } from '@aws-sdk/client-dynamodb';
2
2
  import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb';
3
- import { MessageList } from '@mastra/core/agent';
4
- import { MastraStorage, TABLE_TRACES, TABLE_EVALS, TABLE_WORKFLOW_SNAPSHOT, TABLE_MESSAGES, TABLE_THREADS } from '@mastra/core/storage';
3
+ import { MastraError, ErrorCategory, ErrorDomain } from '@mastra/core/error';
4
+ import { MastraStorage, StoreOperations, WorkflowsStorage, MemoryStorage, resolveMessageLimit, ScoresStorage, LegacyEvalsStorage, TABLE_AI_SPANS, TABLE_RESOURCES, TABLE_TRACES, TABLE_SCORERS, TABLE_EVALS, TABLE_WORKFLOW_SNAPSHOT, TABLE_MESSAGES, TABLE_THREADS } from '@mastra/core/storage';
5
5
  import { Entity, Service } from 'electrodb';
6
+ import { MessageList } from '@mastra/core/agent';
7
+ import { saveScorePayloadSchema } from '@mastra/core/scores';
6
8
 
7
9
  // src/storage/index.ts
8
10
 
@@ -293,9 +295,9 @@ var messageEntity = new Entity({
293
295
  }
294
296
  }
295
297
  });
296
- var threadEntity = new Entity({
298
+ var resourceEntity = new Entity({
297
299
  model: {
298
- entity: "thread",
300
+ entity: "resource",
299
301
  version: "1",
300
302
  service: "mastra"
301
303
  },
@@ -309,25 +311,21 @@ var threadEntity = new Entity({
309
311
  type: "string",
310
312
  required: true
311
313
  },
312
- resourceId: {
313
- type: "string",
314
- required: true
315
- },
316
- title: {
314
+ workingMemory: {
317
315
  type: "string",
318
- required: true
316
+ required: false
319
317
  },
320
318
  metadata: {
321
319
  type: "string",
322
320
  required: false,
323
- // Stringify metadata object on set if it's not already a string
321
+ // Stringify content object on set if it's not already a string
324
322
  set: (value) => {
325
323
  if (value && typeof value !== "string") {
326
324
  return JSON.stringify(value);
327
325
  }
328
326
  return value;
329
327
  },
330
- // Parse JSON string to object on get
328
+ // Parse JSON string to object on get ONLY if it looks like JSON
331
329
  get: (value) => {
332
330
  if (value && typeof value === "string") {
333
331
  try {
@@ -345,18 +343,13 @@ var threadEntity = new Entity({
345
343
  indexes: {
346
344
  primary: {
347
345
  pk: { field: "pk", composite: ["entity", "id"] },
348
- sk: { field: "sk", composite: ["id"] }
349
- },
350
- byResource: {
351
- index: "gsi1",
352
- pk: { field: "gsi1pk", composite: ["entity", "resourceId"] },
353
- sk: { field: "gsi1sk", composite: ["createdAt"] }
346
+ sk: { field: "sk", composite: ["entity"] }
354
347
  }
355
348
  }
356
349
  });
357
- var traceEntity = new Entity({
350
+ var scoreEntity = new Entity({
358
351
  model: {
359
- entity: "trace",
352
+ entity: "score",
360
353
  version: "1",
361
354
  service: "mastra"
362
355
  },
@@ -370,123 +363,315 @@ var traceEntity = new Entity({
370
363
  type: "string",
371
364
  required: true
372
365
  },
373
- parentSpanId: {
366
+ scorerId: {
367
+ type: "string",
368
+ required: true
369
+ },
370
+ traceId: {
374
371
  type: "string",
375
372
  required: false
376
373
  },
377
- name: {
374
+ spanId: {
378
375
  type: "string",
379
- required: true
376
+ required: false
380
377
  },
381
- traceId: {
378
+ runId: {
382
379
  type: "string",
383
380
  required: true
384
381
  },
385
- scope: {
382
+ scorer: {
386
383
  type: "string",
387
- required: true
384
+ required: true,
385
+ set: (value) => {
386
+ if (value && typeof value !== "string") {
387
+ return JSON.stringify(value);
388
+ }
389
+ return value;
390
+ },
391
+ get: (value) => {
392
+ if (value && typeof value === "string") {
393
+ try {
394
+ if (value.startsWith("{") || value.startsWith("[")) {
395
+ return JSON.parse(value);
396
+ }
397
+ } catch {
398
+ return value;
399
+ }
400
+ }
401
+ return value;
402
+ }
388
403
  },
389
- kind: {
404
+ extractStepResult: {
405
+ type: "string",
406
+ required: false,
407
+ set: (value) => {
408
+ if (value && typeof value !== "string") {
409
+ return JSON.stringify(value);
410
+ }
411
+ return value;
412
+ },
413
+ get: (value) => {
414
+ if (value && typeof value === "string") {
415
+ try {
416
+ if (value.startsWith("{") || value.startsWith("[")) {
417
+ return JSON.parse(value);
418
+ }
419
+ } catch {
420
+ return value;
421
+ }
422
+ }
423
+ return value;
424
+ }
425
+ },
426
+ preprocessStepResult: {
427
+ type: "string",
428
+ required: false,
429
+ set: (value) => {
430
+ if (value && typeof value !== "string") {
431
+ return JSON.stringify(value);
432
+ }
433
+ return value;
434
+ },
435
+ get: (value) => {
436
+ if (value && typeof value === "string") {
437
+ try {
438
+ if (value.startsWith("{") || value.startsWith("[")) {
439
+ return JSON.parse(value);
440
+ }
441
+ } catch {
442
+ return value;
443
+ }
444
+ }
445
+ return value;
446
+ }
447
+ },
448
+ analyzeStepResult: {
449
+ type: "string",
450
+ required: false,
451
+ set: (value) => {
452
+ if (value && typeof value !== "string") {
453
+ return JSON.stringify(value);
454
+ }
455
+ return value;
456
+ },
457
+ get: (value) => {
458
+ if (value && typeof value === "string") {
459
+ try {
460
+ if (value.startsWith("{") || value.startsWith("[")) {
461
+ return JSON.parse(value);
462
+ }
463
+ } catch {
464
+ return value;
465
+ }
466
+ }
467
+ return value;
468
+ }
469
+ },
470
+ score: {
390
471
  type: "number",
391
472
  required: true
392
473
  },
393
- attributes: {
474
+ reason: {
394
475
  type: "string",
395
- // JSON stringified
396
- required: false,
397
- // Stringify object on set
476
+ required: false
477
+ },
478
+ extractPrompt: {
479
+ type: "string",
480
+ required: false
481
+ },
482
+ analyzePrompt: {
483
+ type: "string",
484
+ required: false
485
+ },
486
+ // Deprecated in favor of generateReasonPrompt
487
+ reasonPrompt: {
488
+ type: "string",
489
+ required: false
490
+ },
491
+ generateScorePrompt: {
492
+ type: "string",
493
+ required: false
494
+ },
495
+ generateReasonPrompt: {
496
+ type: "string",
497
+ required: false
498
+ },
499
+ input: {
500
+ type: "string",
501
+ required: true,
398
502
  set: (value) => {
399
503
  if (value && typeof value !== "string") {
400
504
  return JSON.stringify(value);
401
505
  }
402
506
  return value;
403
507
  },
404
- // Parse JSON string to object on get
405
508
  get: (value) => {
406
- return value ? JSON.parse(value) : value;
509
+ if (value && typeof value === "string") {
510
+ try {
511
+ if (value.startsWith("{") || value.startsWith("[")) {
512
+ return JSON.parse(value);
513
+ }
514
+ } catch {
515
+ return value;
516
+ }
517
+ }
518
+ return value;
407
519
  }
408
520
  },
409
- status: {
521
+ output: {
522
+ type: "string",
523
+ required: true,
524
+ set: (value) => {
525
+ if (value && typeof value !== "string") {
526
+ return JSON.stringify(value);
527
+ }
528
+ return value;
529
+ },
530
+ get: (value) => {
531
+ if (value && typeof value === "string") {
532
+ try {
533
+ if (value.startsWith("{") || value.startsWith("[")) {
534
+ return JSON.parse(value);
535
+ }
536
+ } catch {
537
+ return value;
538
+ }
539
+ }
540
+ return value;
541
+ }
542
+ },
543
+ additionalContext: {
410
544
  type: "string",
411
- // JSON stringified
412
545
  required: false,
413
- // Stringify object on set
414
546
  set: (value) => {
415
547
  if (value && typeof value !== "string") {
416
548
  return JSON.stringify(value);
417
549
  }
418
550
  return value;
419
551
  },
420
- // Parse JSON string to object on get
421
552
  get: (value) => {
553
+ if (value && typeof value === "string") {
554
+ try {
555
+ if (value.startsWith("{") || value.startsWith("[")) {
556
+ return JSON.parse(value);
557
+ }
558
+ } catch {
559
+ return value;
560
+ }
561
+ }
422
562
  return value;
423
563
  }
424
564
  },
425
- events: {
565
+ runtimeContext: {
426
566
  type: "string",
427
- // JSON stringified
428
567
  required: false,
429
- // Stringify object on set
430
568
  set: (value) => {
431
569
  if (value && typeof value !== "string") {
432
570
  return JSON.stringify(value);
433
571
  }
434
572
  return value;
435
573
  },
436
- // Parse JSON string to object on get
437
574
  get: (value) => {
575
+ if (value && typeof value === "string") {
576
+ try {
577
+ if (value.startsWith("{") || value.startsWith("[")) {
578
+ return JSON.parse(value);
579
+ }
580
+ } catch {
581
+ return value;
582
+ }
583
+ }
438
584
  return value;
439
585
  }
440
586
  },
441
- links: {
587
+ entityType: {
588
+ type: "string",
589
+ required: false
590
+ },
591
+ entityData: {
442
592
  type: "string",
443
- // JSON stringified
444
593
  required: false,
445
- // Stringify object on set
446
594
  set: (value) => {
447
595
  if (value && typeof value !== "string") {
448
596
  return JSON.stringify(value);
449
597
  }
450
598
  return value;
451
599
  },
452
- // Parse JSON string to object on get
453
600
  get: (value) => {
601
+ if (value && typeof value === "string") {
602
+ try {
603
+ if (value.startsWith("{") || value.startsWith("[")) {
604
+ return JSON.parse(value);
605
+ }
606
+ } catch {
607
+ return value;
608
+ }
609
+ }
454
610
  return value;
455
611
  }
456
612
  },
457
- other: {
613
+ entityId: {
458
614
  type: "string",
459
615
  required: false
460
616
  },
461
- startTime: {
462
- type: "number",
617
+ source: {
618
+ type: "string",
463
619
  required: true
464
620
  },
465
- endTime: {
466
- type: "number",
467
- required: true
621
+ resourceId: {
622
+ type: "string",
623
+ required: false
624
+ },
625
+ threadId: {
626
+ type: "string",
627
+ required: false
468
628
  }
469
629
  },
470
630
  indexes: {
471
631
  primary: {
472
632
  pk: { field: "pk", composite: ["entity", "id"] },
473
- sk: { field: "sk", composite: [] }
633
+ sk: { field: "sk", composite: ["entity"] }
474
634
  },
475
- byName: {
635
+ byScorer: {
476
636
  index: "gsi1",
477
- pk: { field: "gsi1pk", composite: ["entity", "name"] },
478
- sk: { field: "gsi1sk", composite: ["startTime"] }
637
+ pk: { field: "gsi1pk", composite: ["entity", "scorerId"] },
638
+ sk: { field: "gsi1sk", composite: ["createdAt"] }
479
639
  },
480
- byScope: {
640
+ byRun: {
481
641
  index: "gsi2",
482
- pk: { field: "gsi2pk", composite: ["entity", "scope"] },
483
- sk: { field: "gsi2sk", composite: ["startTime"] }
642
+ pk: { field: "gsi2pk", composite: ["entity", "runId"] },
643
+ sk: { field: "gsi2sk", composite: ["createdAt"] }
644
+ },
645
+ byTrace: {
646
+ index: "gsi3",
647
+ pk: { field: "gsi3pk", composite: ["entity", "traceId"] },
648
+ sk: { field: "gsi3sk", composite: ["createdAt"] }
649
+ },
650
+ byEntityData: {
651
+ index: "gsi4",
652
+ pk: { field: "gsi4pk", composite: ["entity", "entityId"] },
653
+ sk: { field: "gsi4sk", composite: ["createdAt"] }
654
+ },
655
+ byResource: {
656
+ index: "gsi5",
657
+ pk: { field: "gsi5pk", composite: ["entity", "resourceId"] },
658
+ sk: { field: "gsi5sk", composite: ["createdAt"] }
659
+ },
660
+ byThread: {
661
+ index: "gsi6",
662
+ pk: { field: "gsi6pk", composite: ["entity", "threadId"] },
663
+ sk: { field: "gsi6sk", composite: ["createdAt"] }
664
+ },
665
+ bySpan: {
666
+ index: "gsi7",
667
+ pk: { field: "gsi7pk", composite: ["entity", "traceId", "spanId"] },
668
+ sk: { field: "gsi7sk", composite: ["createdAt"] }
484
669
  }
485
670
  }
486
671
  });
487
- var workflowSnapshotEntity = new Entity({
672
+ var threadEntity = new Entity({
488
673
  model: {
489
- entity: "workflow_snapshot",
674
+ entity: "thread",
490
675
  version: "1",
491
676
  service: "mastra"
492
677
  },
@@ -496,11 +681,202 @@ var workflowSnapshotEntity = new Entity({
496
681
  required: true
497
682
  },
498
683
  ...baseAttributes,
499
- workflow_name: {
684
+ id: {
500
685
  type: "string",
501
686
  required: true
502
687
  },
503
- run_id: {
688
+ resourceId: {
689
+ type: "string",
690
+ required: true
691
+ },
692
+ title: {
693
+ type: "string",
694
+ required: true
695
+ },
696
+ metadata: {
697
+ type: "string",
698
+ required: false,
699
+ // Stringify metadata object on set if it's not already a string
700
+ set: (value) => {
701
+ if (value && typeof value !== "string") {
702
+ return JSON.stringify(value);
703
+ }
704
+ return value;
705
+ },
706
+ // Parse JSON string to object on get
707
+ get: (value) => {
708
+ if (value && typeof value === "string") {
709
+ try {
710
+ if (value.startsWith("{") || value.startsWith("[")) {
711
+ return JSON.parse(value);
712
+ }
713
+ } catch {
714
+ return value;
715
+ }
716
+ }
717
+ return value;
718
+ }
719
+ }
720
+ },
721
+ indexes: {
722
+ primary: {
723
+ pk: { field: "pk", composite: ["entity", "id"] },
724
+ sk: { field: "sk", composite: ["id"] }
725
+ },
726
+ byResource: {
727
+ index: "gsi1",
728
+ pk: { field: "gsi1pk", composite: ["entity", "resourceId"] },
729
+ sk: { field: "gsi1sk", composite: ["createdAt"] }
730
+ }
731
+ }
732
+ });
733
+ var traceEntity = new Entity({
734
+ model: {
735
+ entity: "trace",
736
+ version: "1",
737
+ service: "mastra"
738
+ },
739
+ attributes: {
740
+ entity: {
741
+ type: "string",
742
+ required: true
743
+ },
744
+ ...baseAttributes,
745
+ id: {
746
+ type: "string",
747
+ required: true
748
+ },
749
+ parentSpanId: {
750
+ type: "string",
751
+ required: false
752
+ },
753
+ name: {
754
+ type: "string",
755
+ required: true
756
+ },
757
+ traceId: {
758
+ type: "string",
759
+ required: true
760
+ },
761
+ scope: {
762
+ type: "string",
763
+ required: true
764
+ },
765
+ kind: {
766
+ type: "number",
767
+ required: true
768
+ },
769
+ attributes: {
770
+ type: "string",
771
+ // JSON stringified
772
+ required: false,
773
+ // Stringify object on set
774
+ set: (value) => {
775
+ if (value && typeof value !== "string") {
776
+ return JSON.stringify(value);
777
+ }
778
+ return value;
779
+ },
780
+ // Parse JSON string to object on get
781
+ get: (value) => {
782
+ return value ? JSON.parse(value) : value;
783
+ }
784
+ },
785
+ status: {
786
+ type: "string",
787
+ // JSON stringified
788
+ required: false,
789
+ // Stringify object on set
790
+ set: (value) => {
791
+ if (value && typeof value !== "string") {
792
+ return JSON.stringify(value);
793
+ }
794
+ return value;
795
+ },
796
+ // Parse JSON string to object on get
797
+ get: (value) => {
798
+ return value;
799
+ }
800
+ },
801
+ events: {
802
+ type: "string",
803
+ // JSON stringified
804
+ required: false,
805
+ // Stringify object on set
806
+ set: (value) => {
807
+ if (value && typeof value !== "string") {
808
+ return JSON.stringify(value);
809
+ }
810
+ return value;
811
+ },
812
+ // Parse JSON string to object on get
813
+ get: (value) => {
814
+ return value;
815
+ }
816
+ },
817
+ links: {
818
+ type: "string",
819
+ // JSON stringified
820
+ required: false,
821
+ // Stringify object on set
822
+ set: (value) => {
823
+ if (value && typeof value !== "string") {
824
+ return JSON.stringify(value);
825
+ }
826
+ return value;
827
+ },
828
+ // Parse JSON string to object on get
829
+ get: (value) => {
830
+ return value;
831
+ }
832
+ },
833
+ other: {
834
+ type: "string",
835
+ required: false
836
+ },
837
+ startTime: {
838
+ type: "number",
839
+ required: true
840
+ },
841
+ endTime: {
842
+ type: "number",
843
+ required: true
844
+ }
845
+ },
846
+ indexes: {
847
+ primary: {
848
+ pk: { field: "pk", composite: ["entity", "id"] },
849
+ sk: { field: "sk", composite: [] }
850
+ },
851
+ byName: {
852
+ index: "gsi1",
853
+ pk: { field: "gsi1pk", composite: ["entity", "name"] },
854
+ sk: { field: "gsi1sk", composite: ["startTime"] }
855
+ },
856
+ byScope: {
857
+ index: "gsi2",
858
+ pk: { field: "gsi2pk", composite: ["entity", "scope"] },
859
+ sk: { field: "gsi2sk", composite: ["startTime"] }
860
+ }
861
+ }
862
+ });
863
+ var workflowSnapshotEntity = new Entity({
864
+ model: {
865
+ entity: "workflow_snapshot",
866
+ version: "1",
867
+ service: "mastra"
868
+ },
869
+ attributes: {
870
+ entity: {
871
+ type: "string",
872
+ required: true
873
+ },
874
+ ...baseAttributes,
875
+ workflow_name: {
876
+ type: "string",
877
+ required: true
878
+ },
879
+ run_id: {
504
880
  type: "string",
505
881
  required: true
506
882
  },
@@ -537,46 +913,1022 @@ var workflowSnapshotEntity = new Entity({
537
913
  sk: { field: "gsi2sk", composite: ["workflow_name"] }
538
914
  }
539
915
  }
540
- });
541
-
542
- // src/entities/index.ts
543
- function getElectroDbService(client, tableName) {
544
- return new Service(
545
- {
546
- thread: threadEntity,
547
- message: messageEntity,
548
- eval: evalEntity,
549
- trace: traceEntity,
550
- workflowSnapshot: workflowSnapshotEntity
551
- },
552
- {
553
- client,
554
- table: tableName
916
+ });
917
+
918
+ // src/entities/index.ts
919
+ function getElectroDbService(client, tableName) {
920
+ return new Service(
921
+ {
922
+ thread: threadEntity,
923
+ message: messageEntity,
924
+ eval: evalEntity,
925
+ trace: traceEntity,
926
+ workflow_snapshot: workflowSnapshotEntity,
927
+ resource: resourceEntity,
928
+ score: scoreEntity
929
+ },
930
+ {
931
+ client,
932
+ table: tableName
933
+ }
934
+ );
935
+ }
936
+ var LegacyEvalsDynamoDB = class extends LegacyEvalsStorage {
937
+ service;
938
+ tableName;
939
+ constructor({ service, tableName }) {
940
+ super();
941
+ this.service = service;
942
+ this.tableName = tableName;
943
+ }
944
+ // Eval operations
945
+ async getEvalsByAgentName(agentName, type) {
946
+ this.logger.debug("Getting evals for agent", { agentName, type });
947
+ try {
948
+ const query = this.service.entities.eval.query.byAgent({ entity: "eval", agent_name: agentName });
949
+ const results = await query.go({ order: "desc", limit: 100 });
950
+ if (!results.data.length) {
951
+ return [];
952
+ }
953
+ let filteredData = results.data;
954
+ if (type) {
955
+ filteredData = filteredData.filter((evalRecord) => {
956
+ try {
957
+ const testInfo = evalRecord.test_info && typeof evalRecord.test_info === "string" ? JSON.parse(evalRecord.test_info) : void 0;
958
+ if (type === "test" && !testInfo) {
959
+ return false;
960
+ }
961
+ if (type === "live" && testInfo) {
962
+ return false;
963
+ }
964
+ } catch (e) {
965
+ this.logger.warn("Failed to parse test_info during filtering", { record: evalRecord, error: e });
966
+ }
967
+ return true;
968
+ });
969
+ }
970
+ return filteredData.map((evalRecord) => {
971
+ try {
972
+ return {
973
+ input: evalRecord.input,
974
+ output: evalRecord.output,
975
+ // Safely parse result and test_info
976
+ result: evalRecord.result && typeof evalRecord.result === "string" ? JSON.parse(evalRecord.result) : void 0,
977
+ agentName: evalRecord.agent_name,
978
+ createdAt: evalRecord.created_at,
979
+ // Keep as string from DDB?
980
+ metricName: evalRecord.metric_name,
981
+ instructions: evalRecord.instructions,
982
+ runId: evalRecord.run_id,
983
+ globalRunId: evalRecord.global_run_id,
984
+ testInfo: evalRecord.test_info && typeof evalRecord.test_info === "string" ? JSON.parse(evalRecord.test_info) : void 0
985
+ };
986
+ } catch (parseError) {
987
+ this.logger.error("Failed to parse eval record", { record: evalRecord, error: parseError });
988
+ return {
989
+ agentName: evalRecord.agent_name,
990
+ createdAt: evalRecord.created_at,
991
+ runId: evalRecord.run_id,
992
+ globalRunId: evalRecord.global_run_id
993
+ };
994
+ }
995
+ });
996
+ } catch (error) {
997
+ throw new MastraError(
998
+ {
999
+ id: "STORAGE_DYNAMODB_STORE_GET_EVALS_BY_AGENT_NAME_FAILED",
1000
+ domain: ErrorDomain.STORAGE,
1001
+ category: ErrorCategory.THIRD_PARTY,
1002
+ details: { agentName }
1003
+ },
1004
+ error
1005
+ );
1006
+ }
1007
+ }
1008
+ async getEvals(options = {}) {
1009
+ const { agentName, type, page = 0, perPage = 100, dateRange } = options;
1010
+ this.logger.debug("Getting evals with pagination", { agentName, type, page, perPage, dateRange });
1011
+ try {
1012
+ let query;
1013
+ if (agentName) {
1014
+ query = this.service.entities.eval.query.byAgent({ entity: "eval", agent_name: agentName });
1015
+ } else {
1016
+ query = this.service.entities.eval.query.byEntity({ entity: "eval" });
1017
+ }
1018
+ const results = await query.go({
1019
+ order: "desc",
1020
+ pages: "all"
1021
+ // Get all pages to apply filtering and pagination
1022
+ });
1023
+ if (!results.data.length) {
1024
+ return {
1025
+ evals: [],
1026
+ total: 0,
1027
+ page,
1028
+ perPage,
1029
+ hasMore: false
1030
+ };
1031
+ }
1032
+ let filteredData = results.data;
1033
+ if (type) {
1034
+ filteredData = filteredData.filter((evalRecord) => {
1035
+ try {
1036
+ const testInfo = evalRecord.test_info && typeof evalRecord.test_info === "string" ? JSON.parse(evalRecord.test_info) : void 0;
1037
+ if (type === "test" && !testInfo) {
1038
+ return false;
1039
+ }
1040
+ if (type === "live" && testInfo) {
1041
+ return false;
1042
+ }
1043
+ } catch (e) {
1044
+ this.logger.warn("Failed to parse test_info during filtering", { record: evalRecord, error: e });
1045
+ }
1046
+ return true;
1047
+ });
1048
+ }
1049
+ if (dateRange) {
1050
+ const fromDate = dateRange.start;
1051
+ const toDate = dateRange.end;
1052
+ filteredData = filteredData.filter((evalRecord) => {
1053
+ const recordDate = new Date(evalRecord.created_at);
1054
+ if (fromDate && recordDate < fromDate) {
1055
+ return false;
1056
+ }
1057
+ if (toDate && recordDate > toDate) {
1058
+ return false;
1059
+ }
1060
+ return true;
1061
+ });
1062
+ }
1063
+ const total = filteredData.length;
1064
+ const start = page * perPage;
1065
+ const end = start + perPage;
1066
+ const paginatedData = filteredData.slice(start, end);
1067
+ const evals = paginatedData.map((evalRecord) => {
1068
+ try {
1069
+ return {
1070
+ input: evalRecord.input,
1071
+ output: evalRecord.output,
1072
+ result: evalRecord.result && typeof evalRecord.result === "string" ? JSON.parse(evalRecord.result) : void 0,
1073
+ agentName: evalRecord.agent_name,
1074
+ createdAt: evalRecord.created_at,
1075
+ metricName: evalRecord.metric_name,
1076
+ instructions: evalRecord.instructions,
1077
+ runId: evalRecord.run_id,
1078
+ globalRunId: evalRecord.global_run_id,
1079
+ testInfo: evalRecord.test_info && typeof evalRecord.test_info === "string" ? JSON.parse(evalRecord.test_info) : void 0
1080
+ };
1081
+ } catch (parseError) {
1082
+ this.logger.error("Failed to parse eval record", { record: evalRecord, error: parseError });
1083
+ return {
1084
+ agentName: evalRecord.agent_name,
1085
+ createdAt: evalRecord.created_at,
1086
+ runId: evalRecord.run_id,
1087
+ globalRunId: evalRecord.global_run_id
1088
+ };
1089
+ }
1090
+ });
1091
+ const hasMore = end < total;
1092
+ return {
1093
+ evals,
1094
+ total,
1095
+ page,
1096
+ perPage,
1097
+ hasMore
1098
+ };
1099
+ } catch (error) {
1100
+ throw new MastraError(
1101
+ {
1102
+ id: "STORAGE_DYNAMODB_STORE_GET_EVALS_FAILED",
1103
+ domain: ErrorDomain.STORAGE,
1104
+ category: ErrorCategory.THIRD_PARTY,
1105
+ details: {
1106
+ agentName: agentName || "all",
1107
+ type: type || "all",
1108
+ page,
1109
+ perPage
1110
+ }
1111
+ },
1112
+ error
1113
+ );
1114
+ }
1115
+ }
1116
+ };
1117
+ var MemoryStorageDynamoDB = class extends MemoryStorage {
1118
+ service;
1119
+ constructor({ service }) {
1120
+ super();
1121
+ this.service = service;
1122
+ }
1123
+ // Helper function to parse message data (handle JSON fields)
1124
+ parseMessageData(data) {
1125
+ return {
1126
+ ...data,
1127
+ // Ensure dates are Date objects if needed (ElectroDB might return strings)
1128
+ createdAt: data.createdAt ? new Date(data.createdAt) : void 0,
1129
+ updatedAt: data.updatedAt ? new Date(data.updatedAt) : void 0
1130
+ // Other fields like content, toolCallArgs etc. are assumed to be correctly
1131
+ // transformed by the ElectroDB entity getters.
1132
+ };
1133
+ }
1134
+ // Helper function to transform and sort threads
1135
+ transformAndSortThreads(rawThreads, orderBy, sortDirection) {
1136
+ return rawThreads.map((data) => ({
1137
+ ...data,
1138
+ // Convert date strings back to Date objects for consistency
1139
+ createdAt: typeof data.createdAt === "string" ? new Date(data.createdAt) : data.createdAt,
1140
+ updatedAt: typeof data.updatedAt === "string" ? new Date(data.updatedAt) : data.updatedAt
1141
+ })).sort((a, b) => {
1142
+ const fieldA = orderBy === "createdAt" ? a.createdAt : a.updatedAt;
1143
+ const fieldB = orderBy === "createdAt" ? b.createdAt : b.updatedAt;
1144
+ const comparison = fieldA.getTime() - fieldB.getTime();
1145
+ return sortDirection === "DESC" ? -comparison : comparison;
1146
+ });
1147
+ }
1148
+ async getThreadById({ threadId }) {
1149
+ this.logger.debug("Getting thread by ID", { threadId });
1150
+ try {
1151
+ const result = await this.service.entities.thread.get({ entity: "thread", id: threadId }).go();
1152
+ if (!result.data) {
1153
+ return null;
1154
+ }
1155
+ const data = result.data;
1156
+ return {
1157
+ ...data,
1158
+ // Convert date strings back to Date objects for consistency
1159
+ createdAt: typeof data.createdAt === "string" ? new Date(data.createdAt) : data.createdAt,
1160
+ updatedAt: typeof data.updatedAt === "string" ? new Date(data.updatedAt) : data.updatedAt
1161
+ // metadata: data.metadata ? JSON.parse(data.metadata) : undefined, // REMOVED by AI
1162
+ // metadata is already transformed by the entity's getter
1163
+ };
1164
+ } catch (error) {
1165
+ throw new MastraError(
1166
+ {
1167
+ id: "STORAGE_DYNAMODB_STORE_GET_THREAD_BY_ID_FAILED",
1168
+ domain: ErrorDomain.STORAGE,
1169
+ category: ErrorCategory.THIRD_PARTY,
1170
+ details: { threadId }
1171
+ },
1172
+ error
1173
+ );
1174
+ }
1175
+ }
1176
+ /**
1177
+ * @deprecated use getThreadsByResourceIdPaginated instead for paginated results.
1178
+ */
1179
+ async getThreadsByResourceId(args) {
1180
+ const resourceId = args.resourceId;
1181
+ const orderBy = this.castThreadOrderBy(args.orderBy);
1182
+ const sortDirection = this.castThreadSortDirection(args.sortDirection);
1183
+ this.logger.debug("Getting threads by resource ID", { resourceId, orderBy, sortDirection });
1184
+ try {
1185
+ const result = await this.service.entities.thread.query.byResource({ entity: "thread", resourceId }).go();
1186
+ if (!result.data.length) {
1187
+ return [];
1188
+ }
1189
+ return this.transformAndSortThreads(result.data, orderBy, sortDirection);
1190
+ } catch (error) {
1191
+ throw new MastraError(
1192
+ {
1193
+ id: "STORAGE_DYNAMODB_STORE_GET_THREADS_BY_RESOURCE_ID_FAILED",
1194
+ domain: ErrorDomain.STORAGE,
1195
+ category: ErrorCategory.THIRD_PARTY,
1196
+ details: { resourceId }
1197
+ },
1198
+ error
1199
+ );
1200
+ }
1201
+ }
1202
+ async saveThread({ thread }) {
1203
+ this.logger.debug("Saving thread", { threadId: thread.id });
1204
+ const now = /* @__PURE__ */ new Date();
1205
+ const threadData = {
1206
+ entity: "thread",
1207
+ id: thread.id,
1208
+ resourceId: thread.resourceId,
1209
+ title: thread.title || `Thread ${thread.id}`,
1210
+ createdAt: thread.createdAt?.toISOString() || now.toISOString(),
1211
+ updatedAt: thread.updatedAt?.toISOString() || now.toISOString(),
1212
+ metadata: thread.metadata ? JSON.stringify(thread.metadata) : void 0
1213
+ };
1214
+ try {
1215
+ await this.service.entities.thread.upsert(threadData).go();
1216
+ return {
1217
+ id: thread.id,
1218
+ resourceId: thread.resourceId,
1219
+ title: threadData.title,
1220
+ createdAt: thread.createdAt || now,
1221
+ updatedAt: now,
1222
+ metadata: thread.metadata
1223
+ };
1224
+ } catch (error) {
1225
+ throw new MastraError(
1226
+ {
1227
+ id: "STORAGE_DYNAMODB_STORE_SAVE_THREAD_FAILED",
1228
+ domain: ErrorDomain.STORAGE,
1229
+ category: ErrorCategory.THIRD_PARTY,
1230
+ details: { threadId: thread.id }
1231
+ },
1232
+ error
1233
+ );
1234
+ }
1235
+ }
1236
+ async updateThread({
1237
+ id,
1238
+ title,
1239
+ metadata
1240
+ }) {
1241
+ this.logger.debug("Updating thread", { threadId: id });
1242
+ try {
1243
+ const existingThread = await this.getThreadById({ threadId: id });
1244
+ if (!existingThread) {
1245
+ throw new Error(`Thread not found: ${id}`);
1246
+ }
1247
+ const now = /* @__PURE__ */ new Date();
1248
+ const updateData = {
1249
+ updatedAt: now.toISOString()
1250
+ };
1251
+ if (title) {
1252
+ updateData.title = title;
1253
+ }
1254
+ if (metadata) {
1255
+ const existingMetadata = existingThread.metadata ? typeof existingThread.metadata === "string" ? JSON.parse(existingThread.metadata) : existingThread.metadata : {};
1256
+ const mergedMetadata = { ...existingMetadata, ...metadata };
1257
+ updateData.metadata = JSON.stringify(mergedMetadata);
1258
+ }
1259
+ await this.service.entities.thread.update({ entity: "thread", id }).set(updateData).go();
1260
+ return {
1261
+ ...existingThread,
1262
+ title: title || existingThread.title,
1263
+ metadata: metadata ? { ...existingThread.metadata, ...metadata } : existingThread.metadata,
1264
+ updatedAt: now
1265
+ };
1266
+ } catch (error) {
1267
+ throw new MastraError(
1268
+ {
1269
+ id: "STORAGE_DYNAMODB_STORE_UPDATE_THREAD_FAILED",
1270
+ domain: ErrorDomain.STORAGE,
1271
+ category: ErrorCategory.THIRD_PARTY,
1272
+ details: { threadId: id }
1273
+ },
1274
+ error
1275
+ );
1276
+ }
1277
+ }
1278
+ async deleteThread({ threadId }) {
1279
+ this.logger.debug("Deleting thread", { threadId });
1280
+ try {
1281
+ const messages = await this.getMessages({ threadId });
1282
+ if (messages.length > 0) {
1283
+ const batchSize = 25;
1284
+ for (let i = 0; i < messages.length; i += batchSize) {
1285
+ const batch = messages.slice(i, i + batchSize);
1286
+ await Promise.all(
1287
+ batch.map(
1288
+ (message) => this.service.entities.message.delete({
1289
+ entity: "message",
1290
+ id: message.id,
1291
+ threadId: message.threadId
1292
+ }).go()
1293
+ )
1294
+ );
1295
+ }
1296
+ }
1297
+ await this.service.entities.thread.delete({ entity: "thread", id: threadId }).go();
1298
+ } catch (error) {
1299
+ throw new MastraError(
1300
+ {
1301
+ id: "STORAGE_DYNAMODB_STORE_DELETE_THREAD_FAILED",
1302
+ domain: ErrorDomain.STORAGE,
1303
+ category: ErrorCategory.THIRD_PARTY,
1304
+ details: { threadId }
1305
+ },
1306
+ error
1307
+ );
1308
+ }
1309
+ }
1310
+ async getMessages({
1311
+ threadId,
1312
+ resourceId,
1313
+ selectBy,
1314
+ format
1315
+ }) {
1316
+ this.logger.debug("Getting messages", { threadId, selectBy });
1317
+ try {
1318
+ if (!threadId.trim()) throw new Error("threadId must be a non-empty string");
1319
+ const messages = [];
1320
+ const limit = resolveMessageLimit({ last: selectBy?.last, defaultLimit: Number.MAX_SAFE_INTEGER });
1321
+ if (selectBy?.include?.length) {
1322
+ const includeMessages = await this._getIncludedMessages(threadId, selectBy);
1323
+ if (includeMessages) {
1324
+ messages.push(...includeMessages);
1325
+ }
1326
+ }
1327
+ if (limit !== 0) {
1328
+ const query = this.service.entities.message.query.byThread({ entity: "message", threadId });
1329
+ let results;
1330
+ if (limit !== Number.MAX_SAFE_INTEGER && limit > 0) {
1331
+ results = await query.go({ limit, order: "desc" });
1332
+ results.data = results.data.reverse();
1333
+ } else {
1334
+ results = await query.go();
1335
+ }
1336
+ let allThreadMessages = results.data.map((data) => this.parseMessageData(data)).filter((msg) => "content" in msg);
1337
+ allThreadMessages.sort((a, b) => {
1338
+ const timeA = a.createdAt.getTime();
1339
+ const timeB = b.createdAt.getTime();
1340
+ if (timeA === timeB) {
1341
+ return a.id.localeCompare(b.id);
1342
+ }
1343
+ return timeA - timeB;
1344
+ });
1345
+ messages.push(...allThreadMessages);
1346
+ }
1347
+ messages.sort((a, b) => {
1348
+ const timeA = a.createdAt.getTime();
1349
+ const timeB = b.createdAt.getTime();
1350
+ if (timeA === timeB) {
1351
+ return a.id.localeCompare(b.id);
1352
+ }
1353
+ return timeA - timeB;
1354
+ });
1355
+ const uniqueMessages = messages.filter(
1356
+ (message, index, self) => index === self.findIndex((m) => m.id === message.id)
1357
+ );
1358
+ const list = new MessageList({ threadId, resourceId }).add(uniqueMessages, "memory");
1359
+ if (format === `v2`) return list.get.all.v2();
1360
+ return list.get.all.v1();
1361
+ } catch (error) {
1362
+ throw new MastraError(
1363
+ {
1364
+ id: "STORAGE_DYNAMODB_STORE_GET_MESSAGES_FAILED",
1365
+ domain: ErrorDomain.STORAGE,
1366
+ category: ErrorCategory.THIRD_PARTY,
1367
+ details: { threadId, resourceId: resourceId ?? "" }
1368
+ },
1369
+ error
1370
+ );
1371
+ }
1372
+ }
1373
+ async getMessagesById({
1374
+ messageIds,
1375
+ format
1376
+ }) {
1377
+ this.logger.debug("Getting messages by ID", { messageIds });
1378
+ if (messageIds.length === 0) return [];
1379
+ try {
1380
+ const results = await Promise.all(
1381
+ messageIds.map((id) => this.service.entities.message.query.primary({ entity: "message", id }).go())
1382
+ );
1383
+ const data = results.map((result) => result.data).flat(1);
1384
+ let parsedMessages = data.map((data2) => this.parseMessageData(data2)).filter((msg) => "content" in msg);
1385
+ const uniqueMessages = parsedMessages.filter(
1386
+ (message, index, self) => index === self.findIndex((m) => m.id === message.id)
1387
+ );
1388
+ const list = new MessageList().add(uniqueMessages, "memory");
1389
+ if (format === `v1`) return list.get.all.v1();
1390
+ return list.get.all.v2();
1391
+ } catch (error) {
1392
+ throw new MastraError(
1393
+ {
1394
+ id: "STORAGE_DYNAMODB_STORE_GET_MESSAGES_BY_ID_FAILED",
1395
+ domain: ErrorDomain.STORAGE,
1396
+ category: ErrorCategory.THIRD_PARTY,
1397
+ details: { messageIds: JSON.stringify(messageIds) }
1398
+ },
1399
+ error
1400
+ );
1401
+ }
1402
+ }
1403
+ async saveMessages(args) {
1404
+ const { messages, format = "v1" } = args;
1405
+ this.logger.debug("Saving messages", { count: messages.length });
1406
+ if (!messages.length) {
1407
+ return [];
1408
+ }
1409
+ const threadId = messages[0]?.threadId;
1410
+ if (!threadId) {
1411
+ throw new Error("Thread ID is required");
1412
+ }
1413
+ const messagesToSave = messages.map((msg) => {
1414
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1415
+ return {
1416
+ entity: "message",
1417
+ // Add entity type
1418
+ id: msg.id,
1419
+ threadId: msg.threadId,
1420
+ role: msg.role,
1421
+ type: msg.type,
1422
+ resourceId: msg.resourceId,
1423
+ // Ensure complex fields are stringified if not handled by attribute setters
1424
+ content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content),
1425
+ toolCallArgs: `toolCallArgs` in msg && msg.toolCallArgs ? JSON.stringify(msg.toolCallArgs) : void 0,
1426
+ toolCallIds: `toolCallIds` in msg && msg.toolCallIds ? JSON.stringify(msg.toolCallIds) : void 0,
1427
+ toolNames: `toolNames` in msg && msg.toolNames ? JSON.stringify(msg.toolNames) : void 0,
1428
+ createdAt: msg.createdAt instanceof Date ? msg.createdAt.toISOString() : msg.createdAt || now,
1429
+ updatedAt: now
1430
+ // Add updatedAt
1431
+ };
1432
+ });
1433
+ try {
1434
+ const savedMessageIds = [];
1435
+ for (const messageData of messagesToSave) {
1436
+ if (!messageData.entity) {
1437
+ this.logger.error("Missing entity property in message data for create", { messageData });
1438
+ throw new Error("Internal error: Missing entity property during saveMessages");
1439
+ }
1440
+ try {
1441
+ await this.service.entities.message.put(messageData).go();
1442
+ savedMessageIds.push(messageData.id);
1443
+ } catch (error) {
1444
+ for (const savedId of savedMessageIds) {
1445
+ try {
1446
+ await this.service.entities.message.delete({ entity: "message", id: savedId }).go();
1447
+ } catch (rollbackError) {
1448
+ this.logger.error("Failed to rollback message during save error", {
1449
+ messageId: savedId,
1450
+ error: rollbackError
1451
+ });
1452
+ }
1453
+ }
1454
+ throw error;
1455
+ }
1456
+ }
1457
+ await this.service.entities.thread.update({ entity: "thread", id: threadId }).set({
1458
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1459
+ }).go();
1460
+ const list = new MessageList().add(messages, "memory");
1461
+ if (format === `v1`) return list.get.all.v1();
1462
+ return list.get.all.v2();
1463
+ } catch (error) {
1464
+ throw new MastraError(
1465
+ {
1466
+ id: "STORAGE_DYNAMODB_STORE_SAVE_MESSAGES_FAILED",
1467
+ domain: ErrorDomain.STORAGE,
1468
+ category: ErrorCategory.THIRD_PARTY,
1469
+ details: { count: messages.length }
1470
+ },
1471
+ error
1472
+ );
1473
+ }
1474
+ }
1475
+ async getThreadsByResourceIdPaginated(args) {
1476
+ const { resourceId, page = 0, perPage = 100 } = args;
1477
+ const orderBy = this.castThreadOrderBy(args.orderBy);
1478
+ const sortDirection = this.castThreadSortDirection(args.sortDirection);
1479
+ this.logger.debug("Getting threads by resource ID with pagination", {
1480
+ resourceId,
1481
+ page,
1482
+ perPage,
1483
+ orderBy,
1484
+ sortDirection
1485
+ });
1486
+ try {
1487
+ const query = this.service.entities.thread.query.byResource({ entity: "thread", resourceId });
1488
+ const results = await query.go();
1489
+ const allThreads = this.transformAndSortThreads(results.data, orderBy, sortDirection);
1490
+ const startIndex = page * perPage;
1491
+ const endIndex = startIndex + perPage;
1492
+ const paginatedThreads = allThreads.slice(startIndex, endIndex);
1493
+ const total = allThreads.length;
1494
+ const hasMore = endIndex < total;
1495
+ return {
1496
+ threads: paginatedThreads,
1497
+ total,
1498
+ page,
1499
+ perPage,
1500
+ hasMore
1501
+ };
1502
+ } catch (error) {
1503
+ throw new MastraError(
1504
+ {
1505
+ id: "STORAGE_DYNAMODB_STORE_GET_THREADS_BY_RESOURCE_ID_PAGINATED_FAILED",
1506
+ domain: ErrorDomain.STORAGE,
1507
+ category: ErrorCategory.THIRD_PARTY,
1508
+ details: { resourceId, page, perPage }
1509
+ },
1510
+ error
1511
+ );
1512
+ }
1513
+ }
1514
+ async getMessagesPaginated(args) {
1515
+ const { threadId, resourceId, selectBy, format = "v1" } = args;
1516
+ const { page = 0, perPage = 40, dateRange } = selectBy?.pagination || {};
1517
+ const fromDate = dateRange?.start;
1518
+ const toDate = dateRange?.end;
1519
+ const limit = resolveMessageLimit({ last: selectBy?.last, defaultLimit: Number.MAX_SAFE_INTEGER });
1520
+ this.logger.debug("Getting messages with pagination", { threadId, page, perPage, fromDate, toDate, limit });
1521
+ try {
1522
+ if (!threadId.trim()) throw new Error("threadId must be a non-empty string");
1523
+ let messages = [];
1524
+ if (selectBy?.include?.length) {
1525
+ const includeMessages = await this._getIncludedMessages(threadId, selectBy);
1526
+ if (includeMessages) {
1527
+ messages.push(...includeMessages);
1528
+ }
1529
+ }
1530
+ if (limit !== 0) {
1531
+ const query = this.service.entities.message.query.byThread({ entity: "message", threadId });
1532
+ let results;
1533
+ if (limit !== Number.MAX_SAFE_INTEGER && limit > 0) {
1534
+ results = await query.go({ limit, order: "desc" });
1535
+ results.data = results.data.reverse();
1536
+ } else {
1537
+ results = await query.go();
1538
+ }
1539
+ let allThreadMessages = results.data.map((data) => this.parseMessageData(data)).filter((msg) => "content" in msg);
1540
+ allThreadMessages.sort((a, b) => {
1541
+ const timeA = a.createdAt.getTime();
1542
+ const timeB = b.createdAt.getTime();
1543
+ if (timeA === timeB) {
1544
+ return a.id.localeCompare(b.id);
1545
+ }
1546
+ return timeA - timeB;
1547
+ });
1548
+ const excludeIds = messages.map((m) => m.id);
1549
+ if (excludeIds.length > 0) {
1550
+ allThreadMessages = allThreadMessages.filter((msg) => !excludeIds.includes(msg.id));
1551
+ }
1552
+ messages.push(...allThreadMessages);
1553
+ }
1554
+ messages.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
1555
+ if (fromDate || toDate) {
1556
+ messages = messages.filter((msg) => {
1557
+ const createdAt = new Date(msg.createdAt).getTime();
1558
+ if (fromDate && createdAt < new Date(fromDate).getTime()) return false;
1559
+ if (toDate && createdAt > new Date(toDate).getTime()) return false;
1560
+ return true;
1561
+ });
1562
+ }
1563
+ const total = messages.length;
1564
+ const start = page * perPage;
1565
+ const end = start + perPage;
1566
+ const paginatedMessages = messages.slice(start, end);
1567
+ const hasMore = end < total;
1568
+ const list = new MessageList({ threadId, resourceId }).add(paginatedMessages, "memory");
1569
+ const finalMessages = format === "v2" ? list.get.all.v2() : list.get.all.v1();
1570
+ return {
1571
+ messages: finalMessages,
1572
+ total,
1573
+ page,
1574
+ perPage,
1575
+ hasMore
1576
+ };
1577
+ } catch (error) {
1578
+ const mastraError = new MastraError(
1579
+ {
1580
+ id: "STORAGE_DYNAMODB_STORE_GET_MESSAGES_PAGINATED_FAILED",
1581
+ domain: ErrorDomain.STORAGE,
1582
+ category: ErrorCategory.THIRD_PARTY,
1583
+ details: { threadId, resourceId: resourceId ?? "" }
1584
+ },
1585
+ error
1586
+ );
1587
+ this.logger?.trackException?.(mastraError);
1588
+ this.logger?.error?.(mastraError.toString());
1589
+ return { messages: [], total: 0, page, perPage, hasMore: false };
1590
+ }
1591
+ }
1592
+ // Helper method to get included messages with context
1593
+ async _getIncludedMessages(threadId, selectBy) {
1594
+ if (!threadId.trim()) throw new Error("threadId must be a non-empty string");
1595
+ if (!selectBy?.include?.length) {
1596
+ return [];
1597
+ }
1598
+ const includeMessages = [];
1599
+ for (const includeItem of selectBy.include) {
1600
+ try {
1601
+ const { id, threadId: targetThreadId, withPreviousMessages = 0, withNextMessages = 0 } = includeItem;
1602
+ const searchThreadId = targetThreadId || threadId;
1603
+ this.logger.debug("Getting included messages for", {
1604
+ id,
1605
+ targetThreadId,
1606
+ searchThreadId,
1607
+ withPreviousMessages,
1608
+ withNextMessages
1609
+ });
1610
+ const query = this.service.entities.message.query.byThread({ entity: "message", threadId: searchThreadId });
1611
+ const results = await query.go();
1612
+ const allMessages = results.data.map((data) => this.parseMessageData(data)).filter((msg) => "content" in msg && typeof msg.content === "object");
1613
+ this.logger.debug("Found messages in thread", {
1614
+ threadId: searchThreadId,
1615
+ messageCount: allMessages.length,
1616
+ messageIds: allMessages.map((m) => m.id)
1617
+ });
1618
+ allMessages.sort((a, b) => {
1619
+ const timeA = a.createdAt.getTime();
1620
+ const timeB = b.createdAt.getTime();
1621
+ if (timeA === timeB) {
1622
+ return a.id.localeCompare(b.id);
1623
+ }
1624
+ return timeA - timeB;
1625
+ });
1626
+ const targetIndex = allMessages.findIndex((msg) => msg.id === id);
1627
+ if (targetIndex === -1) {
1628
+ this.logger.warn("Target message not found", { id, threadId: searchThreadId });
1629
+ continue;
1630
+ }
1631
+ this.logger.debug("Found target message at index", { id, targetIndex, totalMessages: allMessages.length });
1632
+ const startIndex = Math.max(0, targetIndex - withPreviousMessages);
1633
+ const endIndex = Math.min(allMessages.length, targetIndex + withNextMessages + 1);
1634
+ const contextMessages = allMessages.slice(startIndex, endIndex);
1635
+ this.logger.debug("Context messages", {
1636
+ startIndex,
1637
+ endIndex,
1638
+ contextCount: contextMessages.length,
1639
+ contextIds: contextMessages.map((m) => m.id)
1640
+ });
1641
+ includeMessages.push(...contextMessages);
1642
+ } catch (error) {
1643
+ this.logger.warn("Failed to get included message", { messageId: includeItem.id, error });
1644
+ }
1645
+ }
1646
+ this.logger.debug("Total included messages", {
1647
+ count: includeMessages.length,
1648
+ ids: includeMessages.map((m) => m.id)
1649
+ });
1650
+ return includeMessages;
1651
+ }
1652
+ async updateMessages(args) {
1653
+ const { messages } = args;
1654
+ this.logger.debug("Updating messages", { count: messages.length });
1655
+ if (!messages.length) {
1656
+ return [];
1657
+ }
1658
+ const updatedMessages = [];
1659
+ const affectedThreadIds = /* @__PURE__ */ new Set();
1660
+ try {
1661
+ for (const updateData of messages) {
1662
+ const { id, ...updates } = updateData;
1663
+ const existingMessage = await this.service.entities.message.get({ entity: "message", id }).go();
1664
+ if (!existingMessage.data) {
1665
+ this.logger.warn("Message not found for update", { id });
1666
+ continue;
1667
+ }
1668
+ const existingMsg = this.parseMessageData(existingMessage.data);
1669
+ const originalThreadId = existingMsg.threadId;
1670
+ affectedThreadIds.add(originalThreadId);
1671
+ const updatePayload = {
1672
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1673
+ };
1674
+ if ("role" in updates && updates.role !== void 0) updatePayload.role = updates.role;
1675
+ if ("type" in updates && updates.type !== void 0) updatePayload.type = updates.type;
1676
+ if ("resourceId" in updates && updates.resourceId !== void 0) updatePayload.resourceId = updates.resourceId;
1677
+ if ("threadId" in updates && updates.threadId !== void 0 && updates.threadId !== null) {
1678
+ updatePayload.threadId = updates.threadId;
1679
+ affectedThreadIds.add(updates.threadId);
1680
+ }
1681
+ if (updates.content) {
1682
+ const existingContent = existingMsg.content;
1683
+ let newContent = { ...existingContent };
1684
+ if (updates.content.metadata !== void 0) {
1685
+ newContent.metadata = {
1686
+ ...existingContent.metadata || {},
1687
+ ...updates.content.metadata || {}
1688
+ };
1689
+ }
1690
+ if (updates.content.content !== void 0) {
1691
+ newContent.content = updates.content.content;
1692
+ }
1693
+ if ("parts" in updates.content && updates.content.parts !== void 0) {
1694
+ newContent.parts = updates.content.parts;
1695
+ }
1696
+ updatePayload.content = JSON.stringify(newContent);
1697
+ }
1698
+ await this.service.entities.message.update({ entity: "message", id }).set(updatePayload).go();
1699
+ const updatedMessage = await this.service.entities.message.get({ entity: "message", id }).go();
1700
+ if (updatedMessage.data) {
1701
+ updatedMessages.push(this.parseMessageData(updatedMessage.data));
1702
+ }
1703
+ }
1704
+ for (const threadId of affectedThreadIds) {
1705
+ await this.service.entities.thread.update({ entity: "thread", id: threadId }).set({
1706
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1707
+ }).go();
1708
+ }
1709
+ return updatedMessages;
1710
+ } catch (error) {
1711
+ throw new MastraError(
1712
+ {
1713
+ id: "STORAGE_DYNAMODB_STORE_UPDATE_MESSAGES_FAILED",
1714
+ domain: ErrorDomain.STORAGE,
1715
+ category: ErrorCategory.THIRD_PARTY,
1716
+ details: { count: messages.length }
1717
+ },
1718
+ error
1719
+ );
1720
+ }
1721
+ }
1722
+ async getResourceById({ resourceId }) {
1723
+ this.logger.debug("Getting resource by ID", { resourceId });
1724
+ try {
1725
+ const result = await this.service.entities.resource.get({ entity: "resource", id: resourceId }).go();
1726
+ if (!result.data) {
1727
+ return null;
1728
+ }
1729
+ const data = result.data;
1730
+ return {
1731
+ ...data,
1732
+ // Convert date strings back to Date objects for consistency
1733
+ createdAt: typeof data.createdAt === "string" ? new Date(data.createdAt) : data.createdAt,
1734
+ updatedAt: typeof data.updatedAt === "string" ? new Date(data.updatedAt) : data.updatedAt,
1735
+ // Ensure workingMemory is always returned as a string, regardless of automatic parsing
1736
+ workingMemory: typeof data.workingMemory === "object" ? JSON.stringify(data.workingMemory) : data.workingMemory
1737
+ // metadata is already transformed by the entity's getter
1738
+ };
1739
+ } catch (error) {
1740
+ throw new MastraError(
1741
+ {
1742
+ id: "STORAGE_DYNAMODB_STORE_GET_RESOURCE_BY_ID_FAILED",
1743
+ domain: ErrorDomain.STORAGE,
1744
+ category: ErrorCategory.THIRD_PARTY,
1745
+ details: { resourceId }
1746
+ },
1747
+ error
1748
+ );
1749
+ }
1750
+ }
1751
+ async saveResource({ resource }) {
1752
+ this.logger.debug("Saving resource", { resourceId: resource.id });
1753
+ const now = /* @__PURE__ */ new Date();
1754
+ const resourceData = {
1755
+ entity: "resource",
1756
+ id: resource.id,
1757
+ workingMemory: resource.workingMemory,
1758
+ metadata: resource.metadata ? JSON.stringify(resource.metadata) : void 0,
1759
+ createdAt: resource.createdAt?.toISOString() || now.toISOString(),
1760
+ updatedAt: now.toISOString()
1761
+ };
1762
+ try {
1763
+ await this.service.entities.resource.upsert(resourceData).go();
1764
+ return {
1765
+ id: resource.id,
1766
+ workingMemory: resource.workingMemory,
1767
+ metadata: resource.metadata,
1768
+ createdAt: resource.createdAt || now,
1769
+ updatedAt: now
1770
+ };
1771
+ } catch (error) {
1772
+ throw new MastraError(
1773
+ {
1774
+ id: "STORAGE_DYNAMODB_STORE_SAVE_RESOURCE_FAILED",
1775
+ domain: ErrorDomain.STORAGE,
1776
+ category: ErrorCategory.THIRD_PARTY,
1777
+ details: { resourceId: resource.id }
1778
+ },
1779
+ error
1780
+ );
1781
+ }
1782
+ }
1783
+ async updateResource({
1784
+ resourceId,
1785
+ workingMemory,
1786
+ metadata
1787
+ }) {
1788
+ this.logger.debug("Updating resource", { resourceId });
1789
+ try {
1790
+ const existingResource = await this.getResourceById({ resourceId });
1791
+ if (!existingResource) {
1792
+ const newResource = {
1793
+ id: resourceId,
1794
+ workingMemory,
1795
+ metadata: metadata || {},
1796
+ createdAt: /* @__PURE__ */ new Date(),
1797
+ updatedAt: /* @__PURE__ */ new Date()
1798
+ };
1799
+ return this.saveResource({ resource: newResource });
1800
+ }
1801
+ const now = /* @__PURE__ */ new Date();
1802
+ const updateData = {
1803
+ updatedAt: now.toISOString()
1804
+ };
1805
+ if (workingMemory !== void 0) {
1806
+ updateData.workingMemory = workingMemory;
1807
+ }
1808
+ if (metadata) {
1809
+ const existingMetadata = existingResource.metadata || {};
1810
+ const mergedMetadata = { ...existingMetadata, ...metadata };
1811
+ updateData.metadata = JSON.stringify(mergedMetadata);
1812
+ }
1813
+ await this.service.entities.resource.update({ entity: "resource", id: resourceId }).set(updateData).go();
1814
+ return {
1815
+ ...existingResource,
1816
+ workingMemory: workingMemory !== void 0 ? workingMemory : existingResource.workingMemory,
1817
+ metadata: metadata ? { ...existingResource.metadata, ...metadata } : existingResource.metadata,
1818
+ updatedAt: now
1819
+ };
1820
+ } catch (error) {
1821
+ throw new MastraError(
1822
+ {
1823
+ id: "STORAGE_DYNAMODB_STORE_UPDATE_RESOURCE_FAILED",
1824
+ domain: ErrorDomain.STORAGE,
1825
+ category: ErrorCategory.THIRD_PARTY,
1826
+ details: { resourceId }
1827
+ },
1828
+ error
1829
+ );
1830
+ }
1831
+ }
1832
+ };
1833
+ var StoreOperationsDynamoDB = class extends StoreOperations {
1834
+ client;
1835
+ tableName;
1836
+ service;
1837
+ constructor({
1838
+ service,
1839
+ tableName,
1840
+ client
1841
+ }) {
1842
+ super();
1843
+ this.service = service;
1844
+ this.client = client;
1845
+ this.tableName = tableName;
1846
+ }
1847
+ async hasColumn() {
1848
+ return true;
1849
+ }
1850
+ async dropTable() {
1851
+ }
1852
+ // Helper methods for entity/table mapping
1853
+ getEntityNameForTable(tableName) {
1854
+ const mapping = {
1855
+ [TABLE_THREADS]: "thread",
1856
+ [TABLE_MESSAGES]: "message",
1857
+ [TABLE_WORKFLOW_SNAPSHOT]: "workflow_snapshot",
1858
+ [TABLE_EVALS]: "eval",
1859
+ [TABLE_SCORERS]: "score",
1860
+ [TABLE_TRACES]: "trace",
1861
+ [TABLE_RESOURCES]: "resource",
1862
+ [TABLE_AI_SPANS]: "ai_span"
1863
+ };
1864
+ return mapping[tableName] || null;
1865
+ }
1866
+ /**
1867
+ * Pre-processes a record to ensure Date objects are converted to ISO strings
1868
+ * This is necessary because ElectroDB validation happens before setters are applied
1869
+ */
1870
+ preprocessRecord(record) {
1871
+ const processed = { ...record };
1872
+ if (processed.createdAt instanceof Date) {
1873
+ processed.createdAt = processed.createdAt.toISOString();
555
1874
  }
556
- );
557
- }
558
-
559
- // src/storage/index.ts
560
- var DynamoDBStore = class extends MastraStorage {
561
- constructor({ name, config }) {
562
- super({ name });
563
- this.hasInitialized = null;
564
- if (!config.tableName || typeof config.tableName !== "string" || config.tableName.trim() === "") {
565
- throw new Error("DynamoDBStore: config.tableName must be provided and cannot be empty.");
1875
+ if (processed.updatedAt instanceof Date) {
1876
+ processed.updatedAt = processed.updatedAt.toISOString();
1877
+ }
1878
+ if (processed.created_at instanceof Date) {
1879
+ processed.created_at = processed.created_at.toISOString();
566
1880
  }
567
- if (!/^[a-zA-Z0-9_.-]{3,255}$/.test(config.tableName)) {
568
- throw new Error(
569
- `DynamoDBStore: config.tableName "${config.tableName}" contains invalid characters or is not between 3 and 255 characters long.`
1881
+ if (processed.result && typeof processed.result === "object") {
1882
+ processed.result = JSON.stringify(processed.result);
1883
+ }
1884
+ if (processed.test_info && typeof processed.test_info === "object") {
1885
+ processed.test_info = JSON.stringify(processed.test_info);
1886
+ } else if (processed.test_info === void 0 || processed.test_info === null) {
1887
+ delete processed.test_info;
1888
+ }
1889
+ if (processed.snapshot && typeof processed.snapshot === "object") {
1890
+ processed.snapshot = JSON.stringify(processed.snapshot);
1891
+ }
1892
+ if (processed.attributes && typeof processed.attributes === "object") {
1893
+ processed.attributes = JSON.stringify(processed.attributes);
1894
+ }
1895
+ if (processed.status && typeof processed.status === "object") {
1896
+ processed.status = JSON.stringify(processed.status);
1897
+ }
1898
+ if (processed.events && typeof processed.events === "object") {
1899
+ processed.events = JSON.stringify(processed.events);
1900
+ }
1901
+ if (processed.links && typeof processed.links === "object") {
1902
+ processed.links = JSON.stringify(processed.links);
1903
+ }
1904
+ return processed;
1905
+ }
1906
+ /**
1907
+ * Validates that the required DynamoDB table exists and is accessible.
1908
+ * This does not check the table structure - it assumes the table
1909
+ * was created with the correct structure via CDK/CloudFormation.
1910
+ */
1911
+ async validateTableExists() {
1912
+ try {
1913
+ const command = new DescribeTableCommand({
1914
+ TableName: this.tableName
1915
+ });
1916
+ await this.client.send(command);
1917
+ return true;
1918
+ } catch (error) {
1919
+ if (error.name === "ResourceNotFoundException") {
1920
+ return false;
1921
+ }
1922
+ throw new MastraError(
1923
+ {
1924
+ id: "STORAGE_DYNAMODB_STORE_VALIDATE_TABLE_EXISTS_FAILED",
1925
+ domain: ErrorDomain.STORAGE,
1926
+ category: ErrorCategory.THIRD_PARTY,
1927
+ details: { tableName: this.tableName }
1928
+ },
1929
+ error
570
1930
  );
571
1931
  }
572
- const dynamoClient = new DynamoDBClient({
573
- region: config.region || "us-east-1",
574
- endpoint: config.endpoint,
575
- credentials: config.credentials
576
- });
577
- this.tableName = config.tableName;
578
- this.client = DynamoDBDocumentClient.from(dynamoClient);
579
- this.service = getElectroDbService(this.client, this.tableName);
580
1932
  }
581
1933
  /**
582
1934
  * This method is modified for DynamoDB with ElectroDB single-table design.
@@ -600,59 +1952,45 @@ var DynamoDBStore = class extends MastraStorage {
600
1952
  this.logger.debug(`Table ${this.tableName} exists and is accessible`);
601
1953
  } catch (error) {
602
1954
  this.logger.error("Error validating table access", { tableName: this.tableName, error });
603
- throw error;
1955
+ throw new MastraError(
1956
+ {
1957
+ id: "STORAGE_DYNAMODB_STORE_VALIDATE_TABLE_ACCESS_FAILED",
1958
+ domain: ErrorDomain.STORAGE,
1959
+ category: ErrorCategory.THIRD_PARTY,
1960
+ details: { tableName: this.tableName }
1961
+ },
1962
+ error
1963
+ );
604
1964
  }
605
1965
  }
606
- /**
607
- * Validates that the required DynamoDB table exists and is accessible.
608
- * This does not check the table structure - it assumes the table
609
- * was created with the correct structure via CDK/CloudFormation.
610
- */
611
- async validateTableExists() {
612
- try {
613
- const command = new DescribeTableCommand({
614
- TableName: this.tableName
1966
+ async insert({ tableName, record }) {
1967
+ this.logger.debug("DynamoDB insert called", { tableName });
1968
+ const entityName = this.getEntityNameForTable(tableName);
1969
+ if (!entityName || !this.service.entities[entityName]) {
1970
+ throw new MastraError({
1971
+ id: "STORAGE_DYNAMODB_STORE_INSERT_INVALID_ARGS",
1972
+ domain: ErrorDomain.STORAGE,
1973
+ category: ErrorCategory.USER,
1974
+ text: "No entity defined for tableName",
1975
+ details: { tableName }
615
1976
  });
616
- await this.client.send(command);
617
- return true;
618
- } catch (error) {
619
- if (error.name === "ResourceNotFoundException") {
620
- return false;
621
- }
622
- throw error;
623
- }
624
- }
625
- /**
626
- * Initialize storage, validating the externally managed table is accessible.
627
- * For the single-table design, we only validate once that we can access
628
- * the table that was created via CDK/CloudFormation.
629
- */
630
- async init() {
631
- if (this.hasInitialized === null) {
632
- this.hasInitialized = this._performInitializationAndStore();
633
1977
  }
634
1978
  try {
635
- await this.hasInitialized;
1979
+ const dataToSave = { entity: entityName, ...this.preprocessRecord(record) };
1980
+ await this.service.entities[entityName].create(dataToSave).go();
636
1981
  } catch (error) {
637
- throw error;
1982
+ throw new MastraError(
1983
+ {
1984
+ id: "STORAGE_DYNAMODB_STORE_INSERT_FAILED",
1985
+ domain: ErrorDomain.STORAGE,
1986
+ category: ErrorCategory.THIRD_PARTY,
1987
+ details: { tableName }
1988
+ },
1989
+ error
1990
+ );
638
1991
  }
639
1992
  }
640
- /**
641
- * Performs the actual table validation and stores the promise.
642
- * Handles resetting the stored promise on failure to allow retries.
643
- */
644
- _performInitializationAndStore() {
645
- return this.validateTableExists().then((exists) => {
646
- if (!exists) {
647
- throw new Error(
648
- `Table ${this.tableName} does not exist or is not accessible. Ensure it's created via CDK/CloudFormation before using this store.`
649
- );
650
- }
651
- return true;
652
- }).catch((err) => {
653
- this.hasInitialized = null;
654
- throw err;
655
- });
1993
+ async alterTable(_args) {
656
1994
  }
657
1995
  /**
658
1996
  * Clear all items from a logical "table" (entity type)
@@ -661,7 +1999,13 @@ var DynamoDBStore = class extends MastraStorage {
661
1999
  this.logger.debug("DynamoDB clearTable called", { tableName });
662
2000
  const entityName = this.getEntityNameForTable(tableName);
663
2001
  if (!entityName || !this.service.entities[entityName]) {
664
- throw new Error(`No entity defined for ${tableName}`);
2002
+ throw new MastraError({
2003
+ id: "STORAGE_DYNAMODB_STORE_CLEAR_TABLE_INVALID_ARGS",
2004
+ domain: ErrorDomain.STORAGE,
2005
+ category: ErrorCategory.USER,
2006
+ text: "No entity defined for tableName",
2007
+ details: { tableName }
2008
+ });
665
2009
  }
666
2010
  try {
667
2011
  const result = await this.service.entities[entityName].scan.go({ pages: "all" });
@@ -681,10 +2025,10 @@ var DynamoDBStore = class extends MastraStorage {
681
2025
  if (!item.id) throw new Error(`Missing required key 'id' for entity 'message'`);
682
2026
  key.id = item.id;
683
2027
  break;
684
- case "workflowSnapshot":
2028
+ case "workflow_snapshot":
685
2029
  if (!item.workflow_name)
686
- throw new Error(`Missing required key 'workflow_name' for entity 'workflowSnapshot'`);
687
- if (!item.run_id) throw new Error(`Missing required key 'run_id' for entity 'workflowSnapshot'`);
2030
+ throw new Error(`Missing required key 'workflow_name' for entity 'workflow_snapshot'`);
2031
+ if (!item.run_id) throw new Error(`Missing required key 'run_id' for entity 'workflow_snapshot'`);
688
2032
  key.workflow_name = item.workflow_name;
689
2033
  key.run_id = item.run_id;
690
2034
  break;
@@ -696,6 +2040,14 @@ var DynamoDBStore = class extends MastraStorage {
696
2040
  if (!item.id) throw new Error(`Missing required key 'id' for entity 'trace'`);
697
2041
  key.id = item.id;
698
2042
  break;
2043
+ case "score":
2044
+ if (!item.id) throw new Error(`Missing required key 'id' for entity 'score'`);
2045
+ key.id = item.id;
2046
+ break;
2047
+ case "resource":
2048
+ if (!item.id) throw new Error(`Missing required key 'id' for entity 'resource'`);
2049
+ key.id = item.id;
2050
+ break;
699
2051
  default:
700
2052
  this.logger.warn(`Unknown entity type encountered during clearTable: ${entityName}`);
701
2053
  throw new Error(`Cannot construct delete key for unknown entity type: ${entityName}`);
@@ -709,25 +2061,15 @@ var DynamoDBStore = class extends MastraStorage {
709
2061
  }
710
2062
  this.logger.debug(`Successfully cleared all records for ${tableName}`);
711
2063
  } catch (error) {
712
- this.logger.error("Failed to clear table", { tableName, error });
713
- throw error;
714
- }
715
- }
716
- /**
717
- * Insert a record into the specified "table" (entity)
718
- */
719
- async insert({ tableName, record }) {
720
- this.logger.debug("DynamoDB insert called", { tableName });
721
- const entityName = this.getEntityNameForTable(tableName);
722
- if (!entityName || !this.service.entities[entityName]) {
723
- throw new Error(`No entity defined for ${tableName}`);
724
- }
725
- try {
726
- const dataToSave = { entity: entityName, ...record };
727
- await this.service.entities[entityName].create(dataToSave).go();
728
- } catch (error) {
729
- this.logger.error("Failed to insert record", { tableName, error });
730
- throw error;
2064
+ throw new MastraError(
2065
+ {
2066
+ id: "STORAGE_DYNAMODB_STORE_CLEAR_TABLE_FAILED",
2067
+ domain: ErrorDomain.STORAGE,
2068
+ category: ErrorCategory.THIRD_PARTY,
2069
+ details: { tableName }
2070
+ },
2071
+ error
2072
+ );
731
2073
  }
732
2074
  }
733
2075
  /**
@@ -737,9 +2079,15 @@ var DynamoDBStore = class extends MastraStorage {
737
2079
  this.logger.debug("DynamoDB batchInsert called", { tableName, count: records.length });
738
2080
  const entityName = this.getEntityNameForTable(tableName);
739
2081
  if (!entityName || !this.service.entities[entityName]) {
740
- throw new Error(`No entity defined for ${tableName}`);
2082
+ throw new MastraError({
2083
+ id: "STORAGE_DYNAMODB_STORE_BATCH_INSERT_INVALID_ARGS",
2084
+ domain: ErrorDomain.STORAGE,
2085
+ category: ErrorCategory.USER,
2086
+ text: "No entity defined for tableName",
2087
+ details: { tableName }
2088
+ });
741
2089
  }
742
- const recordsToSave = records.map((rec) => ({ entity: entityName, ...rec }));
2090
+ const recordsToSave = records.map((rec) => ({ entity: entityName, ...this.preprocessRecord(rec) }));
743
2091
  const batchSize = 25;
744
2092
  const batches = [];
745
2093
  for (let i = 0; i < recordsToSave.length; i += batchSize) {
@@ -758,8 +2106,15 @@ var DynamoDBStore = class extends MastraStorage {
758
2106
  }
759
2107
  }
760
2108
  } catch (error) {
761
- this.logger.error("Failed to batch insert records", { tableName, error });
762
- throw error;
2109
+ throw new MastraError(
2110
+ {
2111
+ id: "STORAGE_DYNAMODB_STORE_BATCH_INSERT_FAILED",
2112
+ domain: ErrorDomain.STORAGE,
2113
+ category: ErrorCategory.THIRD_PARTY,
2114
+ details: { tableName }
2115
+ },
2116
+ error
2117
+ );
763
2118
  }
764
2119
  }
765
2120
  /**
@@ -769,7 +2124,13 @@ var DynamoDBStore = class extends MastraStorage {
769
2124
  this.logger.debug("DynamoDB load called", { tableName, keys });
770
2125
  const entityName = this.getEntityNameForTable(tableName);
771
2126
  if (!entityName || !this.service.entities[entityName]) {
772
- throw new Error(`No entity defined for ${tableName}`);
2127
+ throw new MastraError({
2128
+ id: "STORAGE_DYNAMODB_STORE_LOAD_INVALID_ARGS",
2129
+ domain: ErrorDomain.STORAGE,
2130
+ category: ErrorCategory.USER,
2131
+ text: "No entity defined for tableName",
2132
+ details: { tableName }
2133
+ });
773
2134
  }
774
2135
  try {
775
2136
  const keyObject = { entity: entityName, ...keys };
@@ -780,268 +2141,327 @@ var DynamoDBStore = class extends MastraStorage {
780
2141
  let data = result.data;
781
2142
  return data;
782
2143
  } catch (error) {
783
- this.logger.error("Failed to load record", { tableName, keys, error });
784
- throw error;
2144
+ throw new MastraError(
2145
+ {
2146
+ id: "STORAGE_DYNAMODB_STORE_LOAD_FAILED",
2147
+ domain: ErrorDomain.STORAGE,
2148
+ category: ErrorCategory.THIRD_PARTY,
2149
+ details: { tableName }
2150
+ },
2151
+ error
2152
+ );
785
2153
  }
786
2154
  }
787
- // Thread operations
788
- async getThreadById({ threadId }) {
789
- this.logger.debug("Getting thread by ID", { threadId });
2155
+ };
2156
+ var ScoresStorageDynamoDB = class extends ScoresStorage {
2157
+ service;
2158
+ constructor({ service }) {
2159
+ super();
2160
+ this.service = service;
2161
+ }
2162
+ // Helper function to parse score data (handle JSON fields)
2163
+ parseScoreData(data) {
2164
+ return {
2165
+ ...data,
2166
+ // Convert date strings back to Date objects for consistency
2167
+ createdAt: data.createdAt ? new Date(data.createdAt) : /* @__PURE__ */ new Date(),
2168
+ updatedAt: data.updatedAt ? new Date(data.updatedAt) : /* @__PURE__ */ new Date()
2169
+ // JSON fields are already transformed by the entity's getters
2170
+ };
2171
+ }
2172
+ async getScoreById({ id }) {
2173
+ this.logger.debug("Getting score by ID", { id });
790
2174
  try {
791
- const result = await this.service.entities.thread.get({ entity: "thread", id: threadId }).go();
2175
+ const result = await this.service.entities.score.get({ entity: "score", id }).go();
792
2176
  if (!result.data) {
793
2177
  return null;
794
2178
  }
795
- const data = result.data;
796
- return {
797
- ...data
798
- // metadata: data.metadata ? JSON.parse(data.metadata) : undefined, // REMOVED by AI
799
- // metadata is already transformed by the entity's getter
800
- };
2179
+ return this.parseScoreData(result.data);
801
2180
  } catch (error) {
802
- this.logger.error("Failed to get thread by ID", { threadId, error });
803
- throw error;
2181
+ throw new MastraError(
2182
+ {
2183
+ id: "STORAGE_DYNAMODB_STORE_GET_SCORE_BY_ID_FAILED",
2184
+ domain: ErrorDomain.STORAGE,
2185
+ category: ErrorCategory.THIRD_PARTY,
2186
+ details: { id }
2187
+ },
2188
+ error
2189
+ );
804
2190
  }
805
2191
  }
806
- async getThreadsByResourceId({ resourceId }) {
807
- this.logger.debug("Getting threads by resource ID", { resourceId });
2192
+ async saveScore(score) {
2193
+ let validatedScore;
808
2194
  try {
809
- const result = await this.service.entities.thread.query.byResource({ entity: "thread", resourceId }).go();
810
- if (!result.data.length) {
811
- return [];
812
- }
813
- return result.data.map((data) => ({
814
- ...data
815
- // metadata: data.metadata ? JSON.parse(data.metadata) : undefined, // REMOVED by AI
816
- // metadata is already transformed by the entity's getter
817
- }));
2195
+ validatedScore = saveScorePayloadSchema.parse(score);
818
2196
  } catch (error) {
819
- this.logger.error("Failed to get threads by resource ID", { resourceId, error });
820
- throw error;
2197
+ throw new MastraError(
2198
+ {
2199
+ id: "STORAGE_DYNAMODB_STORE_SAVE_SCORE_FAILED",
2200
+ domain: ErrorDomain.STORAGE,
2201
+ category: ErrorCategory.THIRD_PARTY
2202
+ },
2203
+ error
2204
+ );
821
2205
  }
822
- }
823
- async saveThread({ thread }) {
824
- this.logger.debug("Saving thread", { threadId: thread.id });
825
2206
  const now = /* @__PURE__ */ new Date();
826
- const threadData = {
827
- entity: "thread",
828
- id: thread.id,
829
- resourceId: thread.resourceId,
830
- title: thread.title || `Thread ${thread.id}`,
831
- createdAt: thread.createdAt?.toISOString() || now.toISOString(),
832
- updatedAt: now.toISOString(),
833
- metadata: thread.metadata ? JSON.stringify(thread.metadata) : void 0
2207
+ const scoreId = `score-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
2208
+ const scoreData = {
2209
+ entity: "score",
2210
+ id: scoreId,
2211
+ scorerId: validatedScore.scorerId,
2212
+ traceId: validatedScore.traceId || "",
2213
+ spanId: validatedScore.spanId || "",
2214
+ runId: validatedScore.runId,
2215
+ scorer: typeof validatedScore.scorer === "string" ? validatedScore.scorer : JSON.stringify(validatedScore.scorer),
2216
+ preprocessStepResult: typeof validatedScore.preprocessStepResult === "string" ? validatedScore.preprocessStepResult : JSON.stringify(validatedScore.preprocessStepResult),
2217
+ analyzeStepResult: typeof validatedScore.analyzeStepResult === "string" ? validatedScore.analyzeStepResult : JSON.stringify(validatedScore.analyzeStepResult),
2218
+ score: validatedScore.score,
2219
+ reason: validatedScore.reason,
2220
+ preprocessPrompt: validatedScore.preprocessPrompt,
2221
+ generateScorePrompt: validatedScore.generateScorePrompt,
2222
+ generateReasonPrompt: validatedScore.generateReasonPrompt,
2223
+ analyzePrompt: validatedScore.analyzePrompt,
2224
+ input: typeof validatedScore.input === "string" ? validatedScore.input : JSON.stringify(validatedScore.input),
2225
+ output: typeof validatedScore.output === "string" ? validatedScore.output : JSON.stringify(validatedScore.output),
2226
+ additionalContext: typeof validatedScore.additionalContext === "string" ? validatedScore.additionalContext : JSON.stringify(validatedScore.additionalContext),
2227
+ runtimeContext: typeof validatedScore.runtimeContext === "string" ? validatedScore.runtimeContext : JSON.stringify(validatedScore.runtimeContext),
2228
+ entityType: validatedScore.entityType,
2229
+ entityData: typeof validatedScore.entity === "string" ? validatedScore.entity : JSON.stringify(validatedScore.entity),
2230
+ entityId: validatedScore.entityId,
2231
+ source: validatedScore.source,
2232
+ resourceId: validatedScore.resourceId || "",
2233
+ threadId: validatedScore.threadId || "",
2234
+ createdAt: now.toISOString(),
2235
+ updatedAt: now.toISOString()
834
2236
  };
835
2237
  try {
836
- await this.service.entities.thread.create(threadData).go();
837
- return {
838
- id: thread.id,
839
- resourceId: thread.resourceId,
840
- title: threadData.title,
841
- createdAt: thread.createdAt || now,
842
- updatedAt: now,
843
- metadata: thread.metadata
2238
+ await this.service.entities.score.upsert(scoreData).go();
2239
+ const savedScore = {
2240
+ ...score,
2241
+ id: scoreId,
2242
+ createdAt: now,
2243
+ updatedAt: now
844
2244
  };
2245
+ return { score: savedScore };
845
2246
  } catch (error) {
846
- this.logger.error("Failed to save thread", { threadId: thread.id, error });
847
- throw error;
2247
+ throw new MastraError(
2248
+ {
2249
+ id: "STORAGE_DYNAMODB_STORE_SAVE_SCORE_FAILED",
2250
+ domain: ErrorDomain.STORAGE,
2251
+ category: ErrorCategory.THIRD_PARTY,
2252
+ details: { scorerId: score.scorerId, runId: score.runId }
2253
+ },
2254
+ error
2255
+ );
848
2256
  }
849
2257
  }
850
- async updateThread({
851
- id,
852
- title,
853
- metadata
2258
+ async getScoresByScorerId({
2259
+ scorerId,
2260
+ pagination,
2261
+ entityId,
2262
+ entityType,
2263
+ source
854
2264
  }) {
855
- this.logger.debug("Updating thread", { threadId: id });
856
2265
  try {
857
- const existingThread = await this.getThreadById({ threadId: id });
858
- if (!existingThread) {
859
- throw new Error(`Thread not found: ${id}`);
2266
+ const query = this.service.entities.score.query.byScorer({ entity: "score", scorerId });
2267
+ const results = await query.go();
2268
+ let allScores = results.data.map((data) => this.parseScoreData(data));
2269
+ if (entityId) {
2270
+ allScores = allScores.filter((score) => score.entityId === entityId);
860
2271
  }
861
- const now = /* @__PURE__ */ new Date();
862
- const updateData = {
863
- updatedAt: now.toISOString()
864
- };
865
- if (title) {
866
- updateData.title = title;
2272
+ if (entityType) {
2273
+ allScores = allScores.filter((score) => score.entityType === entityType);
867
2274
  }
868
- if (metadata) {
869
- updateData.metadata = JSON.stringify(metadata);
2275
+ if (source) {
2276
+ allScores = allScores.filter((score) => score.source === source);
870
2277
  }
871
- await this.service.entities.thread.update({ entity: "thread", id }).set(updateData).go();
2278
+ allScores.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
2279
+ const startIndex = pagination.page * pagination.perPage;
2280
+ const endIndex = startIndex + pagination.perPage;
2281
+ const paginatedScores = allScores.slice(startIndex, endIndex);
2282
+ const total = allScores.length;
2283
+ const hasMore = endIndex < total;
872
2284
  return {
873
- ...existingThread,
874
- title: title || existingThread.title,
875
- metadata: metadata || existingThread.metadata,
876
- updatedAt: now
2285
+ scores: paginatedScores,
2286
+ pagination: {
2287
+ total,
2288
+ page: pagination.page,
2289
+ perPage: pagination.perPage,
2290
+ hasMore
2291
+ }
877
2292
  };
878
2293
  } catch (error) {
879
- this.logger.error("Failed to update thread", { threadId: id, error });
880
- throw error;
881
- }
882
- }
883
- async deleteThread({ threadId }) {
884
- this.logger.debug("Deleting thread", { threadId });
885
- try {
886
- await this.service.entities.thread.delete({ entity: "thread", id: threadId }).go();
887
- } catch (error) {
888
- this.logger.error("Failed to delete thread", { threadId, error });
889
- throw error;
2294
+ throw new MastraError(
2295
+ {
2296
+ id: "STORAGE_DYNAMODB_STORE_GET_SCORES_BY_SCORER_ID_FAILED",
2297
+ domain: ErrorDomain.STORAGE,
2298
+ category: ErrorCategory.THIRD_PARTY,
2299
+ details: {
2300
+ scorerId: scorerId || "",
2301
+ entityId: entityId || "",
2302
+ entityType: entityType || "",
2303
+ source: source || "",
2304
+ page: pagination.page,
2305
+ perPage: pagination.perPage
2306
+ }
2307
+ },
2308
+ error
2309
+ );
890
2310
  }
891
2311
  }
892
- async getMessages({
893
- threadId,
894
- resourceId,
895
- selectBy,
896
- format
2312
+ async getScoresByRunId({
2313
+ runId,
2314
+ pagination
897
2315
  }) {
898
- this.logger.debug("Getting messages", { threadId, selectBy });
2316
+ this.logger.debug("Getting scores by run ID", { runId, pagination });
899
2317
  try {
900
- const query = this.service.entities.message.query.byThread({ entity: "message", threadId });
901
- if (selectBy?.last && typeof selectBy.last === "number") {
902
- const results2 = await query.go({ limit: selectBy.last, reverse: true });
903
- const list2 = new MessageList({ threadId, resourceId }).add(
904
- results2.data.map((data) => this.parseMessageData(data)),
905
- "memory"
906
- );
907
- if (format === `v2`) return list2.get.all.mastra();
908
- return list2.get.all.v1();
909
- }
2318
+ const query = this.service.entities.score.query.byRun({ entity: "score", runId });
910
2319
  const results = await query.go();
911
- const list = new MessageList({ threadId, resourceId }).add(
912
- results.data.map((data) => this.parseMessageData(data)),
913
- "memory"
914
- );
915
- if (format === `v2`) return list.get.all.mastra();
916
- return list.get.all.v1();
917
- } catch (error) {
918
- this.logger.error("Failed to get messages", { threadId, error });
919
- throw error;
920
- }
921
- }
922
- async saveMessages(args) {
923
- const { messages, format = "v1" } = args;
924
- this.logger.debug("Saving messages", { count: messages.length });
925
- if (!messages.length) {
926
- return [];
927
- }
928
- const messagesToSave = messages.map((msg) => {
929
- const now = (/* @__PURE__ */ new Date()).toISOString();
2320
+ const allScores = results.data.map((data) => this.parseScoreData(data));
2321
+ allScores.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
2322
+ const startIndex = pagination.page * pagination.perPage;
2323
+ const endIndex = startIndex + pagination.perPage;
2324
+ const paginatedScores = allScores.slice(startIndex, endIndex);
2325
+ const total = allScores.length;
2326
+ const hasMore = endIndex < total;
930
2327
  return {
931
- entity: "message",
932
- // Add entity type
933
- id: msg.id,
934
- threadId: msg.threadId,
935
- role: msg.role,
936
- type: msg.type,
937
- resourceId: msg.resourceId,
938
- // Ensure complex fields are stringified if not handled by attribute setters
939
- content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content),
940
- toolCallArgs: `toolCallArgs` in msg && msg.toolCallArgs ? JSON.stringify(msg.toolCallArgs) : void 0,
941
- toolCallIds: `toolCallIds` in msg && msg.toolCallIds ? JSON.stringify(msg.toolCallIds) : void 0,
942
- toolNames: `toolNames` in msg && msg.toolNames ? JSON.stringify(msg.toolNames) : void 0,
943
- createdAt: msg.createdAt instanceof Date ? msg.createdAt.toISOString() : msg.createdAt || now,
944
- updatedAt: now
945
- // Add updatedAt
946
- };
947
- });
948
- try {
949
- const batchSize = 25;
950
- const batches = [];
951
- for (let i = 0; i < messagesToSave.length; i += batchSize) {
952
- const batch = messagesToSave.slice(i, i + batchSize);
953
- batches.push(batch);
954
- }
955
- for (const batch of batches) {
956
- for (const messageData of batch) {
957
- if (!messageData.entity) {
958
- this.logger.error("Missing entity property in message data for create", { messageData });
959
- throw new Error("Internal error: Missing entity property during saveMessages");
960
- }
961
- await this.service.entities.message.create(messageData).go();
2328
+ scores: paginatedScores,
2329
+ pagination: {
2330
+ total,
2331
+ page: pagination.page,
2332
+ perPage: pagination.perPage,
2333
+ hasMore
962
2334
  }
963
- }
964
- const list = new MessageList().add(messages, "memory");
965
- if (format === `v1`) return list.get.all.v1();
966
- return list.get.all.mastra();
2335
+ };
967
2336
  } catch (error) {
968
- this.logger.error("Failed to save messages", { error });
969
- throw error;
2337
+ throw new MastraError(
2338
+ {
2339
+ id: "STORAGE_DYNAMODB_STORE_GET_SCORES_BY_RUN_ID_FAILED",
2340
+ domain: ErrorDomain.STORAGE,
2341
+ category: ErrorCategory.THIRD_PARTY,
2342
+ details: { runId, page: pagination.page, perPage: pagination.perPage }
2343
+ },
2344
+ error
2345
+ );
970
2346
  }
971
2347
  }
972
- // Helper function to parse message data (handle JSON fields)
973
- parseMessageData(data) {
974
- return {
975
- ...data,
976
- // Ensure dates are Date objects if needed (ElectroDB might return strings)
977
- createdAt: data.createdAt ? new Date(data.createdAt) : void 0,
978
- updatedAt: data.updatedAt ? new Date(data.updatedAt) : void 0
979
- // Other fields like content, toolCallArgs etc. are assumed to be correctly
980
- // transformed by the ElectroDB entity getters.
981
- };
982
- }
983
- // Trace operations
984
- async getTraces(args) {
985
- const { name, scope, page, perPage } = args;
986
- this.logger.debug("Getting traces", { name, scope, page, perPage });
2348
+ async getScoresByEntityId({
2349
+ entityId,
2350
+ entityType,
2351
+ pagination
2352
+ }) {
2353
+ this.logger.debug("Getting scores by entity ID", { entityId, entityType, pagination });
987
2354
  try {
988
- let query;
989
- if (name) {
990
- query = this.service.entities.trace.query.byName({ entity: "trace", name });
991
- } else if (scope) {
992
- query = this.service.entities.trace.query.byScope({ entity: "trace", scope });
993
- } else {
994
- this.logger.warn("Performing a scan operation on traces - consider using a more specific query");
995
- query = this.service.entities.trace.scan;
996
- }
997
- let items = [];
998
- let cursor = null;
999
- let pagesFetched = 0;
1000
- const startPage = page > 0 ? page : 1;
1001
- do {
1002
- const results = await query.go({ cursor, limit: perPage });
1003
- pagesFetched++;
1004
- if (pagesFetched === startPage) {
1005
- items = results.data;
1006
- break;
1007
- }
1008
- cursor = results.cursor;
1009
- if (!cursor && results.data.length > 0 && pagesFetched < startPage) {
1010
- break;
1011
- }
1012
- } while (cursor && pagesFetched < startPage);
1013
- return items;
2355
+ const query = this.service.entities.score.query.byEntityData({ entity: "score", entityId });
2356
+ const results = await query.go();
2357
+ let allScores = results.data.map((data) => this.parseScoreData(data));
2358
+ allScores = allScores.filter((score) => score.entityType === entityType);
2359
+ allScores.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
2360
+ const startIndex = pagination.page * pagination.perPage;
2361
+ const endIndex = startIndex + pagination.perPage;
2362
+ const paginatedScores = allScores.slice(startIndex, endIndex);
2363
+ const total = allScores.length;
2364
+ const hasMore = endIndex < total;
2365
+ return {
2366
+ scores: paginatedScores,
2367
+ pagination: {
2368
+ total,
2369
+ page: pagination.page,
2370
+ perPage: pagination.perPage,
2371
+ hasMore
2372
+ }
2373
+ };
1014
2374
  } catch (error) {
1015
- this.logger.error("Failed to get traces", { error });
1016
- throw error;
2375
+ throw new MastraError(
2376
+ {
2377
+ id: "STORAGE_DYNAMODB_STORE_GET_SCORES_BY_ENTITY_ID_FAILED",
2378
+ domain: ErrorDomain.STORAGE,
2379
+ category: ErrorCategory.THIRD_PARTY,
2380
+ details: { entityId, entityType, page: pagination.page, perPage: pagination.perPage }
2381
+ },
2382
+ error
2383
+ );
1017
2384
  }
1018
2385
  }
1019
- async batchTraceInsert({ records }) {
1020
- this.logger.debug("Batch inserting traces", { count: records.length });
1021
- if (!records.length) {
1022
- return;
1023
- }
2386
+ async getScoresBySpan({
2387
+ traceId,
2388
+ spanId,
2389
+ pagination
2390
+ }) {
2391
+ this.logger.debug("Getting scores by span", { traceId, spanId, pagination });
1024
2392
  try {
1025
- const recordsToSave = records.map((rec) => ({ entity: "trace", ...rec }));
1026
- await this.batchInsert({
1027
- tableName: TABLE_TRACES,
1028
- records: recordsToSave
1029
- // Pass records with 'entity' included
1030
- });
2393
+ const query = this.service.entities.score.query.bySpan({ entity: "score", traceId, spanId });
2394
+ const results = await query.go();
2395
+ const allScores = results.data.map((data) => this.parseScoreData(data));
2396
+ allScores.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
2397
+ const startIndex = pagination.page * pagination.perPage;
2398
+ const endIndex = startIndex + pagination.perPage;
2399
+ const paginatedScores = allScores.slice(startIndex, endIndex);
2400
+ const total = allScores.length;
2401
+ const hasMore = endIndex < total;
2402
+ return {
2403
+ scores: paginatedScores,
2404
+ pagination: {
2405
+ total,
2406
+ page: pagination.page,
2407
+ perPage: pagination.perPage,
2408
+ hasMore
2409
+ }
2410
+ };
1031
2411
  } catch (error) {
1032
- this.logger.error("Failed to batch insert traces", { error });
1033
- throw error;
2412
+ throw new MastraError(
2413
+ {
2414
+ id: "STORAGE_DYNAMODB_STORE_GET_SCORES_BY_SPAN_FAILED",
2415
+ domain: ErrorDomain.STORAGE,
2416
+ category: ErrorCategory.THIRD_PARTY,
2417
+ details: { traceId, spanId, page: pagination.page, perPage: pagination.perPage }
2418
+ },
2419
+ error
2420
+ );
1034
2421
  }
1035
2422
  }
2423
+ };
2424
+ function formatWorkflowRun(snapshotData) {
2425
+ return {
2426
+ workflowName: snapshotData.workflow_name,
2427
+ runId: snapshotData.run_id,
2428
+ snapshot: snapshotData.snapshot,
2429
+ createdAt: new Date(snapshotData.createdAt),
2430
+ updatedAt: new Date(snapshotData.updatedAt),
2431
+ resourceId: snapshotData.resourceId
2432
+ };
2433
+ }
2434
+ var WorkflowStorageDynamoDB = class extends WorkflowsStorage {
2435
+ service;
2436
+ constructor({ service }) {
2437
+ super();
2438
+ this.service = service;
2439
+ }
2440
+ updateWorkflowResults({
2441
+ // workflowName,
2442
+ // runId,
2443
+ // stepId,
2444
+ // result,
2445
+ // runtimeContext,
2446
+ }) {
2447
+ throw new Error("Method not implemented.");
2448
+ }
2449
+ updateWorkflowState({
2450
+ // workflowName,
2451
+ // runId,
2452
+ // opts,
2453
+ }) {
2454
+ throw new Error("Method not implemented.");
2455
+ }
1036
2456
  // Workflow operations
1037
2457
  async persistWorkflowSnapshot({
1038
2458
  workflowName,
1039
2459
  runId,
2460
+ resourceId,
1040
2461
  snapshot
1041
2462
  }) {
1042
2463
  this.logger.debug("Persisting workflow snapshot", { workflowName, runId });
1043
2464
  try {
1044
- const resourceId = "resourceId" in snapshot ? snapshot.resourceId : void 0;
1045
2465
  const now = (/* @__PURE__ */ new Date()).toISOString();
1046
2466
  const data = {
1047
2467
  entity: "workflow_snapshot",
@@ -1054,10 +2474,17 @@ var DynamoDBStore = class extends MastraStorage {
1054
2474
  updatedAt: now,
1055
2475
  resourceId
1056
2476
  };
1057
- await this.service.entities.workflowSnapshot.create(data).go();
2477
+ await this.service.entities.workflow_snapshot.upsert(data).go();
1058
2478
  } catch (error) {
1059
- this.logger.error("Failed to persist workflow snapshot", { workflowName, runId, error });
1060
- throw error;
2479
+ throw new MastraError(
2480
+ {
2481
+ id: "STORAGE_DYNAMODB_STORE_PERSIST_WORKFLOW_SNAPSHOT_FAILED",
2482
+ domain: ErrorDomain.STORAGE,
2483
+ category: ErrorCategory.THIRD_PARTY,
2484
+ details: { workflowName, runId }
2485
+ },
2486
+ error
2487
+ );
1061
2488
  }
1062
2489
  }
1063
2490
  async loadWorkflowSnapshot({
@@ -1066,7 +2493,7 @@ var DynamoDBStore = class extends MastraStorage {
1066
2493
  }) {
1067
2494
  this.logger.debug("Loading workflow snapshot", { workflowName, runId });
1068
2495
  try {
1069
- const result = await this.service.entities.workflowSnapshot.get({
2496
+ const result = await this.service.entities.workflow_snapshot.get({
1070
2497
  entity: "workflow_snapshot",
1071
2498
  // Add entity type
1072
2499
  workflow_name: workflowName,
@@ -1077,8 +2504,15 @@ var DynamoDBStore = class extends MastraStorage {
1077
2504
  }
1078
2505
  return result.data.snapshot;
1079
2506
  } catch (error) {
1080
- this.logger.error("Failed to load workflow snapshot", { workflowName, runId, error });
1081
- throw error;
2507
+ throw new MastraError(
2508
+ {
2509
+ id: "STORAGE_DYNAMODB_STORE_LOAD_WORKFLOW_SNAPSHOT_FAILED",
2510
+ domain: ErrorDomain.STORAGE,
2511
+ category: ErrorCategory.THIRD_PARTY,
2512
+ details: { workflowName, runId }
2513
+ },
2514
+ error
2515
+ );
1082
2516
  }
1083
2517
  }
1084
2518
  async getWorkflowRuns(args) {
@@ -1088,14 +2522,14 @@ var DynamoDBStore = class extends MastraStorage {
1088
2522
  const offset = args?.offset || 0;
1089
2523
  let query;
1090
2524
  if (args?.workflowName) {
1091
- query = this.service.entities.workflowSnapshot.query.primary({
2525
+ query = this.service.entities.workflow_snapshot.query.primary({
1092
2526
  entity: "workflow_snapshot",
1093
2527
  // Add entity type
1094
2528
  workflow_name: args.workflowName
1095
2529
  });
1096
2530
  } else {
1097
2531
  this.logger.warn("Performing a scan operation on workflow snapshots - consider using a more specific query");
1098
- query = this.service.entities.workflowSnapshot.scan;
2532
+ query = this.service.entities.workflow_snapshot.scan;
1099
2533
  }
1100
2534
  const allMatchingSnapshots = [];
1101
2535
  let cursor = null;
@@ -1133,14 +2567,21 @@ var DynamoDBStore = class extends MastraStorage {
1133
2567
  }
1134
2568
  const total = allMatchingSnapshots.length;
1135
2569
  const paginatedData = allMatchingSnapshots.slice(offset, offset + limit);
1136
- const runs = paginatedData.map((snapshot) => this.formatWorkflowRun(snapshot));
2570
+ const runs = paginatedData.map((snapshot) => formatWorkflowRun(snapshot));
1137
2571
  return {
1138
2572
  runs,
1139
2573
  total
1140
2574
  };
1141
2575
  } catch (error) {
1142
- this.logger.error("Failed to get workflow runs", { error });
1143
- throw error;
2576
+ throw new MastraError(
2577
+ {
2578
+ id: "STORAGE_DYNAMODB_STORE_GET_WORKFLOW_RUNS_FAILED",
2579
+ domain: ErrorDomain.STORAGE,
2580
+ category: ErrorCategory.THIRD_PARTY,
2581
+ details: { workflowName: args?.workflowName || "", resourceId: args?.resourceId || "" }
2582
+ },
2583
+ error
2584
+ );
1144
2585
  }
1145
2586
  }
1146
2587
  async getWorkflowRunById(args) {
@@ -1149,7 +2590,7 @@ var DynamoDBStore = class extends MastraStorage {
1149
2590
  try {
1150
2591
  if (workflowName) {
1151
2592
  this.logger.debug("WorkflowName provided, using direct GET operation.");
1152
- const result2 = await this.service.entities.workflowSnapshot.get({
2593
+ const result2 = await this.service.entities.workflow_snapshot.get({
1153
2594
  entity: "workflow_snapshot",
1154
2595
  // Entity type for PK
1155
2596
  workflow_name: workflowName,
@@ -1171,7 +2612,7 @@ var DynamoDBStore = class extends MastraStorage {
1171
2612
  this.logger.debug(
1172
2613
  'WorkflowName not provided. Attempting to find workflow run by runId using GSI. Ensure GSI (e.g., "byRunId") is defined on the workflowSnapshot entity with run_id as its key and provisioned in DynamoDB.'
1173
2614
  );
1174
- const result = await this.service.entities.workflowSnapshot.query.gsi2({ entity: "workflow_snapshot", run_id: runId }).go();
2615
+ const result = await this.service.entities.workflow_snapshot.query.gsi2({ entity: "workflow_snapshot", run_id: runId }).go();
1175
2616
  const matchingRunDbItem = result.data && result.data.length > 0 ? result.data[0] : null;
1176
2617
  if (!matchingRunDbItem) {
1177
2618
  return null;
@@ -1186,89 +2627,272 @@ var DynamoDBStore = class extends MastraStorage {
1186
2627
  resourceId: matchingRunDbItem.resourceId
1187
2628
  };
1188
2629
  } catch (error) {
1189
- this.logger.error("Failed to get workflow run by ID", { runId, workflowName, error });
1190
- throw error;
2630
+ throw new MastraError(
2631
+ {
2632
+ id: "STORAGE_DYNAMODB_STORE_GET_WORKFLOW_RUN_BY_ID_FAILED",
2633
+ domain: ErrorDomain.STORAGE,
2634
+ category: ErrorCategory.THIRD_PARTY,
2635
+ details: { runId, workflowName: args?.workflowName || "" }
2636
+ },
2637
+ error
2638
+ );
1191
2639
  }
1192
2640
  }
1193
- // Helper function to format workflow run
1194
- formatWorkflowRun(snapshotData) {
1195
- return {
1196
- workflowName: snapshotData.workflow_name,
1197
- runId: snapshotData.run_id,
1198
- snapshot: snapshotData.snapshot,
1199
- createdAt: new Date(snapshotData.createdAt),
1200
- updatedAt: new Date(snapshotData.updatedAt),
1201
- resourceId: snapshotData.resourceId
1202
- };
2641
+ };
2642
+
2643
+ // src/storage/index.ts
2644
+ var DynamoDBStore = class extends MastraStorage {
2645
+ tableName;
2646
+ client;
2647
+ service;
2648
+ hasInitialized = null;
2649
+ stores;
2650
+ constructor({ name, config }) {
2651
+ super({ name });
2652
+ try {
2653
+ if (!config.tableName || typeof config.tableName !== "string" || config.tableName.trim() === "") {
2654
+ throw new Error("DynamoDBStore: config.tableName must be provided and cannot be empty.");
2655
+ }
2656
+ if (!/^[a-zA-Z0-9_.-]{3,255}$/.test(config.tableName)) {
2657
+ throw new Error(
2658
+ `DynamoDBStore: config.tableName "${config.tableName}" contains invalid characters or is not between 3 and 255 characters long.`
2659
+ );
2660
+ }
2661
+ const dynamoClient = new DynamoDBClient({
2662
+ region: config.region || "us-east-1",
2663
+ endpoint: config.endpoint,
2664
+ credentials: config.credentials
2665
+ });
2666
+ this.tableName = config.tableName;
2667
+ this.client = DynamoDBDocumentClient.from(dynamoClient);
2668
+ this.service = getElectroDbService(this.client, this.tableName);
2669
+ const operations = new StoreOperationsDynamoDB({
2670
+ service: this.service,
2671
+ tableName: this.tableName,
2672
+ client: this.client
2673
+ });
2674
+ const workflows = new WorkflowStorageDynamoDB({ service: this.service });
2675
+ const memory = new MemoryStorageDynamoDB({ service: this.service });
2676
+ const scores = new ScoresStorageDynamoDB({ service: this.service });
2677
+ this.stores = {
2678
+ operations,
2679
+ legacyEvals: new LegacyEvalsDynamoDB({ service: this.service, tableName: this.tableName }),
2680
+ workflows,
2681
+ memory,
2682
+ scores
2683
+ };
2684
+ } catch (error) {
2685
+ throw new MastraError(
2686
+ {
2687
+ id: "STORAGE_DYNAMODB_STORE_CONSTRUCTOR_FAILED",
2688
+ domain: ErrorDomain.STORAGE,
2689
+ category: ErrorCategory.USER
2690
+ },
2691
+ error
2692
+ );
2693
+ }
1203
2694
  }
1204
- // Helper methods for entity/table mapping
1205
- getEntityNameForTable(tableName) {
1206
- const mapping = {
1207
- [TABLE_THREADS]: "thread",
1208
- [TABLE_MESSAGES]: "message",
1209
- [TABLE_WORKFLOW_SNAPSHOT]: "workflowSnapshot",
1210
- [TABLE_EVALS]: "eval",
1211
- [TABLE_TRACES]: "trace"
2695
+ get supports() {
2696
+ return {
2697
+ selectByIncludeResourceScope: true,
2698
+ resourceWorkingMemory: true,
2699
+ hasColumn: false,
2700
+ createTable: false,
2701
+ deleteMessages: false,
2702
+ getScoresBySpan: true
1212
2703
  };
1213
- return mapping[tableName] || null;
1214
2704
  }
1215
- // Eval operations
1216
- async getEvalsByAgentName(agentName, type) {
1217
- this.logger.debug("Getting evals for agent", { agentName, type });
2705
+ /**
2706
+ * Validates that the required DynamoDB table exists and is accessible.
2707
+ * This does not check the table structure - it assumes the table
2708
+ * was created with the correct structure via CDK/CloudFormation.
2709
+ */
2710
+ async validateTableExists() {
1218
2711
  try {
1219
- const query = this.service.entities.eval.query.byAgent({ entity: "eval", agent_name: agentName });
1220
- const results = await query.go({ order: "desc", limit: 100 });
1221
- if (!results.data.length) {
1222
- return [];
1223
- }
1224
- let filteredData = results.data;
1225
- if (type) {
1226
- filteredData = filteredData.filter((evalRecord) => {
1227
- try {
1228
- const testInfo = evalRecord.test_info && typeof evalRecord.test_info === "string" ? JSON.parse(evalRecord.test_info) : void 0;
1229
- if (type === "test" && !testInfo) {
1230
- return false;
1231
- }
1232
- if (type === "live" && testInfo) {
1233
- return false;
1234
- }
1235
- } catch (e) {
1236
- this.logger.warn("Failed to parse test_info during filtering", { record: evalRecord, error: e });
1237
- }
1238
- return true;
1239
- });
1240
- }
1241
- return filteredData.map((evalRecord) => {
1242
- try {
1243
- return {
1244
- input: evalRecord.input,
1245
- output: evalRecord.output,
1246
- // Safely parse result and test_info
1247
- result: evalRecord.result && typeof evalRecord.result === "string" ? JSON.parse(evalRecord.result) : void 0,
1248
- agentName: evalRecord.agent_name,
1249
- createdAt: evalRecord.created_at,
1250
- // Keep as string from DDB?
1251
- metricName: evalRecord.metric_name,
1252
- instructions: evalRecord.instructions,
1253
- runId: evalRecord.run_id,
1254
- globalRunId: evalRecord.global_run_id,
1255
- testInfo: evalRecord.test_info && typeof evalRecord.test_info === "string" ? JSON.parse(evalRecord.test_info) : void 0
1256
- };
1257
- } catch (parseError) {
1258
- this.logger.error("Failed to parse eval record", { record: evalRecord, error: parseError });
1259
- return {
1260
- agentName: evalRecord.agent_name,
1261
- createdAt: evalRecord.created_at,
1262
- runId: evalRecord.run_id,
1263
- globalRunId: evalRecord.global_run_id
1264
- };
1265
- }
2712
+ const command = new DescribeTableCommand({
2713
+ TableName: this.tableName
1266
2714
  });
2715
+ await this.client.send(command);
2716
+ return true;
2717
+ } catch (error) {
2718
+ if (error.name === "ResourceNotFoundException") {
2719
+ return false;
2720
+ }
2721
+ throw new MastraError(
2722
+ {
2723
+ id: "STORAGE_DYNAMODB_STORE_VALIDATE_TABLE_EXISTS_FAILED",
2724
+ domain: ErrorDomain.STORAGE,
2725
+ category: ErrorCategory.THIRD_PARTY,
2726
+ details: { tableName: this.tableName }
2727
+ },
2728
+ error
2729
+ );
2730
+ }
2731
+ }
2732
+ /**
2733
+ * Initialize storage, validating the externally managed table is accessible.
2734
+ * For the single-table design, we only validate once that we can access
2735
+ * the table that was created via CDK/CloudFormation.
2736
+ */
2737
+ async init() {
2738
+ if (this.hasInitialized === null) {
2739
+ this.hasInitialized = this._performInitializationAndStore();
2740
+ }
2741
+ try {
2742
+ await this.hasInitialized;
1267
2743
  } catch (error) {
1268
- this.logger.error("Failed to get evals by agent name", { agentName, type, error });
1269
- throw error;
2744
+ throw new MastraError(
2745
+ {
2746
+ id: "STORAGE_DYNAMODB_STORE_INIT_FAILED",
2747
+ domain: ErrorDomain.STORAGE,
2748
+ category: ErrorCategory.THIRD_PARTY,
2749
+ details: { tableName: this.tableName }
2750
+ },
2751
+ error
2752
+ );
1270
2753
  }
1271
2754
  }
2755
+ /**
2756
+ * Performs the actual table validation and stores the promise.
2757
+ * Handles resetting the stored promise on failure to allow retries.
2758
+ */
2759
+ _performInitializationAndStore() {
2760
+ return this.validateTableExists().then((exists) => {
2761
+ if (!exists) {
2762
+ throw new Error(
2763
+ `Table ${this.tableName} does not exist or is not accessible. Ensure it's created via CDK/CloudFormation before using this store.`
2764
+ );
2765
+ }
2766
+ return true;
2767
+ }).catch((err) => {
2768
+ this.hasInitialized = null;
2769
+ throw err;
2770
+ });
2771
+ }
2772
+ async createTable({ tableName, schema }) {
2773
+ return this.stores.operations.createTable({ tableName, schema });
2774
+ }
2775
+ async alterTable(_args) {
2776
+ return this.stores.operations.alterTable(_args);
2777
+ }
2778
+ async clearTable({ tableName }) {
2779
+ return this.stores.operations.clearTable({ tableName });
2780
+ }
2781
+ async dropTable({ tableName }) {
2782
+ return this.stores.operations.dropTable({ tableName });
2783
+ }
2784
+ async insert({ tableName, record }) {
2785
+ return this.stores.operations.insert({ tableName, record });
2786
+ }
2787
+ async batchInsert({ tableName, records }) {
2788
+ return this.stores.operations.batchInsert({ tableName, records });
2789
+ }
2790
+ async load({ tableName, keys }) {
2791
+ return this.stores.operations.load({ tableName, keys });
2792
+ }
2793
+ // Thread operations
2794
+ async getThreadById({ threadId }) {
2795
+ return this.stores.memory.getThreadById({ threadId });
2796
+ }
2797
+ async getThreadsByResourceId(args) {
2798
+ return this.stores.memory.getThreadsByResourceId(args);
2799
+ }
2800
+ async saveThread({ thread }) {
2801
+ return this.stores.memory.saveThread({ thread });
2802
+ }
2803
+ async updateThread({
2804
+ id,
2805
+ title,
2806
+ metadata
2807
+ }) {
2808
+ return this.stores.memory.updateThread({ id, title, metadata });
2809
+ }
2810
+ async deleteThread({ threadId }) {
2811
+ return this.stores.memory.deleteThread({ threadId });
2812
+ }
2813
+ async getMessages({
2814
+ threadId,
2815
+ resourceId,
2816
+ selectBy,
2817
+ format
2818
+ }) {
2819
+ return this.stores.memory.getMessages({ threadId, resourceId, selectBy, format });
2820
+ }
2821
+ async getMessagesById({
2822
+ messageIds,
2823
+ format
2824
+ }) {
2825
+ return this.stores.memory.getMessagesById({ messageIds, format });
2826
+ }
2827
+ async saveMessages(args) {
2828
+ return this.stores.memory.saveMessages(args);
2829
+ }
2830
+ async getThreadsByResourceIdPaginated(args) {
2831
+ return this.stores.memory.getThreadsByResourceIdPaginated(args);
2832
+ }
2833
+ async getMessagesPaginated(args) {
2834
+ return this.stores.memory.getMessagesPaginated(args);
2835
+ }
2836
+ async updateMessages(_args) {
2837
+ return this.stores.memory.updateMessages(_args);
2838
+ }
2839
+ // Workflow operations
2840
+ async updateWorkflowResults({
2841
+ workflowName,
2842
+ runId,
2843
+ stepId,
2844
+ result,
2845
+ runtimeContext
2846
+ }) {
2847
+ return this.stores.workflows.updateWorkflowResults({ workflowName, runId, stepId, result, runtimeContext });
2848
+ }
2849
+ async updateWorkflowState({
2850
+ workflowName,
2851
+ runId,
2852
+ opts
2853
+ }) {
2854
+ return this.stores.workflows.updateWorkflowState({ workflowName, runId, opts });
2855
+ }
2856
+ async persistWorkflowSnapshot({
2857
+ workflowName,
2858
+ runId,
2859
+ resourceId,
2860
+ snapshot
2861
+ }) {
2862
+ return this.stores.workflows.persistWorkflowSnapshot({ workflowName, runId, resourceId, snapshot });
2863
+ }
2864
+ async loadWorkflowSnapshot({
2865
+ workflowName,
2866
+ runId
2867
+ }) {
2868
+ return this.stores.workflows.loadWorkflowSnapshot({ workflowName, runId });
2869
+ }
2870
+ async getWorkflowRuns(args) {
2871
+ return this.stores.workflows.getWorkflowRuns(args);
2872
+ }
2873
+ async getWorkflowRunById(args) {
2874
+ return this.stores.workflows.getWorkflowRunById(args);
2875
+ }
2876
+ async getResourceById({ resourceId }) {
2877
+ return this.stores.memory.getResourceById({ resourceId });
2878
+ }
2879
+ async saveResource({ resource }) {
2880
+ return this.stores.memory.saveResource({ resource });
2881
+ }
2882
+ async updateResource({
2883
+ resourceId,
2884
+ workingMemory,
2885
+ metadata
2886
+ }) {
2887
+ return this.stores.memory.updateResource({ resourceId, workingMemory, metadata });
2888
+ }
2889
+ // Eval operations
2890
+ async getEvalsByAgentName(agentName, type) {
2891
+ return this.stores.legacyEvals.getEvalsByAgentName(agentName, type);
2892
+ }
2893
+ async getEvals(options) {
2894
+ return this.stores.legacyEvals.getEvals(options);
2895
+ }
1272
2896
  /**
1273
2897
  * Closes the DynamoDB client connection and cleans up resources.
1274
2898
  * Should be called when the store is no longer needed, e.g., at the end of tests or application shutdown.
@@ -1279,10 +2903,60 @@ var DynamoDBStore = class extends MastraStorage {
1279
2903
  this.client.destroy();
1280
2904
  this.logger.debug("DynamoDB client closed successfully for store:", { name: this.name });
1281
2905
  } catch (error) {
1282
- this.logger.error("Error closing DynamoDB client for store:", { name: this.name, error });
1283
- throw error;
2906
+ throw new MastraError(
2907
+ {
2908
+ id: "STORAGE_DYNAMODB_STORE_CLOSE_FAILED",
2909
+ domain: ErrorDomain.STORAGE,
2910
+ category: ErrorCategory.THIRD_PARTY
2911
+ },
2912
+ error
2913
+ );
1284
2914
  }
1285
2915
  }
2916
+ /**
2917
+ * SCORERS - Not implemented
2918
+ */
2919
+ async getScoreById({ id: _id }) {
2920
+ return this.stores.scores.getScoreById({ id: _id });
2921
+ }
2922
+ async saveScore(_score) {
2923
+ return this.stores.scores.saveScore(_score);
2924
+ }
2925
+ async getScoresByRunId({
2926
+ runId: _runId,
2927
+ pagination: _pagination
2928
+ }) {
2929
+ return this.stores.scores.getScoresByRunId({ runId: _runId, pagination: _pagination });
2930
+ }
2931
+ async getScoresByEntityId({
2932
+ entityId: _entityId,
2933
+ entityType: _entityType,
2934
+ pagination: _pagination
2935
+ }) {
2936
+ return this.stores.scores.getScoresByEntityId({
2937
+ entityId: _entityId,
2938
+ entityType: _entityType,
2939
+ pagination: _pagination
2940
+ });
2941
+ }
2942
+ async getScoresByScorerId({
2943
+ scorerId,
2944
+ source,
2945
+ entityId,
2946
+ entityType,
2947
+ pagination
2948
+ }) {
2949
+ return this.stores.scores.getScoresByScorerId({ scorerId, source, entityId, entityType, pagination });
2950
+ }
2951
+ async getScoresBySpan({
2952
+ traceId,
2953
+ spanId,
2954
+ pagination
2955
+ }) {
2956
+ return this.stores.scores.getScoresBySpan({ traceId, spanId, pagination });
2957
+ }
1286
2958
  };
1287
2959
 
1288
2960
  export { DynamoDBStore };
2961
+ //# sourceMappingURL=index.js.map
2962
+ //# sourceMappingURL=index.js.map