@mastra/dynamodb 0.13.0 → 0.13.1

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