@mastra/dynamodb 0.0.0-share-agent-metadata-with-cloud-20250718123411 → 0.0.0-span-scorring-test-20251124132129

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 +1484 -0
  2. package/README.md +0 -4
  3. package/dist/entities/eval.d.ts +102 -0
  4. package/dist/entities/eval.d.ts.map +1 -0
  5. package/dist/entities/index.d.ts +761 -0
  6. package/dist/entities/index.d.ts.map +1 -0
  7. package/dist/entities/message.d.ts +100 -0
  8. package/dist/entities/message.d.ts.map +1 -0
  9. package/dist/entities/resource.d.ts +54 -0
  10. package/dist/entities/resource.d.ts.map +1 -0
  11. package/dist/entities/score.d.ts +244 -0
  12. package/dist/entities/score.d.ts.map +1 -0
  13. package/dist/entities/thread.d.ts +69 -0
  14. package/dist/entities/thread.d.ts.map +1 -0
  15. package/dist/entities/trace.d.ts +127 -0
  16. package/dist/entities/trace.d.ts.map +1 -0
  17. package/dist/entities/utils.d.ts +21 -0
  18. package/dist/entities/utils.d.ts.map +1 -0
  19. package/dist/entities/workflow-snapshot.d.ts +74 -0
  20. package/dist/entities/workflow-snapshot.d.ts.map +1 -0
  21. package/dist/index.cjs +1754 -566
  22. package/dist/index.cjs.map +1 -0
  23. package/dist/index.d.ts +2 -2
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +1755 -567
  26. package/dist/index.js.map +1 -0
  27. package/dist/storage/domains/memory/index.d.ts +61 -0
  28. package/dist/storage/domains/memory/index.d.ts.map +1 -0
  29. package/dist/storage/domains/operations/index.d.ts +69 -0
  30. package/dist/storage/domains/operations/index.d.ts.map +1 -0
  31. package/dist/storage/domains/score/index.d.ts +51 -0
  32. package/dist/storage/domains/score/index.d.ts.map +1 -0
  33. package/dist/storage/domains/workflows/index.d.ts +44 -0
  34. package/dist/storage/domains/workflows/index.d.ts.map +1 -0
  35. package/dist/storage/index.d.ts +204 -0
  36. package/dist/storage/index.d.ts.map +1 -0
  37. package/package.json +31 -18
  38. package/dist/_tsup-dts-rollup.d.cts +0 -1160
  39. package/dist/_tsup-dts-rollup.d.ts +0 -1160
  40. package/dist/index.d.cts +0 -2
  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 -1483
  51. package/src/storage/index.ts +0 -1383
package/dist/index.js CHANGED
@@ -1,9 +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
3
  import { MastraError, ErrorCategory, ErrorDomain } from '@mastra/core/error';
5
- import { MastraStorage, TABLE_TRACES, TABLE_EVALS, TABLE_WORKFLOW_SNAPSHOT, TABLE_MESSAGES, TABLE_THREADS } from '@mastra/core/storage';
4
+ import { MastraStorage, StoreOperations, WorkflowsStorage, normalizePerPage, MemoryStorage, calculatePagination, ScoresStorage, TABLE_SPANS, TABLE_RESOURCES, TABLE_TRACES, TABLE_SCORERS, TABLE_WORKFLOW_SNAPSHOT, TABLE_MESSAGES, TABLE_THREADS } from '@mastra/core/storage';
6
5
  import { Entity, Service } from 'electrodb';
6
+ import { MessageList } from '@mastra/core/agent';
7
+ import { saveScorePayloadSchema } from '@mastra/core/evals';
7
8
 
8
9
  // src/storage/index.ts
9
10
 
@@ -294,9 +295,9 @@ var messageEntity = new Entity({
294
295
  }
295
296
  }
296
297
  });
297
- var threadEntity = new Entity({
298
+ var resourceEntity = new Entity({
298
299
  model: {
299
- entity: "thread",
300
+ entity: "resource",
300
301
  version: "1",
301
302
  service: "mastra"
302
303
  },
@@ -310,25 +311,21 @@ var threadEntity = new Entity({
310
311
  type: "string",
311
312
  required: true
312
313
  },
313
- resourceId: {
314
- type: "string",
315
- required: true
316
- },
317
- title: {
314
+ workingMemory: {
318
315
  type: "string",
319
- required: true
316
+ required: false
320
317
  },
321
318
  metadata: {
322
319
  type: "string",
323
320
  required: false,
324
- // 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
325
322
  set: (value) => {
326
323
  if (value && typeof value !== "string") {
327
324
  return JSON.stringify(value);
328
325
  }
329
326
  return value;
330
327
  },
331
- // Parse JSON string to object on get
328
+ // Parse JSON string to object on get ONLY if it looks like JSON
332
329
  get: (value) => {
333
330
  if (value && typeof value === "string") {
334
331
  try {
@@ -346,18 +343,13 @@ var threadEntity = new Entity({
346
343
  indexes: {
347
344
  primary: {
348
345
  pk: { field: "pk", composite: ["entity", "id"] },
349
- sk: { field: "sk", composite: ["id"] }
350
- },
351
- byResource: {
352
- index: "gsi1",
353
- pk: { field: "gsi1pk", composite: ["entity", "resourceId"] },
354
- sk: { field: "gsi1sk", composite: ["createdAt"] }
346
+ sk: { field: "sk", composite: ["entity"] }
355
347
  }
356
348
  }
357
349
  });
358
- var traceEntity = new Entity({
350
+ var scoreEntity = new Entity({
359
351
  model: {
360
- entity: "trace",
352
+ entity: "score",
361
353
  version: "1",
362
354
  service: "mastra"
363
355
  },
@@ -371,123 +363,315 @@ var traceEntity = new Entity({
371
363
  type: "string",
372
364
  required: true
373
365
  },
374
- parentSpanId: {
366
+ scorerId: {
367
+ type: "string",
368
+ required: true
369
+ },
370
+ traceId: {
375
371
  type: "string",
376
372
  required: false
377
373
  },
378
- name: {
374
+ spanId: {
379
375
  type: "string",
380
- required: true
376
+ required: false
381
377
  },
382
- traceId: {
378
+ runId: {
383
379
  type: "string",
384
380
  required: true
385
381
  },
386
- scope: {
382
+ scorer: {
387
383
  type: "string",
388
- 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
+ }
389
403
  },
390
- 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: {
391
471
  type: "number",
392
472
  required: true
393
473
  },
394
- attributes: {
474
+ reason: {
395
475
  type: "string",
396
- // JSON stringified
397
- required: false,
398
- // 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,
399
502
  set: (value) => {
400
503
  if (value && typeof value !== "string") {
401
504
  return JSON.stringify(value);
402
505
  }
403
506
  return value;
404
507
  },
405
- // Parse JSON string to object on get
406
508
  get: (value) => {
407
- 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;
408
519
  }
409
520
  },
410
- 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: {
411
544
  type: "string",
412
- // JSON stringified
413
545
  required: false,
414
- // Stringify object on set
415
546
  set: (value) => {
416
547
  if (value && typeof value !== "string") {
417
548
  return JSON.stringify(value);
418
549
  }
419
550
  return value;
420
551
  },
421
- // Parse JSON string to object on get
422
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
+ }
423
562
  return value;
424
563
  }
425
564
  },
426
- events: {
565
+ requestContext: {
427
566
  type: "string",
428
- // JSON stringified
429
567
  required: false,
430
- // Stringify object on set
431
568
  set: (value) => {
432
569
  if (value && typeof value !== "string") {
433
570
  return JSON.stringify(value);
434
571
  }
435
572
  return value;
436
573
  },
437
- // Parse JSON string to object on get
438
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
+ }
439
584
  return value;
440
585
  }
441
586
  },
442
- links: {
587
+ entityType: {
588
+ type: "string",
589
+ required: false
590
+ },
591
+ entityData: {
443
592
  type: "string",
444
- // JSON stringified
445
593
  required: false,
446
- // Stringify object on set
447
594
  set: (value) => {
448
595
  if (value && typeof value !== "string") {
449
596
  return JSON.stringify(value);
450
597
  }
451
598
  return value;
452
599
  },
453
- // Parse JSON string to object on get
454
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
+ }
455
610
  return value;
456
611
  }
457
612
  },
458
- other: {
613
+ entityId: {
459
614
  type: "string",
460
615
  required: false
461
616
  },
462
- startTime: {
463
- type: "number",
617
+ source: {
618
+ type: "string",
464
619
  required: true
465
620
  },
466
- endTime: {
467
- type: "number",
468
- required: true
621
+ resourceId: {
622
+ type: "string",
623
+ required: false
624
+ },
625
+ threadId: {
626
+ type: "string",
627
+ required: false
469
628
  }
470
629
  },
471
630
  indexes: {
472
631
  primary: {
473
632
  pk: { field: "pk", composite: ["entity", "id"] },
474
- sk: { field: "sk", composite: [] }
633
+ sk: { field: "sk", composite: ["entity"] }
475
634
  },
476
- byName: {
635
+ byScorer: {
477
636
  index: "gsi1",
478
- pk: { field: "gsi1pk", composite: ["entity", "name"] },
479
- sk: { field: "gsi1sk", composite: ["startTime"] }
637
+ pk: { field: "gsi1pk", composite: ["entity", "scorerId"] },
638
+ sk: { field: "gsi1sk", composite: ["createdAt"] }
480
639
  },
481
- byScope: {
640
+ byRun: {
482
641
  index: "gsi2",
483
- pk: { field: "gsi2pk", composite: ["entity", "scope"] },
484
- 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"] }
485
669
  }
486
670
  }
487
671
  });
488
- var workflowSnapshotEntity = new Entity({
672
+ var threadEntity = new Entity({
489
673
  model: {
490
- entity: "workflow_snapshot",
674
+ entity: "thread",
491
675
  version: "1",
492
676
  service: "mastra"
493
677
  },
@@ -497,11 +681,202 @@ var workflowSnapshotEntity = new Entity({
497
681
  required: true
498
682
  },
499
683
  ...baseAttributes,
500
- workflow_name: {
684
+ id: {
501
685
  type: "string",
502
686
  required: true
503
687
  },
504
- 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: {
505
880
  type: "string",
506
881
  required: true
507
882
  },
@@ -548,7 +923,9 @@ function getElectroDbService(client, tableName) {
548
923
  message: messageEntity,
549
924
  eval: evalEntity,
550
925
  trace: traceEntity,
551
- workflowSnapshot: workflowSnapshotEntity
926
+ workflow_snapshot: workflowSnapshotEntity,
927
+ resource: resourceEntity,
928
+ score: scoreEntity
552
929
  },
553
930
  {
554
931
  client,
@@ -556,76 +933,771 @@ function getElectroDbService(client, tableName) {
556
933
  }
557
934
  );
558
935
  }
559
-
560
- // src/storage/index.ts
561
- var DynamoDBStore = class extends MastraStorage {
562
- tableName;
563
- client;
936
+ var MemoryStorageDynamoDB = class extends MemoryStorage {
564
937
  service;
565
- hasInitialized = null;
566
- constructor({ name, config }) {
567
- super({ name });
938
+ constructor({ service }) {
939
+ super();
940
+ this.service = service;
941
+ }
942
+ // Helper function to parse message data (handle JSON fields)
943
+ parseMessageData(data) {
944
+ return {
945
+ ...data,
946
+ // Ensure dates are Date objects if needed (ElectroDB might return strings)
947
+ createdAt: data.createdAt ? new Date(data.createdAt) : void 0,
948
+ updatedAt: data.updatedAt ? new Date(data.updatedAt) : void 0
949
+ // Other fields like content, toolCallArgs etc. are assumed to be correctly
950
+ // transformed by the ElectroDB entity getters.
951
+ };
952
+ }
953
+ // Helper function to transform and sort threads
954
+ transformAndSortThreads(rawThreads, field, direction) {
955
+ return rawThreads.map((data) => ({
956
+ ...data,
957
+ // Convert date strings back to Date objects for consistency
958
+ createdAt: typeof data.createdAt === "string" ? new Date(data.createdAt) : data.createdAt,
959
+ updatedAt: typeof data.updatedAt === "string" ? new Date(data.updatedAt) : data.updatedAt
960
+ })).sort((a, b) => {
961
+ const fieldA = field === "createdAt" ? a.createdAt : a.updatedAt;
962
+ const fieldB = field === "createdAt" ? b.createdAt : b.updatedAt;
963
+ const comparison = fieldA.getTime() - fieldB.getTime();
964
+ return direction === "DESC" ? -comparison : comparison;
965
+ });
966
+ }
967
+ async getThreadById({ threadId }) {
968
+ this.logger.debug("Getting thread by ID", { threadId });
568
969
  try {
569
- if (!config.tableName || typeof config.tableName !== "string" || config.tableName.trim() === "") {
570
- throw new Error("DynamoDBStore: config.tableName must be provided and cannot be empty.");
571
- }
572
- if (!/^[a-zA-Z0-9_.-]{3,255}$/.test(config.tableName)) {
573
- throw new Error(
574
- `DynamoDBStore: config.tableName "${config.tableName}" contains invalid characters or is not between 3 and 255 characters long.`
575
- );
970
+ const result = await this.service.entities.thread.get({ entity: "thread", id: threadId }).go();
971
+ if (!result.data) {
972
+ return null;
576
973
  }
577
- const dynamoClient = new DynamoDBClient({
578
- region: config.region || "us-east-1",
579
- endpoint: config.endpoint,
580
- credentials: config.credentials
581
- });
582
- this.tableName = config.tableName;
583
- this.client = DynamoDBDocumentClient.from(dynamoClient);
584
- this.service = getElectroDbService(this.client, this.tableName);
974
+ const data = result.data;
975
+ return {
976
+ ...data,
977
+ // Convert date strings back to Date objects for consistency
978
+ createdAt: typeof data.createdAt === "string" ? new Date(data.createdAt) : data.createdAt,
979
+ updatedAt: typeof data.updatedAt === "string" ? new Date(data.updatedAt) : data.updatedAt
980
+ // metadata: data.metadata ? JSON.parse(data.metadata) : undefined, // REMOVED by AI
981
+ // metadata is already transformed by the entity's getter
982
+ };
585
983
  } catch (error) {
586
984
  throw new MastraError(
587
985
  {
588
- id: "STORAGE_DYNAMODB_STORE_CONSTRUCTOR_FAILED",
986
+ id: "STORAGE_DYNAMODB_STORE_GET_THREAD_BY_ID_FAILED",
589
987
  domain: ErrorDomain.STORAGE,
590
- category: ErrorCategory.USER
988
+ category: ErrorCategory.THIRD_PARTY,
989
+ details: { threadId }
591
990
  },
592
991
  error
593
992
  );
594
993
  }
595
994
  }
596
- /**
597
- * This method is modified for DynamoDB with ElectroDB single-table design.
598
- * It assumes the table is created and managed externally via CDK/CloudFormation.
599
- *
600
- * This implementation only validates that the required table exists and is accessible.
601
- * No table creation is attempted - we simply check if we can access the table.
602
- */
603
- async createTable({ tableName }) {
604
- this.logger.debug("Validating access to externally managed table", { tableName, physicalTable: this.tableName });
995
+ async saveThread({ thread }) {
996
+ this.logger.debug("Saving thread", { threadId: thread.id });
997
+ const now = /* @__PURE__ */ new Date();
998
+ const threadData = {
999
+ entity: "thread",
1000
+ id: thread.id,
1001
+ resourceId: thread.resourceId,
1002
+ title: thread.title || `Thread ${thread.id}`,
1003
+ createdAt: thread.createdAt?.toISOString() || now.toISOString(),
1004
+ updatedAt: thread.updatedAt?.toISOString() || now.toISOString(),
1005
+ metadata: thread.metadata ? JSON.stringify(thread.metadata) : void 0
1006
+ };
605
1007
  try {
606
- const tableExists = await this.validateTableExists();
607
- if (!tableExists) {
608
- this.logger.error(
609
- `Table ${this.tableName} does not exist or is not accessible. It should be created via CDK/CloudFormation.`
610
- );
611
- throw new Error(
612
- `Table ${this.tableName} does not exist or is not accessible. Ensure it's created via CDK/CloudFormation before using this store.`
1008
+ await this.service.entities.thread.upsert(threadData).go();
1009
+ return {
1010
+ id: thread.id,
1011
+ resourceId: thread.resourceId,
1012
+ title: threadData.title,
1013
+ createdAt: thread.createdAt || now,
1014
+ updatedAt: thread.updatedAt || now,
1015
+ metadata: thread.metadata
1016
+ };
1017
+ } catch (error) {
1018
+ throw new MastraError(
1019
+ {
1020
+ id: "STORAGE_DYNAMODB_STORE_SAVE_THREAD_FAILED",
1021
+ domain: ErrorDomain.STORAGE,
1022
+ category: ErrorCategory.THIRD_PARTY,
1023
+ details: { threadId: thread.id }
1024
+ },
1025
+ error
1026
+ );
1027
+ }
1028
+ }
1029
+ async updateThread({
1030
+ id,
1031
+ title,
1032
+ metadata
1033
+ }) {
1034
+ this.logger.debug("Updating thread", { threadId: id });
1035
+ try {
1036
+ const existingThread = await this.getThreadById({ threadId: id });
1037
+ if (!existingThread) {
1038
+ throw new Error(`Thread not found: ${id}`);
1039
+ }
1040
+ const now = /* @__PURE__ */ new Date();
1041
+ const updateData = {
1042
+ updatedAt: now.toISOString()
1043
+ };
1044
+ if (title) {
1045
+ updateData.title = title;
1046
+ }
1047
+ if (metadata) {
1048
+ const existingMetadata = existingThread.metadata ? typeof existingThread.metadata === "string" ? JSON.parse(existingThread.metadata) : existingThread.metadata : {};
1049
+ const mergedMetadata = { ...existingMetadata, ...metadata };
1050
+ updateData.metadata = JSON.stringify(mergedMetadata);
1051
+ }
1052
+ await this.service.entities.thread.update({ entity: "thread", id }).set(updateData).go();
1053
+ return {
1054
+ ...existingThread,
1055
+ title: title || existingThread.title,
1056
+ metadata: metadata ? { ...existingThread.metadata, ...metadata } : existingThread.metadata,
1057
+ updatedAt: now
1058
+ };
1059
+ } catch (error) {
1060
+ throw new MastraError(
1061
+ {
1062
+ id: "STORAGE_DYNAMODB_STORE_UPDATE_THREAD_FAILED",
1063
+ domain: ErrorDomain.STORAGE,
1064
+ category: ErrorCategory.THIRD_PARTY,
1065
+ details: { threadId: id }
1066
+ },
1067
+ error
1068
+ );
1069
+ }
1070
+ }
1071
+ async deleteThread({ threadId }) {
1072
+ this.logger.debug("Deleting thread", { threadId });
1073
+ try {
1074
+ const { messages } = await this.listMessages({ threadId, perPage: false });
1075
+ if (messages.length > 0) {
1076
+ const batchSize = 25;
1077
+ for (let i = 0; i < messages.length; i += batchSize) {
1078
+ const batch = messages.slice(i, i + batchSize);
1079
+ await Promise.all(
1080
+ batch.map(
1081
+ (message) => this.service.entities.message.delete({
1082
+ entity: "message",
1083
+ id: message.id,
1084
+ threadId: message.threadId
1085
+ }).go()
1086
+ )
1087
+ );
1088
+ }
1089
+ }
1090
+ await this.service.entities.thread.delete({ entity: "thread", id: threadId }).go();
1091
+ } catch (error) {
1092
+ throw new MastraError(
1093
+ {
1094
+ id: "STORAGE_DYNAMODB_STORE_DELETE_THREAD_FAILED",
1095
+ domain: ErrorDomain.STORAGE,
1096
+ category: ErrorCategory.THIRD_PARTY,
1097
+ details: { threadId }
1098
+ },
1099
+ error
1100
+ );
1101
+ }
1102
+ }
1103
+ async listMessagesById({ messageIds }) {
1104
+ this.logger.debug("Getting messages by ID", { messageIds });
1105
+ if (messageIds.length === 0) return { messages: [] };
1106
+ try {
1107
+ const results = await Promise.all(
1108
+ messageIds.map((id) => this.service.entities.message.query.primary({ entity: "message", id }).go())
1109
+ );
1110
+ const data = results.map((result) => result.data).flat(1);
1111
+ let parsedMessages = data.map((data2) => this.parseMessageData(data2)).filter((msg) => "content" in msg);
1112
+ const uniqueMessages = parsedMessages.filter(
1113
+ (message, index, self) => index === self.findIndex((m) => m.id === message.id)
1114
+ );
1115
+ const list = new MessageList().add(uniqueMessages, "memory");
1116
+ return { messages: list.get.all.db() };
1117
+ } catch (error) {
1118
+ throw new MastraError(
1119
+ {
1120
+ id: "STORAGE_DYNAMODB_STORE_LIST_MESSAGES_BY_ID_FAILED",
1121
+ domain: ErrorDomain.STORAGE,
1122
+ category: ErrorCategory.THIRD_PARTY,
1123
+ details: { messageIds: JSON.stringify(messageIds) }
1124
+ },
1125
+ error
1126
+ );
1127
+ }
1128
+ }
1129
+ async listMessages(args) {
1130
+ const { threadId, resourceId, include, filter, perPage: perPageInput, page = 0, orderBy } = args;
1131
+ if (!threadId.trim()) {
1132
+ throw new MastraError(
1133
+ {
1134
+ id: "STORAGE_DYNAMODB_LIST_MESSAGES_INVALID_THREAD_ID",
1135
+ domain: ErrorDomain.STORAGE,
1136
+ category: ErrorCategory.THIRD_PARTY,
1137
+ details: { threadId }
1138
+ },
1139
+ new Error("threadId must be a non-empty string")
1140
+ );
1141
+ }
1142
+ const perPage = normalizePerPage(perPageInput, 40);
1143
+ const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
1144
+ try {
1145
+ if (page < 0) {
1146
+ throw new MastraError(
1147
+ {
1148
+ id: "STORAGE_DYNAMODB_LIST_MESSAGES_INVALID_PAGE",
1149
+ domain: ErrorDomain.STORAGE,
1150
+ category: ErrorCategory.USER,
1151
+ details: { page }
1152
+ },
1153
+ new Error("page must be >= 0")
613
1154
  );
614
1155
  }
615
- this.logger.debug(`Table ${this.tableName} exists and is accessible`);
1156
+ const { field, direction } = this.parseOrderBy(orderBy, "ASC");
1157
+ this.logger.debug("Getting messages with listMessages", {
1158
+ threadId,
1159
+ resourceId,
1160
+ perPageInput,
1161
+ offset,
1162
+ perPage,
1163
+ page,
1164
+ field,
1165
+ direction
1166
+ });
1167
+ const query = this.service.entities.message.query.byThread({ entity: "message", threadId });
1168
+ const results = await query.go();
1169
+ let allThreadMessages = results.data.map((data) => this.parseMessageData(data)).filter((msg) => "content" in msg && typeof msg.content === "object");
1170
+ if (resourceId) {
1171
+ allThreadMessages = allThreadMessages.filter((msg) => msg.resourceId === resourceId);
1172
+ }
1173
+ if (filter?.dateRange) {
1174
+ const dateRange = filter.dateRange;
1175
+ allThreadMessages = allThreadMessages.filter((msg) => {
1176
+ const createdAt = new Date(msg.createdAt).getTime();
1177
+ if (dateRange.start) {
1178
+ const startTime = dateRange.start instanceof Date ? dateRange.start.getTime() : new Date(dateRange.start).getTime();
1179
+ if (createdAt < startTime) return false;
1180
+ }
1181
+ if (dateRange.end) {
1182
+ const endTime = dateRange.end instanceof Date ? dateRange.end.getTime() : new Date(dateRange.end).getTime();
1183
+ if (createdAt > endTime) return false;
1184
+ }
1185
+ return true;
1186
+ });
1187
+ }
1188
+ allThreadMessages.sort((a, b) => {
1189
+ const aValue = field === "createdAt" ? new Date(a.createdAt).getTime() : a[field];
1190
+ const bValue = field === "createdAt" ? new Date(b.createdAt).getTime() : b[field];
1191
+ if (aValue === bValue) {
1192
+ return a.id.localeCompare(b.id);
1193
+ }
1194
+ return direction === "ASC" ? aValue - bValue : bValue - aValue;
1195
+ });
1196
+ const total = allThreadMessages.length;
1197
+ const paginatedMessages = allThreadMessages.slice(offset, offset + perPage);
1198
+ const paginatedCount = paginatedMessages.length;
1199
+ if (total === 0 && paginatedCount === 0 && (!include || include.length === 0)) {
1200
+ return {
1201
+ messages: [],
1202
+ total: 0,
1203
+ page,
1204
+ perPage: perPageForResponse,
1205
+ hasMore: false
1206
+ };
1207
+ }
1208
+ const messageIds = new Set(paginatedMessages.map((m) => m.id));
1209
+ let includeMessages = [];
1210
+ if (include && include.length > 0) {
1211
+ const selectBy = { include };
1212
+ includeMessages = await this._getIncludedMessages(threadId, selectBy);
1213
+ for (const includeMsg of includeMessages) {
1214
+ if (!messageIds.has(includeMsg.id)) {
1215
+ paginatedMessages.push(includeMsg);
1216
+ messageIds.add(includeMsg.id);
1217
+ }
1218
+ }
1219
+ }
1220
+ const list = new MessageList().add(paginatedMessages, "memory");
1221
+ let finalMessages = list.get.all.db();
1222
+ finalMessages = finalMessages.sort((a, b) => {
1223
+ const aValue = field === "createdAt" ? new Date(a.createdAt).getTime() : a[field];
1224
+ const bValue = field === "createdAt" ? new Date(b.createdAt).getTime() : b[field];
1225
+ if (aValue === bValue) {
1226
+ return a.id.localeCompare(b.id);
1227
+ }
1228
+ return direction === "ASC" ? aValue - bValue : bValue - aValue;
1229
+ });
1230
+ const returnedThreadMessageIds = new Set(finalMessages.filter((m) => m.threadId === threadId).map((m) => m.id));
1231
+ const allThreadMessagesReturned = returnedThreadMessageIds.size >= total;
1232
+ let hasMore = false;
1233
+ if (perPageInput !== false && !allThreadMessagesReturned) {
1234
+ hasMore = offset + paginatedCount < total;
1235
+ }
1236
+ return {
1237
+ messages: finalMessages,
1238
+ total,
1239
+ page,
1240
+ perPage: perPageForResponse,
1241
+ hasMore
1242
+ };
1243
+ } catch (error) {
1244
+ const mastraError = new MastraError(
1245
+ {
1246
+ id: "STORAGE_DYNAMODB_STORE_LIST_MESSAGES_FAILED",
1247
+ domain: ErrorDomain.STORAGE,
1248
+ category: ErrorCategory.THIRD_PARTY,
1249
+ details: {
1250
+ threadId,
1251
+ resourceId: resourceId ?? ""
1252
+ }
1253
+ },
1254
+ error
1255
+ );
1256
+ this.logger?.error?.(mastraError.toString());
1257
+ this.logger?.trackException?.(mastraError);
1258
+ return {
1259
+ messages: [],
1260
+ total: 0,
1261
+ page,
1262
+ perPage: perPageForResponse,
1263
+ hasMore: false
1264
+ };
1265
+ }
1266
+ }
1267
+ async saveMessages(args) {
1268
+ const { messages } = args;
1269
+ this.logger.debug("Saving messages", { count: messages.length });
1270
+ if (!messages.length) {
1271
+ return { messages: [] };
1272
+ }
1273
+ const threadId = messages[0]?.threadId;
1274
+ if (!threadId) {
1275
+ throw new Error("Thread ID is required");
1276
+ }
1277
+ const messagesToSave = messages.map((msg) => {
1278
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1279
+ return {
1280
+ entity: "message",
1281
+ // Add entity type
1282
+ id: msg.id,
1283
+ threadId: msg.threadId,
1284
+ role: msg.role,
1285
+ type: msg.type,
1286
+ resourceId: msg.resourceId,
1287
+ // Ensure complex fields are stringified if not handled by attribute setters
1288
+ content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content),
1289
+ toolCallArgs: `toolCallArgs` in msg && msg.toolCallArgs ? JSON.stringify(msg.toolCallArgs) : void 0,
1290
+ toolCallIds: `toolCallIds` in msg && msg.toolCallIds ? JSON.stringify(msg.toolCallIds) : void 0,
1291
+ toolNames: `toolNames` in msg && msg.toolNames ? JSON.stringify(msg.toolNames) : void 0,
1292
+ createdAt: msg.createdAt instanceof Date ? msg.createdAt.toISOString() : msg.createdAt || now,
1293
+ updatedAt: now
1294
+ // Add updatedAt
1295
+ };
1296
+ });
1297
+ try {
1298
+ const savedMessageIds = [];
1299
+ for (const messageData of messagesToSave) {
1300
+ if (!messageData.entity) {
1301
+ this.logger.error("Missing entity property in message data for create", { messageData });
1302
+ throw new Error("Internal error: Missing entity property during saveMessages");
1303
+ }
1304
+ try {
1305
+ await this.service.entities.message.put(messageData).go();
1306
+ savedMessageIds.push(messageData.id);
1307
+ } catch (error) {
1308
+ for (const savedId of savedMessageIds) {
1309
+ try {
1310
+ await this.service.entities.message.delete({ entity: "message", id: savedId }).go();
1311
+ } catch (rollbackError) {
1312
+ this.logger.error("Failed to rollback message during save error", {
1313
+ messageId: savedId,
1314
+ error: rollbackError
1315
+ });
1316
+ }
1317
+ }
1318
+ throw error;
1319
+ }
1320
+ }
1321
+ await this.service.entities.thread.update({ entity: "thread", id: threadId }).set({
1322
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1323
+ }).go();
1324
+ const list = new MessageList().add(messages, "memory");
1325
+ return { messages: list.get.all.db() };
616
1326
  } catch (error) {
617
- this.logger.error("Error validating table access", { tableName: this.tableName, error });
618
1327
  throw new MastraError(
619
1328
  {
620
- id: "STORAGE_DYNAMODB_STORE_VALIDATE_TABLE_ACCESS_FAILED",
1329
+ id: "STORAGE_DYNAMODB_STORE_SAVE_MESSAGES_FAILED",
621
1330
  domain: ErrorDomain.STORAGE,
622
1331
  category: ErrorCategory.THIRD_PARTY,
623
- details: { tableName: this.tableName }
1332
+ details: { count: messages.length }
1333
+ },
1334
+ error
1335
+ );
1336
+ }
1337
+ }
1338
+ async listThreadsByResourceId(args) {
1339
+ const { resourceId, page = 0, perPage: perPageInput, orderBy } = args;
1340
+ const perPage = normalizePerPage(perPageInput, 100);
1341
+ if (page < 0) {
1342
+ throw new MastraError(
1343
+ {
1344
+ id: "STORAGE_DYNAMODB_LIST_THREADS_BY_RESOURCE_ID_INVALID_PAGE",
1345
+ domain: ErrorDomain.STORAGE,
1346
+ category: ErrorCategory.USER,
1347
+ details: { page }
1348
+ },
1349
+ new Error("page must be >= 0")
1350
+ );
1351
+ }
1352
+ const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
1353
+ const { field, direction } = this.parseOrderBy(orderBy);
1354
+ this.logger.debug("Getting threads by resource ID with pagination", {
1355
+ resourceId,
1356
+ page,
1357
+ perPage,
1358
+ field,
1359
+ direction
1360
+ });
1361
+ try {
1362
+ const query = this.service.entities.thread.query.byResource({ entity: "thread", resourceId });
1363
+ const results = await query.go();
1364
+ const allThreads = this.transformAndSortThreads(results.data, field, direction);
1365
+ const endIndex = offset + perPage;
1366
+ const paginatedThreads = allThreads.slice(offset, endIndex);
1367
+ const total = allThreads.length;
1368
+ const hasMore = offset + perPage < total;
1369
+ return {
1370
+ threads: paginatedThreads,
1371
+ total,
1372
+ page,
1373
+ perPage: perPageForResponse,
1374
+ hasMore
1375
+ };
1376
+ } catch (error) {
1377
+ throw new MastraError(
1378
+ {
1379
+ id: "DYNAMODB_STORAGE_LIST_THREADS_BY_RESOURCE_ID_FAILED",
1380
+ domain: ErrorDomain.STORAGE,
1381
+ category: ErrorCategory.THIRD_PARTY,
1382
+ details: { resourceId, page, perPage }
1383
+ },
1384
+ error
1385
+ );
1386
+ }
1387
+ }
1388
+ // Helper method to get included messages with context
1389
+ async _getIncludedMessages(threadId, selectBy) {
1390
+ if (!threadId.trim()) throw new Error("threadId must be a non-empty string");
1391
+ if (!selectBy?.include?.length) {
1392
+ return [];
1393
+ }
1394
+ const includeMessages = [];
1395
+ for (const includeItem of selectBy.include) {
1396
+ try {
1397
+ const { id, threadId: targetThreadId, withPreviousMessages = 0, withNextMessages = 0 } = includeItem;
1398
+ const searchThreadId = targetThreadId || threadId;
1399
+ this.logger.debug("Getting included messages for", {
1400
+ id,
1401
+ targetThreadId,
1402
+ searchThreadId,
1403
+ withPreviousMessages,
1404
+ withNextMessages
1405
+ });
1406
+ const query = this.service.entities.message.query.byThread({ entity: "message", threadId: searchThreadId });
1407
+ const results = await query.go();
1408
+ const allMessages = results.data.map((data) => this.parseMessageData(data)).filter((msg) => "content" in msg && typeof msg.content === "object");
1409
+ this.logger.debug("Found messages in thread", {
1410
+ threadId: searchThreadId,
1411
+ messageCount: allMessages.length,
1412
+ messageIds: allMessages.map((m) => m.id)
1413
+ });
1414
+ allMessages.sort((a, b) => {
1415
+ const timeA = a.createdAt.getTime();
1416
+ const timeB = b.createdAt.getTime();
1417
+ if (timeA === timeB) {
1418
+ return a.id.localeCompare(b.id);
1419
+ }
1420
+ return timeA - timeB;
1421
+ });
1422
+ const targetIndex = allMessages.findIndex((msg) => msg.id === id);
1423
+ if (targetIndex === -1) {
1424
+ this.logger.warn("Target message not found", { id, threadId: searchThreadId });
1425
+ continue;
1426
+ }
1427
+ this.logger.debug("Found target message at index", { id, targetIndex, totalMessages: allMessages.length });
1428
+ const startIndex = Math.max(0, targetIndex - withPreviousMessages);
1429
+ const endIndex = Math.min(allMessages.length, targetIndex + withNextMessages + 1);
1430
+ const contextMessages = allMessages.slice(startIndex, endIndex);
1431
+ this.logger.debug("Context messages", {
1432
+ startIndex,
1433
+ endIndex,
1434
+ contextCount: contextMessages.length,
1435
+ contextIds: contextMessages.map((m) => m.id)
1436
+ });
1437
+ includeMessages.push(...contextMessages);
1438
+ } catch (error) {
1439
+ this.logger.warn("Failed to get included message", { messageId: includeItem.id, error });
1440
+ }
1441
+ }
1442
+ this.logger.debug("Total included messages", {
1443
+ count: includeMessages.length,
1444
+ ids: includeMessages.map((m) => m.id)
1445
+ });
1446
+ return includeMessages;
1447
+ }
1448
+ async updateMessages(args) {
1449
+ const { messages } = args;
1450
+ this.logger.debug("Updating messages", { count: messages.length });
1451
+ if (!messages.length) {
1452
+ return [];
1453
+ }
1454
+ const updatedMessages = [];
1455
+ const affectedThreadIds = /* @__PURE__ */ new Set();
1456
+ try {
1457
+ for (const updateData of messages) {
1458
+ const { id, ...updates } = updateData;
1459
+ const existingMessage = await this.service.entities.message.get({ entity: "message", id }).go();
1460
+ if (!existingMessage.data) {
1461
+ this.logger.warn("Message not found for update", { id });
1462
+ continue;
1463
+ }
1464
+ const existingMsg = this.parseMessageData(existingMessage.data);
1465
+ const originalThreadId = existingMsg.threadId;
1466
+ affectedThreadIds.add(originalThreadId);
1467
+ const updatePayload = {
1468
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1469
+ };
1470
+ if ("role" in updates && updates.role !== void 0) updatePayload.role = updates.role;
1471
+ if ("type" in updates && updates.type !== void 0) updatePayload.type = updates.type;
1472
+ if ("resourceId" in updates && updates.resourceId !== void 0) updatePayload.resourceId = updates.resourceId;
1473
+ if ("threadId" in updates && updates.threadId !== void 0 && updates.threadId !== null) {
1474
+ updatePayload.threadId = updates.threadId;
1475
+ affectedThreadIds.add(updates.threadId);
1476
+ }
1477
+ if (updates.content) {
1478
+ const existingContent = existingMsg.content;
1479
+ let newContent = { ...existingContent };
1480
+ if (updates.content.metadata !== void 0) {
1481
+ newContent.metadata = {
1482
+ ...existingContent.metadata || {},
1483
+ ...updates.content.metadata || {}
1484
+ };
1485
+ }
1486
+ if (updates.content.content !== void 0) {
1487
+ newContent.content = updates.content.content;
1488
+ }
1489
+ if ("parts" in updates.content && updates.content.parts !== void 0) {
1490
+ newContent.parts = updates.content.parts;
1491
+ }
1492
+ updatePayload.content = JSON.stringify(newContent);
1493
+ }
1494
+ await this.service.entities.message.update({ entity: "message", id }).set(updatePayload).go();
1495
+ const updatedMessage = await this.service.entities.message.get({ entity: "message", id }).go();
1496
+ if (updatedMessage.data) {
1497
+ updatedMessages.push(this.parseMessageData(updatedMessage.data));
1498
+ }
1499
+ }
1500
+ for (const threadId of affectedThreadIds) {
1501
+ await this.service.entities.thread.update({ entity: "thread", id: threadId }).set({
1502
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1503
+ }).go();
1504
+ }
1505
+ return updatedMessages;
1506
+ } catch (error) {
1507
+ throw new MastraError(
1508
+ {
1509
+ id: "STORAGE_DYNAMODB_STORE_UPDATE_MESSAGES_FAILED",
1510
+ domain: ErrorDomain.STORAGE,
1511
+ category: ErrorCategory.THIRD_PARTY,
1512
+ details: { count: messages.length }
1513
+ },
1514
+ error
1515
+ );
1516
+ }
1517
+ }
1518
+ async getResourceById({ resourceId }) {
1519
+ this.logger.debug("Getting resource by ID", { resourceId });
1520
+ try {
1521
+ const result = await this.service.entities.resource.get({ entity: "resource", id: resourceId }).go();
1522
+ if (!result.data) {
1523
+ return null;
1524
+ }
1525
+ const data = result.data;
1526
+ return {
1527
+ ...data,
1528
+ // Convert date strings back to Date objects for consistency
1529
+ createdAt: typeof data.createdAt === "string" ? new Date(data.createdAt) : data.createdAt,
1530
+ updatedAt: typeof data.updatedAt === "string" ? new Date(data.updatedAt) : data.updatedAt,
1531
+ // Ensure workingMemory is always returned as a string, regardless of automatic parsing
1532
+ workingMemory: typeof data.workingMemory === "object" ? JSON.stringify(data.workingMemory) : data.workingMemory
1533
+ // metadata is already transformed by the entity's getter
1534
+ };
1535
+ } catch (error) {
1536
+ throw new MastraError(
1537
+ {
1538
+ id: "STORAGE_DYNAMODB_STORE_GET_RESOURCE_BY_ID_FAILED",
1539
+ domain: ErrorDomain.STORAGE,
1540
+ category: ErrorCategory.THIRD_PARTY,
1541
+ details: { resourceId }
1542
+ },
1543
+ error
1544
+ );
1545
+ }
1546
+ }
1547
+ async saveResource({ resource }) {
1548
+ this.logger.debug("Saving resource", { resourceId: resource.id });
1549
+ const now = /* @__PURE__ */ new Date();
1550
+ const resourceData = {
1551
+ entity: "resource",
1552
+ id: resource.id,
1553
+ workingMemory: resource.workingMemory,
1554
+ metadata: resource.metadata ? JSON.stringify(resource.metadata) : void 0,
1555
+ createdAt: resource.createdAt?.toISOString() || now.toISOString(),
1556
+ updatedAt: now.toISOString()
1557
+ };
1558
+ try {
1559
+ await this.service.entities.resource.upsert(resourceData).go();
1560
+ return {
1561
+ id: resource.id,
1562
+ workingMemory: resource.workingMemory,
1563
+ metadata: resource.metadata,
1564
+ createdAt: resource.createdAt || now,
1565
+ updatedAt: now
1566
+ };
1567
+ } catch (error) {
1568
+ throw new MastraError(
1569
+ {
1570
+ id: "STORAGE_DYNAMODB_STORE_SAVE_RESOURCE_FAILED",
1571
+ domain: ErrorDomain.STORAGE,
1572
+ category: ErrorCategory.THIRD_PARTY,
1573
+ details: { resourceId: resource.id }
624
1574
  },
625
1575
  error
626
1576
  );
627
1577
  }
628
1578
  }
1579
+ async updateResource({
1580
+ resourceId,
1581
+ workingMemory,
1582
+ metadata
1583
+ }) {
1584
+ this.logger.debug("Updating resource", { resourceId });
1585
+ try {
1586
+ const existingResource = await this.getResourceById({ resourceId });
1587
+ if (!existingResource) {
1588
+ const newResource = {
1589
+ id: resourceId,
1590
+ workingMemory,
1591
+ metadata: metadata || {},
1592
+ createdAt: /* @__PURE__ */ new Date(),
1593
+ updatedAt: /* @__PURE__ */ new Date()
1594
+ };
1595
+ return this.saveResource({ resource: newResource });
1596
+ }
1597
+ const now = /* @__PURE__ */ new Date();
1598
+ const updateData = {
1599
+ updatedAt: now.toISOString()
1600
+ };
1601
+ if (workingMemory !== void 0) {
1602
+ updateData.workingMemory = workingMemory;
1603
+ }
1604
+ if (metadata) {
1605
+ const existingMetadata = existingResource.metadata || {};
1606
+ const mergedMetadata = { ...existingMetadata, ...metadata };
1607
+ updateData.metadata = JSON.stringify(mergedMetadata);
1608
+ }
1609
+ await this.service.entities.resource.update({ entity: "resource", id: resourceId }).set(updateData).go();
1610
+ return {
1611
+ ...existingResource,
1612
+ workingMemory: workingMemory !== void 0 ? workingMemory : existingResource.workingMemory,
1613
+ metadata: metadata ? { ...existingResource.metadata, ...metadata } : existingResource.metadata,
1614
+ updatedAt: now
1615
+ };
1616
+ } catch (error) {
1617
+ throw new MastraError(
1618
+ {
1619
+ id: "STORAGE_DYNAMODB_STORE_UPDATE_RESOURCE_FAILED",
1620
+ domain: ErrorDomain.STORAGE,
1621
+ category: ErrorCategory.THIRD_PARTY,
1622
+ details: { resourceId }
1623
+ },
1624
+ error
1625
+ );
1626
+ }
1627
+ }
1628
+ };
1629
+ var StoreOperationsDynamoDB = class extends StoreOperations {
1630
+ client;
1631
+ tableName;
1632
+ service;
1633
+ constructor({
1634
+ service,
1635
+ tableName,
1636
+ client
1637
+ }) {
1638
+ super();
1639
+ this.service = service;
1640
+ this.client = client;
1641
+ this.tableName = tableName;
1642
+ }
1643
+ async hasColumn() {
1644
+ return true;
1645
+ }
1646
+ async dropTable() {
1647
+ }
1648
+ // Helper methods for entity/table mapping
1649
+ getEntityNameForTable(tableName) {
1650
+ const mapping = {
1651
+ [TABLE_THREADS]: "thread",
1652
+ [TABLE_MESSAGES]: "message",
1653
+ [TABLE_WORKFLOW_SNAPSHOT]: "workflow_snapshot",
1654
+ [TABLE_SCORERS]: "score",
1655
+ [TABLE_TRACES]: "trace",
1656
+ [TABLE_RESOURCES]: "resource",
1657
+ [TABLE_SPANS]: "ai_span"
1658
+ };
1659
+ return mapping[tableName] || null;
1660
+ }
1661
+ /**
1662
+ * Pre-processes a record to ensure Date objects are converted to ISO strings
1663
+ * This is necessary because ElectroDB validation happens before setters are applied
1664
+ */
1665
+ preprocessRecord(record) {
1666
+ const processed = { ...record };
1667
+ if (processed.createdAt instanceof Date) {
1668
+ processed.createdAt = processed.createdAt.toISOString();
1669
+ }
1670
+ if (processed.updatedAt instanceof Date) {
1671
+ processed.updatedAt = processed.updatedAt.toISOString();
1672
+ }
1673
+ if (processed.created_at instanceof Date) {
1674
+ processed.created_at = processed.created_at.toISOString();
1675
+ }
1676
+ if (processed.result && typeof processed.result === "object") {
1677
+ processed.result = JSON.stringify(processed.result);
1678
+ }
1679
+ if (processed.test_info && typeof processed.test_info === "object") {
1680
+ processed.test_info = JSON.stringify(processed.test_info);
1681
+ } else if (processed.test_info === void 0 || processed.test_info === null) {
1682
+ delete processed.test_info;
1683
+ }
1684
+ if (processed.snapshot && typeof processed.snapshot === "object") {
1685
+ processed.snapshot = JSON.stringify(processed.snapshot);
1686
+ }
1687
+ if (processed.attributes && typeof processed.attributes === "object") {
1688
+ processed.attributes = JSON.stringify(processed.attributes);
1689
+ }
1690
+ if (processed.status && typeof processed.status === "object") {
1691
+ processed.status = JSON.stringify(processed.status);
1692
+ }
1693
+ if (processed.events && typeof processed.events === "object") {
1694
+ processed.events = JSON.stringify(processed.events);
1695
+ }
1696
+ if (processed.links && typeof processed.links === "object") {
1697
+ processed.links = JSON.stringify(processed.links);
1698
+ }
1699
+ return processed;
1700
+ }
629
1701
  /**
630
1702
  * Validates that the required DynamoDB table exists and is accessible.
631
1703
  * This does not check the table structure - it assumes the table
@@ -644,7 +1716,40 @@ var DynamoDBStore = class extends MastraStorage {
644
1716
  }
645
1717
  throw new MastraError(
646
1718
  {
647
- id: "STORAGE_DYNAMODB_STORE_VALIDATE_TABLE_EXISTS_FAILED",
1719
+ id: "STORAGE_DYNAMODB_STORE_VALIDATE_TABLE_EXISTS_FAILED",
1720
+ domain: ErrorDomain.STORAGE,
1721
+ category: ErrorCategory.THIRD_PARTY,
1722
+ details: { tableName: this.tableName }
1723
+ },
1724
+ error
1725
+ );
1726
+ }
1727
+ }
1728
+ /**
1729
+ * This method is modified for DynamoDB with ElectroDB single-table design.
1730
+ * It assumes the table is created and managed externally via CDK/CloudFormation.
1731
+ *
1732
+ * This implementation only validates that the required table exists and is accessible.
1733
+ * No table creation is attempted - we simply check if we can access the table.
1734
+ */
1735
+ async createTable({ tableName }) {
1736
+ this.logger.debug("Validating access to externally managed table", { tableName, physicalTable: this.tableName });
1737
+ try {
1738
+ const tableExists = await this.validateTableExists();
1739
+ if (!tableExists) {
1740
+ this.logger.error(
1741
+ `Table ${this.tableName} does not exist or is not accessible. It should be created via CDK/CloudFormation.`
1742
+ );
1743
+ throw new Error(
1744
+ `Table ${this.tableName} does not exist or is not accessible. Ensure it's created via CDK/CloudFormation before using this store.`
1745
+ );
1746
+ }
1747
+ this.logger.debug(`Table ${this.tableName} exists and is accessible`);
1748
+ } catch (error) {
1749
+ this.logger.error("Error validating table access", { tableName: this.tableName, error });
1750
+ throw new MastraError(
1751
+ {
1752
+ id: "STORAGE_DYNAMODB_STORE_VALIDATE_TABLE_ACCESS_FAILED",
648
1753
  domain: ErrorDomain.STORAGE,
649
1754
  category: ErrorCategory.THIRD_PARTY,
650
1755
  details: { tableName: this.tableName }
@@ -653,63 +1758,33 @@ var DynamoDBStore = class extends MastraStorage {
653
1758
  );
654
1759
  }
655
1760
  }
656
- /**
657
- * Initialize storage, validating the externally managed table is accessible.
658
- * For the single-table design, we only validate once that we can access
659
- * the table that was created via CDK/CloudFormation.
660
- */
661
- async init() {
662
- if (this.hasInitialized === null) {
663
- this.hasInitialized = this._performInitializationAndStore();
1761
+ async insert({ tableName, record }) {
1762
+ this.logger.debug("DynamoDB insert called", { tableName });
1763
+ const entityName = this.getEntityNameForTable(tableName);
1764
+ if (!entityName || !this.service.entities[entityName]) {
1765
+ throw new MastraError({
1766
+ id: "STORAGE_DYNAMODB_STORE_INSERT_INVALID_ARGS",
1767
+ domain: ErrorDomain.STORAGE,
1768
+ category: ErrorCategory.USER,
1769
+ text: "No entity defined for tableName",
1770
+ details: { tableName }
1771
+ });
664
1772
  }
665
1773
  try {
666
- await this.hasInitialized;
1774
+ const dataToSave = { entity: entityName, ...this.preprocessRecord(record) };
1775
+ await this.service.entities[entityName].create(dataToSave).go();
667
1776
  } catch (error) {
668
1777
  throw new MastraError(
669
1778
  {
670
- id: "STORAGE_DYNAMODB_STORE_INIT_FAILED",
1779
+ id: "STORAGE_DYNAMODB_STORE_INSERT_FAILED",
671
1780
  domain: ErrorDomain.STORAGE,
672
1781
  category: ErrorCategory.THIRD_PARTY,
673
- details: { tableName: this.tableName }
1782
+ details: { tableName }
674
1783
  },
675
1784
  error
676
1785
  );
677
1786
  }
678
1787
  }
679
- /**
680
- * Performs the actual table validation and stores the promise.
681
- * Handles resetting the stored promise on failure to allow retries.
682
- */
683
- _performInitializationAndStore() {
684
- return this.validateTableExists().then((exists) => {
685
- if (!exists) {
686
- throw new Error(
687
- `Table ${this.tableName} does not exist or is not accessible. Ensure it's created via CDK/CloudFormation before using this store.`
688
- );
689
- }
690
- return true;
691
- }).catch((err) => {
692
- this.hasInitialized = null;
693
- throw err;
694
- });
695
- }
696
- /**
697
- * Pre-processes a record to ensure Date objects are converted to ISO strings
698
- * This is necessary because ElectroDB validation happens before setters are applied
699
- */
700
- preprocessRecord(record) {
701
- const processed = { ...record };
702
- if (processed.createdAt instanceof Date) {
703
- processed.createdAt = processed.createdAt.toISOString();
704
- }
705
- if (processed.updatedAt instanceof Date) {
706
- processed.updatedAt = processed.updatedAt.toISOString();
707
- }
708
- if (processed.created_at instanceof Date) {
709
- processed.created_at = processed.created_at.toISOString();
710
- }
711
- return processed;
712
- }
713
1788
  async alterTable(_args) {
714
1789
  }
715
1790
  /**
@@ -745,10 +1820,10 @@ var DynamoDBStore = class extends MastraStorage {
745
1820
  if (!item.id) throw new Error(`Missing required key 'id' for entity 'message'`);
746
1821
  key.id = item.id;
747
1822
  break;
748
- case "workflowSnapshot":
1823
+ case "workflow_snapshot":
749
1824
  if (!item.workflow_name)
750
- throw new Error(`Missing required key 'workflow_name' for entity 'workflowSnapshot'`);
751
- if (!item.run_id) throw new Error(`Missing required key 'run_id' for entity 'workflowSnapshot'`);
1825
+ throw new Error(`Missing required key 'workflow_name' for entity 'workflow_snapshot'`);
1826
+ if (!item.run_id) throw new Error(`Missing required key 'run_id' for entity 'workflow_snapshot'`);
752
1827
  key.workflow_name = item.workflow_name;
753
1828
  key.run_id = item.run_id;
754
1829
  break;
@@ -760,6 +1835,14 @@ var DynamoDBStore = class extends MastraStorage {
760
1835
  if (!item.id) throw new Error(`Missing required key 'id' for entity 'trace'`);
761
1836
  key.id = item.id;
762
1837
  break;
1838
+ case "score":
1839
+ if (!item.id) throw new Error(`Missing required key 'id' for entity 'score'`);
1840
+ key.id = item.id;
1841
+ break;
1842
+ case "resource":
1843
+ if (!item.id) throw new Error(`Missing required key 'id' for entity 'resource'`);
1844
+ key.id = item.id;
1845
+ break;
763
1846
  default:
764
1847
  this.logger.warn(`Unknown entity type encountered during clearTable: ${entityName}`);
765
1848
  throw new Error(`Cannot construct delete key for unknown entity type: ${entityName}`);
@@ -784,46 +1867,10 @@ var DynamoDBStore = class extends MastraStorage {
784
1867
  );
785
1868
  }
786
1869
  }
787
- /**
788
- * Insert a record into the specified "table" (entity)
789
- */
790
- async insert({
791
- tableName,
792
- record
793
- }) {
794
- this.logger.debug("DynamoDB insert called", { tableName });
795
- const entityName = this.getEntityNameForTable(tableName);
796
- if (!entityName || !this.service.entities[entityName]) {
797
- throw new MastraError({
798
- id: "STORAGE_DYNAMODB_STORE_INSERT_INVALID_ARGS",
799
- domain: ErrorDomain.STORAGE,
800
- category: ErrorCategory.USER,
801
- text: "No entity defined for tableName",
802
- details: { tableName }
803
- });
804
- }
805
- try {
806
- const dataToSave = { entity: entityName, ...this.preprocessRecord(record) };
807
- await this.service.entities[entityName].create(dataToSave).go();
808
- } catch (error) {
809
- throw new MastraError(
810
- {
811
- id: "STORAGE_DYNAMODB_STORE_INSERT_FAILED",
812
- domain: ErrorDomain.STORAGE,
813
- category: ErrorCategory.THIRD_PARTY,
814
- details: { tableName }
815
- },
816
- error
817
- );
818
- }
819
- }
820
1870
  /**
821
1871
  * Insert multiple records as a batch
822
1872
  */
823
- async batchInsert({
824
- tableName,
825
- records
826
- }) {
1873
+ async batchInsert({ tableName, records }) {
827
1874
  this.logger.debug("DynamoDB batchInsert called", { tableName, count: records.length });
828
1875
  const entityName = this.getEntityNameForTable(tableName);
829
1876
  if (!entityName || !this.service.entities[entityName]) {
@@ -868,10 +1915,7 @@ var DynamoDBStore = class extends MastraStorage {
868
1915
  /**
869
1916
  * Load a record by its keys
870
1917
  */
871
- async load({
872
- tableName,
873
- keys
874
- }) {
1918
+ async load({ tableName, keys }) {
875
1919
  this.logger.debug("DynamoDB load called", { tableName, keys });
876
1920
  const entityName = this.getEntityNameForTable(tableName);
877
1921
  if (!entityName || !this.service.entities[entityName]) {
@@ -903,344 +1947,320 @@ var DynamoDBStore = class extends MastraStorage {
903
1947
  );
904
1948
  }
905
1949
  }
906
- // Thread operations
907
- async getThreadById({ threadId }) {
908
- this.logger.debug("Getting thread by ID", { threadId });
1950
+ };
1951
+ var ScoresStorageDynamoDB = class extends ScoresStorage {
1952
+ service;
1953
+ constructor({ service }) {
1954
+ super();
1955
+ this.service = service;
1956
+ }
1957
+ // Helper function to parse score data (handle JSON fields)
1958
+ parseScoreData(data) {
1959
+ return {
1960
+ ...data,
1961
+ // Convert date strings back to Date objects for consistency
1962
+ createdAt: data.createdAt ? new Date(data.createdAt) : /* @__PURE__ */ new Date(),
1963
+ updatedAt: data.updatedAt ? new Date(data.updatedAt) : /* @__PURE__ */ new Date()
1964
+ // JSON fields are already transformed by the entity's getters
1965
+ };
1966
+ }
1967
+ async getScoreById({ id }) {
1968
+ this.logger.debug("Getting score by ID", { id });
909
1969
  try {
910
- const result = await this.service.entities.thread.get({ entity: "thread", id: threadId }).go();
1970
+ const result = await this.service.entities.score.get({ entity: "score", id }).go();
911
1971
  if (!result.data) {
912
1972
  return null;
913
1973
  }
914
- const data = result.data;
915
- return {
916
- ...data,
917
- // Convert date strings back to Date objects for consistency
918
- createdAt: typeof data.createdAt === "string" ? new Date(data.createdAt) : data.createdAt,
919
- updatedAt: typeof data.updatedAt === "string" ? new Date(data.updatedAt) : data.updatedAt
920
- // metadata: data.metadata ? JSON.parse(data.metadata) : undefined, // REMOVED by AI
921
- // metadata is already transformed by the entity's getter
922
- };
1974
+ return this.parseScoreData(result.data);
923
1975
  } catch (error) {
924
1976
  throw new MastraError(
925
1977
  {
926
- id: "STORAGE_DYNAMODB_STORE_GET_THREAD_BY_ID_FAILED",
1978
+ id: "STORAGE_DYNAMODB_STORE_GET_SCORE_BY_ID_FAILED",
927
1979
  domain: ErrorDomain.STORAGE,
928
1980
  category: ErrorCategory.THIRD_PARTY,
929
- details: { threadId }
1981
+ details: { id }
930
1982
  },
931
1983
  error
932
1984
  );
933
1985
  }
934
1986
  }
935
- async getThreadsByResourceId({ resourceId }) {
936
- this.logger.debug("Getting threads by resource ID", { resourceId });
1987
+ async saveScore(score) {
1988
+ let validatedScore;
937
1989
  try {
938
- const result = await this.service.entities.thread.query.byResource({ entity: "thread", resourceId }).go();
939
- if (!result.data.length) {
940
- return [];
941
- }
942
- return result.data.map((data) => ({
943
- ...data,
944
- // Convert date strings back to Date objects for consistency
945
- createdAt: typeof data.createdAt === "string" ? new Date(data.createdAt) : data.createdAt,
946
- updatedAt: typeof data.updatedAt === "string" ? new Date(data.updatedAt) : data.updatedAt
947
- // metadata: data.metadata ? JSON.parse(data.metadata) : undefined, // REMOVED by AI
948
- // metadata is already transformed by the entity's getter
949
- }));
1990
+ validatedScore = saveScorePayloadSchema.parse(score);
950
1991
  } catch (error) {
951
1992
  throw new MastraError(
952
1993
  {
953
- id: "STORAGE_DYNAMODB_STORE_GET_THREADS_BY_RESOURCE_ID_FAILED",
1994
+ id: "STORAGE_DYNAMODB_STORE_SAVE_SCORE_FAILED",
954
1995
  domain: ErrorDomain.STORAGE,
955
- category: ErrorCategory.THIRD_PARTY,
956
- details: { resourceId }
1996
+ category: ErrorCategory.THIRD_PARTY
957
1997
  },
958
1998
  error
959
1999
  );
960
2000
  }
961
- }
962
- async saveThread({ thread }) {
963
- this.logger.debug("Saving thread", { threadId: thread.id });
964
2001
  const now = /* @__PURE__ */ new Date();
965
- const threadData = {
966
- entity: "thread",
967
- id: thread.id,
968
- resourceId: thread.resourceId,
969
- title: thread.title || `Thread ${thread.id}`,
970
- createdAt: thread.createdAt?.toISOString() || now.toISOString(),
971
- updatedAt: now.toISOString(),
972
- metadata: thread.metadata ? JSON.stringify(thread.metadata) : void 0
2002
+ const scoreId = `score-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
2003
+ const scoreData = {
2004
+ entity: "score",
2005
+ id: scoreId,
2006
+ scorerId: validatedScore.scorerId,
2007
+ traceId: validatedScore.traceId || "",
2008
+ spanId: validatedScore.spanId || "",
2009
+ runId: validatedScore.runId,
2010
+ scorer: typeof validatedScore.scorer === "string" ? validatedScore.scorer : JSON.stringify(validatedScore.scorer),
2011
+ preprocessStepResult: typeof validatedScore.preprocessStepResult === "string" ? validatedScore.preprocessStepResult : JSON.stringify(validatedScore.preprocessStepResult),
2012
+ analyzeStepResult: typeof validatedScore.analyzeStepResult === "string" ? validatedScore.analyzeStepResult : JSON.stringify(validatedScore.analyzeStepResult),
2013
+ score: validatedScore.score,
2014
+ reason: validatedScore.reason,
2015
+ preprocessPrompt: validatedScore.preprocessPrompt,
2016
+ generateScorePrompt: validatedScore.generateScorePrompt,
2017
+ generateReasonPrompt: validatedScore.generateReasonPrompt,
2018
+ analyzePrompt: validatedScore.analyzePrompt,
2019
+ input: typeof validatedScore.input === "string" ? validatedScore.input : JSON.stringify(validatedScore.input),
2020
+ output: typeof validatedScore.output === "string" ? validatedScore.output : JSON.stringify(validatedScore.output),
2021
+ additionalContext: typeof validatedScore.additionalContext === "string" ? validatedScore.additionalContext : JSON.stringify(validatedScore.additionalContext),
2022
+ requestContext: typeof validatedScore.requestContext === "string" ? validatedScore.requestContext : JSON.stringify(validatedScore.requestContext),
2023
+ entityType: validatedScore.entityType,
2024
+ entityData: typeof validatedScore.entity === "string" ? validatedScore.entity : JSON.stringify(validatedScore.entity),
2025
+ entityId: validatedScore.entityId,
2026
+ source: validatedScore.source,
2027
+ resourceId: validatedScore.resourceId || "",
2028
+ threadId: validatedScore.threadId || "",
2029
+ createdAt: now.toISOString(),
2030
+ updatedAt: now.toISOString()
973
2031
  };
974
2032
  try {
975
- await this.service.entities.thread.upsert(threadData).go();
976
- return {
977
- id: thread.id,
978
- resourceId: thread.resourceId,
979
- title: threadData.title,
980
- createdAt: thread.createdAt || now,
981
- updatedAt: now,
982
- metadata: thread.metadata
983
- };
984
- } catch (error) {
985
- throw new MastraError(
986
- {
987
- id: "STORAGE_DYNAMODB_STORE_SAVE_THREAD_FAILED",
988
- domain: ErrorDomain.STORAGE,
989
- category: ErrorCategory.THIRD_PARTY,
990
- details: { threadId: thread.id }
991
- },
992
- error
993
- );
994
- }
995
- }
996
- async updateThread({
997
- id,
998
- title,
999
- metadata
1000
- }) {
1001
- this.logger.debug("Updating thread", { threadId: id });
1002
- try {
1003
- const existingThread = await this.getThreadById({ threadId: id });
1004
- if (!existingThread) {
1005
- throw new Error(`Thread not found: ${id}`);
1006
- }
1007
- const now = /* @__PURE__ */ new Date();
1008
- const updateData = {
1009
- updatedAt: now.toISOString()
1010
- };
1011
- if (title) {
1012
- updateData.title = title;
1013
- }
1014
- if (metadata) {
1015
- updateData.metadata = JSON.stringify(metadata);
1016
- }
1017
- await this.service.entities.thread.update({ entity: "thread", id }).set(updateData).go();
1018
- return {
1019
- ...existingThread,
1020
- title: title || existingThread.title,
1021
- metadata: metadata || existingThread.metadata,
2033
+ await this.service.entities.score.upsert(scoreData).go();
2034
+ const savedScore = {
2035
+ ...score,
2036
+ id: scoreId,
2037
+ createdAt: now,
1022
2038
  updatedAt: now
1023
2039
  };
2040
+ return { score: savedScore };
1024
2041
  } catch (error) {
1025
2042
  throw new MastraError(
1026
2043
  {
1027
- id: "STORAGE_DYNAMODB_STORE_UPDATE_THREAD_FAILED",
1028
- domain: ErrorDomain.STORAGE,
1029
- category: ErrorCategory.THIRD_PARTY,
1030
- details: { threadId: id }
1031
- },
1032
- error
1033
- );
1034
- }
1035
- }
1036
- async deleteThread({ threadId }) {
1037
- this.logger.debug("Deleting thread", { threadId });
1038
- try {
1039
- await this.service.entities.thread.delete({ entity: "thread", id: threadId }).go();
1040
- } catch (error) {
1041
- throw new MastraError(
1042
- {
1043
- id: "STORAGE_DYNAMODB_STORE_DELETE_THREAD_FAILED",
2044
+ id: "STORAGE_DYNAMODB_STORE_SAVE_SCORE_FAILED",
1044
2045
  domain: ErrorDomain.STORAGE,
1045
2046
  category: ErrorCategory.THIRD_PARTY,
1046
- details: { threadId }
2047
+ details: { scorerId: score.scorerId, runId: score.runId }
1047
2048
  },
1048
2049
  error
1049
2050
  );
1050
2051
  }
1051
2052
  }
1052
- async getMessages({
1053
- threadId,
1054
- resourceId,
1055
- selectBy,
1056
- format
2053
+ async listScoresByScorerId({
2054
+ scorerId,
2055
+ pagination,
2056
+ entityId,
2057
+ entityType,
2058
+ source
1057
2059
  }) {
1058
- this.logger.debug("Getting messages", { threadId, selectBy });
1059
2060
  try {
1060
- const query = this.service.entities.message.query.byThread({ entity: "message", threadId });
1061
- const limit = this.resolveMessageLimit({ last: selectBy?.last, defaultLimit: Number.MAX_SAFE_INTEGER });
1062
- if (limit !== Number.MAX_SAFE_INTEGER) {
1063
- const results2 = await query.go({ limit, order: "desc" });
1064
- const list2 = new MessageList({ threadId, resourceId }).add(
1065
- results2.data.map((data) => this.parseMessageData(data)),
1066
- "memory"
1067
- );
1068
- if (format === `v2`) return list2.get.all.v2();
1069
- return list2.get.all.v1();
1070
- }
2061
+ const query = this.service.entities.score.query.byScorer({ entity: "score", scorerId });
1071
2062
  const results = await query.go();
1072
- const list = new MessageList({ threadId, resourceId }).add(
1073
- results.data.map((data) => this.parseMessageData(data)),
1074
- "memory"
1075
- );
1076
- if (format === `v2`) return list.get.all.v2();
1077
- return list.get.all.v1();
1078
- } catch (error) {
1079
- throw new MastraError(
1080
- {
1081
- id: "STORAGE_DYNAMODB_STORE_GET_MESSAGES_FAILED",
1082
- domain: ErrorDomain.STORAGE,
1083
- category: ErrorCategory.THIRD_PARTY,
1084
- details: { threadId }
1085
- },
1086
- error
1087
- );
1088
- }
1089
- }
1090
- async saveMessages(args) {
1091
- const { messages, format = "v1" } = args;
1092
- this.logger.debug("Saving messages", { count: messages.length });
1093
- if (!messages.length) {
1094
- return [];
1095
- }
1096
- const threadId = messages[0]?.threadId;
1097
- if (!threadId) {
1098
- throw new Error("Thread ID is required");
1099
- }
1100
- const messagesToSave = messages.map((msg) => {
1101
- const now = (/* @__PURE__ */ new Date()).toISOString();
1102
- return {
1103
- entity: "message",
1104
- // Add entity type
1105
- id: msg.id,
1106
- threadId: msg.threadId,
1107
- role: msg.role,
1108
- type: msg.type,
1109
- resourceId: msg.resourceId,
1110
- // Ensure complex fields are stringified if not handled by attribute setters
1111
- content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content),
1112
- toolCallArgs: `toolCallArgs` in msg && msg.toolCallArgs ? JSON.stringify(msg.toolCallArgs) : void 0,
1113
- toolCallIds: `toolCallIds` in msg && msg.toolCallIds ? JSON.stringify(msg.toolCallIds) : void 0,
1114
- toolNames: `toolNames` in msg && msg.toolNames ? JSON.stringify(msg.toolNames) : void 0,
1115
- createdAt: msg.createdAt instanceof Date ? msg.createdAt.toISOString() : msg.createdAt || now,
1116
- updatedAt: now
1117
- // Add updatedAt
2063
+ let allScores = results.data.map((data) => this.parseScoreData(data));
2064
+ if (entityId) {
2065
+ allScores = allScores.filter((score) => score.entityId === entityId);
2066
+ }
2067
+ if (entityType) {
2068
+ allScores = allScores.filter((score) => score.entityType === entityType);
2069
+ }
2070
+ if (source) {
2071
+ allScores = allScores.filter((score) => score.source === source);
2072
+ }
2073
+ allScores.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
2074
+ const { page, perPage: perPageInput } = pagination;
2075
+ const perPage = normalizePerPage(perPageInput, Number.MAX_SAFE_INTEGER);
2076
+ const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
2077
+ const total = allScores.length;
2078
+ const end = perPageInput === false ? allScores.length : start + perPage;
2079
+ const paginatedScores = allScores.slice(start, end);
2080
+ return {
2081
+ scores: paginatedScores,
2082
+ pagination: {
2083
+ total,
2084
+ page,
2085
+ perPage: perPageForResponse,
2086
+ hasMore: end < total
2087
+ }
1118
2088
  };
1119
- });
1120
- try {
1121
- const batchSize = 25;
1122
- const batches = [];
1123
- for (let i = 0; i < messagesToSave.length; i += batchSize) {
1124
- const batch = messagesToSave.slice(i, i + batchSize);
1125
- batches.push(batch);
1126
- }
1127
- await Promise.all([
1128
- // Process message batches
1129
- ...batches.map(async (batch) => {
1130
- for (const messageData of batch) {
1131
- if (!messageData.entity) {
1132
- this.logger.error("Missing entity property in message data for create", { messageData });
1133
- throw new Error("Internal error: Missing entity property during saveMessages");
1134
- }
1135
- await this.service.entities.message.put(messageData).go();
1136
- }
1137
- }),
1138
- // Update thread's updatedAt timestamp
1139
- this.service.entities.thread.update({ entity: "thread", id: threadId }).set({
1140
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1141
- }).go()
1142
- ]);
1143
- const list = new MessageList().add(messages, "memory");
1144
- if (format === `v1`) return list.get.all.v1();
1145
- return list.get.all.v2();
1146
2089
  } catch (error) {
1147
2090
  throw new MastraError(
1148
2091
  {
1149
- id: "STORAGE_DYNAMODB_STORE_SAVE_MESSAGES_FAILED",
2092
+ id: "STORAGE_DYNAMODB_STORE_GET_SCORES_BY_SCORER_ID_FAILED",
1150
2093
  domain: ErrorDomain.STORAGE,
1151
2094
  category: ErrorCategory.THIRD_PARTY,
1152
- details: { count: messages.length }
2095
+ details: {
2096
+ scorerId: scorerId || "",
2097
+ entityId: entityId || "",
2098
+ entityType: entityType || "",
2099
+ source: source || "",
2100
+ page: pagination.page,
2101
+ perPage: pagination.perPage
2102
+ }
1153
2103
  },
1154
2104
  error
1155
2105
  );
1156
2106
  }
1157
2107
  }
1158
- // Helper function to parse message data (handle JSON fields)
1159
- parseMessageData(data) {
1160
- return {
1161
- ...data,
1162
- // Ensure dates are Date objects if needed (ElectroDB might return strings)
1163
- createdAt: data.createdAt ? new Date(data.createdAt) : void 0,
1164
- updatedAt: data.updatedAt ? new Date(data.updatedAt) : void 0
1165
- // Other fields like content, toolCallArgs etc. are assumed to be correctly
1166
- // transformed by the ElectroDB entity getters.
1167
- };
1168
- }
1169
- // Trace operations
1170
- async getTraces(args) {
1171
- const { name, scope, page, perPage } = args;
1172
- this.logger.debug("Getting traces", { name, scope, page, perPage });
2108
+ async listScoresByRunId({
2109
+ runId,
2110
+ pagination
2111
+ }) {
2112
+ this.logger.debug("Getting scores by run ID", { runId, pagination });
1173
2113
  try {
1174
- let query;
1175
- if (name) {
1176
- query = this.service.entities.trace.query.byName({ entity: "trace", name });
1177
- } else if (scope) {
1178
- query = this.service.entities.trace.query.byScope({ entity: "trace", scope });
1179
- } else {
1180
- this.logger.warn("Performing a scan operation on traces - consider using a more specific query");
1181
- query = this.service.entities.trace.scan;
1182
- }
1183
- let items = [];
1184
- let cursor = null;
1185
- let pagesFetched = 0;
1186
- const startPage = page > 0 ? page : 1;
1187
- do {
1188
- const results = await query.go({ cursor, limit: perPage });
1189
- pagesFetched++;
1190
- if (pagesFetched === startPage) {
1191
- items = results.data;
1192
- break;
1193
- }
1194
- cursor = results.cursor;
1195
- if (!cursor && results.data.length > 0 && pagesFetched < startPage) {
1196
- break;
2114
+ const query = this.service.entities.score.query.byRun({ entity: "score", runId });
2115
+ const results = await query.go();
2116
+ const allScores = results.data.map((data) => this.parseScoreData(data));
2117
+ allScores.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
2118
+ const { page, perPage: perPageInput } = pagination;
2119
+ const perPage = normalizePerPage(perPageInput, Number.MAX_SAFE_INTEGER);
2120
+ const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
2121
+ const total = allScores.length;
2122
+ const end = perPageInput === false ? allScores.length : start + perPage;
2123
+ const paginatedScores = allScores.slice(start, end);
2124
+ return {
2125
+ scores: paginatedScores,
2126
+ pagination: {
2127
+ total,
2128
+ page,
2129
+ perPage: perPageForResponse,
2130
+ hasMore: end < total
1197
2131
  }
1198
- } while (cursor && pagesFetched < startPage);
1199
- return items;
2132
+ };
1200
2133
  } catch (error) {
1201
2134
  throw new MastraError(
1202
2135
  {
1203
- id: "STORAGE_DYNAMODB_STORE_GET_TRACES_FAILED",
2136
+ id: "STORAGE_DYNAMODB_STORE_GET_SCORES_BY_RUN_ID_FAILED",
1204
2137
  domain: ErrorDomain.STORAGE,
1205
- category: ErrorCategory.THIRD_PARTY
2138
+ category: ErrorCategory.THIRD_PARTY,
2139
+ details: { runId, page: pagination.page, perPage: pagination.perPage }
1206
2140
  },
1207
2141
  error
1208
2142
  );
1209
2143
  }
1210
2144
  }
1211
- async batchTraceInsert({ records }) {
1212
- this.logger.debug("Batch inserting traces", { count: records.length });
1213
- if (!records.length) {
1214
- return;
2145
+ async listScoresByEntityId({
2146
+ entityId,
2147
+ entityType,
2148
+ pagination
2149
+ }) {
2150
+ this.logger.debug("Getting scores by entity ID", { entityId, entityType, pagination });
2151
+ try {
2152
+ const query = this.service.entities.score.query.byEntityData({ entity: "score", entityId });
2153
+ const results = await query.go();
2154
+ let allScores = results.data.map((data) => this.parseScoreData(data));
2155
+ allScores = allScores.filter((score) => score.entityType === entityType);
2156
+ allScores.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
2157
+ const { page, perPage: perPageInput } = pagination;
2158
+ const perPage = normalizePerPage(perPageInput, Number.MAX_SAFE_INTEGER);
2159
+ const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
2160
+ const total = allScores.length;
2161
+ const end = perPageInput === false ? allScores.length : start + perPage;
2162
+ const paginatedScores = allScores.slice(start, end);
2163
+ return {
2164
+ scores: paginatedScores,
2165
+ pagination: {
2166
+ total,
2167
+ page,
2168
+ perPage: perPageForResponse,
2169
+ hasMore: end < total
2170
+ }
2171
+ };
2172
+ } catch (error) {
2173
+ throw new MastraError(
2174
+ {
2175
+ id: "STORAGE_DYNAMODB_STORE_GET_SCORES_BY_ENTITY_ID_FAILED",
2176
+ domain: ErrorDomain.STORAGE,
2177
+ category: ErrorCategory.THIRD_PARTY,
2178
+ details: { entityId, entityType, page: pagination.page, perPage: pagination.perPage }
2179
+ },
2180
+ error
2181
+ );
1215
2182
  }
2183
+ }
2184
+ async listScoresBySpan({
2185
+ traceId,
2186
+ spanId,
2187
+ pagination
2188
+ }) {
2189
+ this.logger.debug("Getting scores by span", { traceId, spanId, pagination });
1216
2190
  try {
1217
- const recordsToSave = records.map((rec) => ({ entity: "trace", ...rec }));
1218
- await this.batchInsert({
1219
- tableName: TABLE_TRACES,
1220
- records: recordsToSave
1221
- // Pass records with 'entity' included
1222
- });
2191
+ const query = this.service.entities.score.query.bySpan({ entity: "score", traceId, spanId });
2192
+ const results = await query.go();
2193
+ const allScores = results.data.map((data) => this.parseScoreData(data));
2194
+ allScores.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
2195
+ const { page, perPage: perPageInput } = pagination;
2196
+ const perPage = normalizePerPage(perPageInput, Number.MAX_SAFE_INTEGER);
2197
+ const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
2198
+ const total = allScores.length;
2199
+ const end = perPageInput === false ? allScores.length : start + perPage;
2200
+ const paginatedScores = allScores.slice(start, end);
2201
+ return {
2202
+ scores: paginatedScores,
2203
+ pagination: {
2204
+ total,
2205
+ page,
2206
+ perPage: perPageForResponse,
2207
+ hasMore: end < total
2208
+ }
2209
+ };
1223
2210
  } catch (error) {
1224
2211
  throw new MastraError(
1225
2212
  {
1226
- id: "STORAGE_DYNAMODB_STORE_BATCH_TRACE_INSERT_FAILED",
2213
+ id: "STORAGE_DYNAMODB_STORE_GET_SCORES_BY_SPAN_FAILED",
1227
2214
  domain: ErrorDomain.STORAGE,
1228
2215
  category: ErrorCategory.THIRD_PARTY,
1229
- details: { count: records.length }
2216
+ details: { traceId, spanId, page: pagination.page, perPage: pagination.perPage }
1230
2217
  },
1231
2218
  error
1232
2219
  );
1233
2220
  }
1234
2221
  }
2222
+ };
2223
+ function formatWorkflowRun(snapshotData) {
2224
+ return {
2225
+ workflowName: snapshotData.workflow_name,
2226
+ runId: snapshotData.run_id,
2227
+ snapshot: snapshotData.snapshot,
2228
+ createdAt: new Date(snapshotData.createdAt),
2229
+ updatedAt: new Date(snapshotData.updatedAt),
2230
+ resourceId: snapshotData.resourceId
2231
+ };
2232
+ }
2233
+ var WorkflowStorageDynamoDB = class extends WorkflowsStorage {
2234
+ service;
2235
+ constructor({ service }) {
2236
+ super();
2237
+ this.service = service;
2238
+ }
2239
+ updateWorkflowResults({
2240
+ // workflowName,
2241
+ // runId,
2242
+ // stepId,
2243
+ // result,
2244
+ // requestContext,
2245
+ }) {
2246
+ throw new Error("Method not implemented.");
2247
+ }
2248
+ updateWorkflowState({
2249
+ // workflowName,
2250
+ // runId,
2251
+ // opts,
2252
+ }) {
2253
+ throw new Error("Method not implemented.");
2254
+ }
1235
2255
  // Workflow operations
1236
2256
  async persistWorkflowSnapshot({
1237
2257
  workflowName,
1238
2258
  runId,
2259
+ resourceId,
1239
2260
  snapshot
1240
2261
  }) {
1241
2262
  this.logger.debug("Persisting workflow snapshot", { workflowName, runId });
1242
2263
  try {
1243
- const resourceId = "resourceId" in snapshot ? snapshot.resourceId : void 0;
1244
2264
  const now = (/* @__PURE__ */ new Date()).toISOString();
1245
2265
  const data = {
1246
2266
  entity: "workflow_snapshot",
@@ -1248,12 +2268,11 @@ var DynamoDBStore = class extends MastraStorage {
1248
2268
  workflow_name: workflowName,
1249
2269
  run_id: runId,
1250
2270
  snapshot: JSON.stringify(snapshot),
1251
- // Stringify the snapshot object
1252
2271
  createdAt: now,
1253
2272
  updatedAt: now,
1254
2273
  resourceId
1255
2274
  };
1256
- await this.service.entities.workflowSnapshot.upsert(data).go();
2275
+ await this.service.entities.workflow_snapshot.upsert(data).go();
1257
2276
  } catch (error) {
1258
2277
  throw new MastraError(
1259
2278
  {
@@ -1272,7 +2291,7 @@ var DynamoDBStore = class extends MastraStorage {
1272
2291
  }) {
1273
2292
  this.logger.debug("Loading workflow snapshot", { workflowName, runId });
1274
2293
  try {
1275
- const result = await this.service.entities.workflowSnapshot.get({
2294
+ const result = await this.service.entities.workflow_snapshot.get({
1276
2295
  entity: "workflow_snapshot",
1277
2296
  // Add entity type
1278
2297
  workflow_name: workflowName,
@@ -1294,21 +2313,34 @@ var DynamoDBStore = class extends MastraStorage {
1294
2313
  );
1295
2314
  }
1296
2315
  }
1297
- async getWorkflowRuns(args) {
2316
+ async listWorkflowRuns(args) {
1298
2317
  this.logger.debug("Getting workflow runs", { args });
1299
2318
  try {
1300
- const limit = args?.limit || 10;
1301
- const offset = args?.offset || 0;
2319
+ const perPage = args?.perPage !== void 0 ? args.perPage : 10;
2320
+ const page = args?.page !== void 0 ? args.page : 0;
2321
+ if (page < 0) {
2322
+ throw new MastraError(
2323
+ {
2324
+ id: "DYNAMODB_STORE_INVALID_PAGE",
2325
+ domain: ErrorDomain.STORAGE,
2326
+ category: ErrorCategory.USER,
2327
+ details: { page }
2328
+ },
2329
+ new Error("page must be >= 0")
2330
+ );
2331
+ }
2332
+ const normalizedPerPage = normalizePerPage(perPage, 10);
2333
+ const offset = page * normalizedPerPage;
1302
2334
  let query;
1303
2335
  if (args?.workflowName) {
1304
- query = this.service.entities.workflowSnapshot.query.primary({
2336
+ query = this.service.entities.workflow_snapshot.query.primary({
1305
2337
  entity: "workflow_snapshot",
1306
2338
  // Add entity type
1307
2339
  workflow_name: args.workflowName
1308
2340
  });
1309
2341
  } else {
1310
2342
  this.logger.warn("Performing a scan operation on workflow snapshots - consider using a more specific query");
1311
- query = this.service.entities.workflowSnapshot.scan;
2343
+ query = this.service.entities.workflow_snapshot.scan;
1312
2344
  }
1313
2345
  const allMatchingSnapshots = [];
1314
2346
  let cursor = null;
@@ -1320,6 +2352,11 @@ var DynamoDBStore = class extends MastraStorage {
1320
2352
  });
1321
2353
  if (pageResults.data && pageResults.data.length > 0) {
1322
2354
  let pageFilteredData = pageResults.data;
2355
+ if (args?.status) {
2356
+ pageFilteredData = pageFilteredData.filter((snapshot) => {
2357
+ return snapshot.snapshot.status === args.status;
2358
+ });
2359
+ }
1323
2360
  if (args?.fromDate || args?.toDate) {
1324
2361
  pageFilteredData = pageFilteredData.filter((snapshot) => {
1325
2362
  const createdAt = new Date(snapshot.createdAt);
@@ -1345,8 +2382,8 @@ var DynamoDBStore = class extends MastraStorage {
1345
2382
  return { runs: [], total: 0 };
1346
2383
  }
1347
2384
  const total = allMatchingSnapshots.length;
1348
- const paginatedData = allMatchingSnapshots.slice(offset, offset + limit);
1349
- const runs = paginatedData.map((snapshot) => this.formatWorkflowRun(snapshot));
2385
+ const paginatedData = allMatchingSnapshots.slice(offset, offset + normalizedPerPage);
2386
+ const runs = paginatedData.map((snapshot) => formatWorkflowRun(snapshot));
1350
2387
  return {
1351
2388
  runs,
1352
2389
  total
@@ -1354,7 +2391,7 @@ var DynamoDBStore = class extends MastraStorage {
1354
2391
  } catch (error) {
1355
2392
  throw new MastraError(
1356
2393
  {
1357
- id: "STORAGE_DYNAMODB_STORE_GET_WORKFLOW_RUNS_FAILED",
2394
+ id: "STORAGE_DYNAMODB_STORE_LIST_WORKFLOW_RUNS_FAILED",
1358
2395
  domain: ErrorDomain.STORAGE,
1359
2396
  category: ErrorCategory.THIRD_PARTY,
1360
2397
  details: { workflowName: args?.workflowName || "", resourceId: args?.resourceId || "" }
@@ -1369,7 +2406,7 @@ var DynamoDBStore = class extends MastraStorage {
1369
2406
  try {
1370
2407
  if (workflowName) {
1371
2408
  this.logger.debug("WorkflowName provided, using direct GET operation.");
1372
- const result2 = await this.service.entities.workflowSnapshot.get({
2409
+ const result2 = await this.service.entities.workflow_snapshot.get({
1373
2410
  entity: "workflow_snapshot",
1374
2411
  // Entity type for PK
1375
2412
  workflow_name: workflowName,
@@ -1391,7 +2428,7 @@ var DynamoDBStore = class extends MastraStorage {
1391
2428
  this.logger.debug(
1392
2429
  '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.'
1393
2430
  );
1394
- const result = await this.service.entities.workflowSnapshot.query.gsi2({ entity: "workflow_snapshot", run_id: runId }).go();
2431
+ const result = await this.service.entities.workflow_snapshot.query.gsi2({ entity: "workflow_snapshot", run_id: runId }).go();
1395
2432
  const matchingRunDbItem = result.data && result.data.length > 0 ? result.data[0] : null;
1396
2433
  if (!matchingRunDbItem) {
1397
2434
  return null;
@@ -1417,121 +2454,232 @@ var DynamoDBStore = class extends MastraStorage {
1417
2454
  );
1418
2455
  }
1419
2456
  }
1420
- // Helper function to format workflow run
1421
- formatWorkflowRun(snapshotData) {
1422
- return {
1423
- workflowName: snapshotData.workflow_name,
1424
- runId: snapshotData.run_id,
1425
- snapshot: snapshotData.snapshot,
1426
- createdAt: new Date(snapshotData.createdAt),
1427
- updatedAt: new Date(snapshotData.updatedAt),
1428
- resourceId: snapshotData.resourceId
1429
- };
2457
+ };
2458
+
2459
+ // src/storage/index.ts
2460
+ var DynamoDBStore = class extends MastraStorage {
2461
+ tableName;
2462
+ client;
2463
+ service;
2464
+ hasInitialized = null;
2465
+ stores;
2466
+ constructor({ name, config }) {
2467
+ super({ id: config.id, name });
2468
+ try {
2469
+ if (!config.tableName || typeof config.tableName !== "string" || config.tableName.trim() === "") {
2470
+ throw new Error("DynamoDBStore: config.tableName must be provided and cannot be empty.");
2471
+ }
2472
+ if (!/^[a-zA-Z0-9_.-]{3,255}$/.test(config.tableName)) {
2473
+ throw new Error(
2474
+ `DynamoDBStore: config.tableName "${config.tableName}" contains invalid characters or is not between 3 and 255 characters long.`
2475
+ );
2476
+ }
2477
+ const dynamoClient = new DynamoDBClient({
2478
+ region: config.region || "us-east-1",
2479
+ endpoint: config.endpoint,
2480
+ credentials: config.credentials
2481
+ });
2482
+ this.tableName = config.tableName;
2483
+ this.client = DynamoDBDocumentClient.from(dynamoClient);
2484
+ this.service = getElectroDbService(this.client, this.tableName);
2485
+ const operations = new StoreOperationsDynamoDB({
2486
+ service: this.service,
2487
+ tableName: this.tableName,
2488
+ client: this.client
2489
+ });
2490
+ const workflows = new WorkflowStorageDynamoDB({ service: this.service });
2491
+ const memory = new MemoryStorageDynamoDB({ service: this.service });
2492
+ const scores = new ScoresStorageDynamoDB({ service: this.service });
2493
+ this.stores = {
2494
+ operations,
2495
+ workflows,
2496
+ memory,
2497
+ scores
2498
+ };
2499
+ } catch (error) {
2500
+ throw new MastraError(
2501
+ {
2502
+ id: "STORAGE_DYNAMODB_STORE_CONSTRUCTOR_FAILED",
2503
+ domain: ErrorDomain.STORAGE,
2504
+ category: ErrorCategory.USER
2505
+ },
2506
+ error
2507
+ );
2508
+ }
1430
2509
  }
1431
- // Helper methods for entity/table mapping
1432
- getEntityNameForTable(tableName) {
1433
- const mapping = {
1434
- [TABLE_THREADS]: "thread",
1435
- [TABLE_MESSAGES]: "message",
1436
- [TABLE_WORKFLOW_SNAPSHOT]: "workflowSnapshot",
1437
- [TABLE_EVALS]: "eval",
1438
- [TABLE_TRACES]: "trace"
2510
+ get supports() {
2511
+ return {
2512
+ selectByIncludeResourceScope: true,
2513
+ resourceWorkingMemory: true,
2514
+ hasColumn: false,
2515
+ createTable: false,
2516
+ deleteMessages: false,
2517
+ listScoresBySpan: true
1439
2518
  };
1440
- return mapping[tableName] || null;
1441
2519
  }
1442
- // Eval operations
1443
- async getEvalsByAgentName(agentName, type) {
1444
- this.logger.debug("Getting evals for agent", { agentName, type });
2520
+ /**
2521
+ * Validates that the required DynamoDB table exists and is accessible.
2522
+ * This does not check the table structure - it assumes the table
2523
+ * was created with the correct structure via CDK/CloudFormation.
2524
+ */
2525
+ async validateTableExists() {
1445
2526
  try {
1446
- const query = this.service.entities.eval.query.byAgent({ entity: "eval", agent_name: agentName });
1447
- const results = await query.go({ order: "desc", limit: 100 });
1448
- if (!results.data.length) {
1449
- return [];
1450
- }
1451
- let filteredData = results.data;
1452
- if (type) {
1453
- filteredData = filteredData.filter((evalRecord) => {
1454
- try {
1455
- const testInfo = evalRecord.test_info && typeof evalRecord.test_info === "string" ? JSON.parse(evalRecord.test_info) : void 0;
1456
- if (type === "test" && !testInfo) {
1457
- return false;
1458
- }
1459
- if (type === "live" && testInfo) {
1460
- return false;
1461
- }
1462
- } catch (e) {
1463
- this.logger.warn("Failed to parse test_info during filtering", { record: evalRecord, error: e });
1464
- }
1465
- return true;
1466
- });
1467
- }
1468
- return filteredData.map((evalRecord) => {
1469
- try {
1470
- return {
1471
- input: evalRecord.input,
1472
- output: evalRecord.output,
1473
- // Safely parse result and test_info
1474
- result: evalRecord.result && typeof evalRecord.result === "string" ? JSON.parse(evalRecord.result) : void 0,
1475
- agentName: evalRecord.agent_name,
1476
- createdAt: evalRecord.created_at,
1477
- // Keep as string from DDB?
1478
- metricName: evalRecord.metric_name,
1479
- instructions: evalRecord.instructions,
1480
- runId: evalRecord.run_id,
1481
- globalRunId: evalRecord.global_run_id,
1482
- testInfo: evalRecord.test_info && typeof evalRecord.test_info === "string" ? JSON.parse(evalRecord.test_info) : void 0
1483
- };
1484
- } catch (parseError) {
1485
- this.logger.error("Failed to parse eval record", { record: evalRecord, error: parseError });
1486
- return {
1487
- agentName: evalRecord.agent_name,
1488
- createdAt: evalRecord.created_at,
1489
- runId: evalRecord.run_id,
1490
- globalRunId: evalRecord.global_run_id
1491
- };
1492
- }
2527
+ const command = new DescribeTableCommand({
2528
+ TableName: this.tableName
1493
2529
  });
2530
+ await this.client.send(command);
2531
+ return true;
1494
2532
  } catch (error) {
2533
+ if (error.name === "ResourceNotFoundException") {
2534
+ return false;
2535
+ }
1495
2536
  throw new MastraError(
1496
2537
  {
1497
- id: "STORAGE_DYNAMODB_STORE_GET_EVALS_BY_AGENT_NAME_FAILED",
2538
+ id: "STORAGE_DYNAMODB_STORE_VALIDATE_TABLE_EXISTS_FAILED",
1498
2539
  domain: ErrorDomain.STORAGE,
1499
2540
  category: ErrorCategory.THIRD_PARTY,
1500
- details: { agentName }
2541
+ details: { tableName: this.tableName }
1501
2542
  },
1502
2543
  error
1503
2544
  );
1504
2545
  }
1505
2546
  }
1506
- async getTracesPaginated(_args) {
1507
- throw new MastraError(
1508
- {
1509
- id: "STORAGE_DYNAMODB_STORE_GET_TRACES_PAGINATED_FAILED",
1510
- domain: ErrorDomain.STORAGE,
1511
- category: ErrorCategory.THIRD_PARTY
1512
- },
1513
- new Error("Method not implemented.")
1514
- );
2547
+ /**
2548
+ * Initialize storage, validating the externally managed table is accessible.
2549
+ * For the single-table design, we only validate once that we can access
2550
+ * the table that was created via CDK/CloudFormation.
2551
+ */
2552
+ async init() {
2553
+ if (this.hasInitialized === null) {
2554
+ this.hasInitialized = this._performInitializationAndStore();
2555
+ }
2556
+ try {
2557
+ await this.hasInitialized;
2558
+ } catch (error) {
2559
+ throw new MastraError(
2560
+ {
2561
+ id: "STORAGE_DYNAMODB_STORE_INIT_FAILED",
2562
+ domain: ErrorDomain.STORAGE,
2563
+ category: ErrorCategory.THIRD_PARTY,
2564
+ details: { tableName: this.tableName }
2565
+ },
2566
+ error
2567
+ );
2568
+ }
1515
2569
  }
1516
- async getThreadsByResourceIdPaginated(_args) {
1517
- throw new MastraError(
1518
- {
1519
- id: "STORAGE_DYNAMODB_STORE_GET_THREADS_BY_RESOURCE_ID_PAGINATED_FAILED",
1520
- domain: ErrorDomain.STORAGE,
1521
- category: ErrorCategory.THIRD_PARTY
1522
- },
1523
- new Error("Method not implemented.")
1524
- );
2570
+ /**
2571
+ * Performs the actual table validation and stores the promise.
2572
+ * Handles resetting the stored promise on failure to allow retries.
2573
+ */
2574
+ _performInitializationAndStore() {
2575
+ return this.validateTableExists().then((exists) => {
2576
+ if (!exists) {
2577
+ throw new Error(
2578
+ `Table ${this.tableName} does not exist or is not accessible. Ensure it's created via CDK/CloudFormation before using this store.`
2579
+ );
2580
+ }
2581
+ return true;
2582
+ }).catch((err) => {
2583
+ this.hasInitialized = null;
2584
+ throw err;
2585
+ });
1525
2586
  }
1526
- async getMessagesPaginated(_args) {
1527
- throw new MastraError(
1528
- {
1529
- id: "STORAGE_DYNAMODB_STORE_GET_MESSAGES_PAGINATED_FAILED",
1530
- domain: ErrorDomain.STORAGE,
1531
- category: ErrorCategory.THIRD_PARTY
1532
- },
1533
- new Error("Method not implemented.")
1534
- );
2587
+ async createTable({ tableName, schema }) {
2588
+ return this.stores.operations.createTable({ tableName, schema });
2589
+ }
2590
+ async alterTable(_args) {
2591
+ return this.stores.operations.alterTable(_args);
2592
+ }
2593
+ async clearTable({ tableName }) {
2594
+ return this.stores.operations.clearTable({ tableName });
2595
+ }
2596
+ async dropTable({ tableName }) {
2597
+ return this.stores.operations.dropTable({ tableName });
2598
+ }
2599
+ async insert({ tableName, record }) {
2600
+ return this.stores.operations.insert({ tableName, record });
2601
+ }
2602
+ async batchInsert({ tableName, records }) {
2603
+ return this.stores.operations.batchInsert({ tableName, records });
2604
+ }
2605
+ async load({ tableName, keys }) {
2606
+ return this.stores.operations.load({ tableName, keys });
2607
+ }
2608
+ // Thread operations
2609
+ async getThreadById({ threadId }) {
2610
+ return this.stores.memory.getThreadById({ threadId });
2611
+ }
2612
+ async saveThread({ thread }) {
2613
+ return this.stores.memory.saveThread({ thread });
2614
+ }
2615
+ async updateThread({
2616
+ id,
2617
+ title,
2618
+ metadata
2619
+ }) {
2620
+ return this.stores.memory.updateThread({ id, title, metadata });
2621
+ }
2622
+ async deleteThread({ threadId }) {
2623
+ return this.stores.memory.deleteThread({ threadId });
2624
+ }
2625
+ async listMessagesById(args) {
2626
+ return this.stores.memory.listMessagesById(args);
2627
+ }
2628
+ async saveMessages(args) {
2629
+ return this.stores.memory.saveMessages(args);
2630
+ }
2631
+ async updateMessages(_args) {
2632
+ return this.stores.memory.updateMessages(_args);
2633
+ }
2634
+ // Workflow operations
2635
+ async updateWorkflowResults({
2636
+ workflowName,
2637
+ runId,
2638
+ stepId,
2639
+ result,
2640
+ requestContext
2641
+ }) {
2642
+ return this.stores.workflows.updateWorkflowResults({ workflowName, runId, stepId, result, requestContext });
2643
+ }
2644
+ async updateWorkflowState({
2645
+ workflowName,
2646
+ runId,
2647
+ opts
2648
+ }) {
2649
+ return this.stores.workflows.updateWorkflowState({ workflowName, runId, opts });
2650
+ }
2651
+ async persistWorkflowSnapshot({
2652
+ workflowName,
2653
+ runId,
2654
+ resourceId,
2655
+ snapshot
2656
+ }) {
2657
+ return this.stores.workflows.persistWorkflowSnapshot({ workflowName, runId, resourceId, snapshot });
2658
+ }
2659
+ async loadWorkflowSnapshot({
2660
+ workflowName,
2661
+ runId
2662
+ }) {
2663
+ return this.stores.workflows.loadWorkflowSnapshot({ workflowName, runId });
2664
+ }
2665
+ async listWorkflowRuns(args) {
2666
+ return this.stores.workflows.listWorkflowRuns(args);
2667
+ }
2668
+ async getWorkflowRunById(args) {
2669
+ return this.stores.workflows.getWorkflowRunById(args);
2670
+ }
2671
+ async getResourceById({ resourceId }) {
2672
+ return this.stores.memory.getResourceById({ resourceId });
2673
+ }
2674
+ async saveResource({ resource }) {
2675
+ return this.stores.memory.saveResource({ resource });
2676
+ }
2677
+ async updateResource({
2678
+ resourceId,
2679
+ workingMemory,
2680
+ metadata
2681
+ }) {
2682
+ return this.stores.memory.updateResource({ resourceId, workingMemory, metadata });
1535
2683
  }
1536
2684
  /**
1537
2685
  * Closes the DynamoDB client connection and cleans up resources.
@@ -1553,10 +2701,50 @@ var DynamoDBStore = class extends MastraStorage {
1553
2701
  );
1554
2702
  }
1555
2703
  }
1556
- async updateMessages(_args) {
1557
- this.logger.error("updateMessages is not yet implemented in DynamoDBStore");
1558
- throw new Error("Method not implemented");
2704
+ /**
2705
+ * SCORERS - Not implemented
2706
+ */
2707
+ async getScoreById({ id: _id }) {
2708
+ return this.stores.scores.getScoreById({ id: _id });
2709
+ }
2710
+ async saveScore(_score) {
2711
+ return this.stores.scores.saveScore(_score);
2712
+ }
2713
+ async listScoresByRunId({
2714
+ runId: _runId,
2715
+ pagination: _pagination
2716
+ }) {
2717
+ return this.stores.scores.listScoresByRunId({ runId: _runId, pagination: _pagination });
2718
+ }
2719
+ async listScoresByEntityId({
2720
+ entityId: _entityId,
2721
+ entityType: _entityType,
2722
+ pagination: _pagination
2723
+ }) {
2724
+ return this.stores.scores.listScoresByEntityId({
2725
+ entityId: _entityId,
2726
+ entityType: _entityType,
2727
+ pagination: _pagination
2728
+ });
2729
+ }
2730
+ async listScoresByScorerId({
2731
+ scorerId,
2732
+ source,
2733
+ entityId,
2734
+ entityType,
2735
+ pagination
2736
+ }) {
2737
+ return this.stores.scores.listScoresByScorerId({ scorerId, source, entityId, entityType, pagination });
2738
+ }
2739
+ async listScoresBySpan({
2740
+ traceId,
2741
+ spanId,
2742
+ pagination
2743
+ }) {
2744
+ return this.stores.scores.listScoresBySpan({ traceId, spanId, pagination });
1559
2745
  }
1560
2746
  };
1561
2747
 
1562
2748
  export { DynamoDBStore };
2749
+ //# sourceMappingURL=index.js.map
2750
+ //# sourceMappingURL=index.js.map