@mastra/dynamodb 0.0.0-error-handler-fix-20251020202607 → 0.0.0-execa-dynamic-import-20260304221256

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/CHANGELOG.md +1442 -3
  2. package/LICENSE.md +15 -0
  3. package/README.md +1 -2
  4. package/dist/docs/SKILL.md +22 -0
  5. package/dist/docs/assets/SOURCE_MAP.json +6 -0
  6. package/dist/docs/references/reference-storage-dynamodb.md +282 -0
  7. package/dist/entities/eval.d.ts +8 -0
  8. package/dist/entities/eval.d.ts.map +1 -1
  9. package/dist/entities/index.d.ts +66 -5
  10. package/dist/entities/index.d.ts.map +1 -1
  11. package/dist/entities/message.d.ts +8 -0
  12. package/dist/entities/message.d.ts.map +1 -1
  13. package/dist/entities/resource.d.ts +8 -0
  14. package/dist/entities/resource.d.ts.map +1 -1
  15. package/dist/entities/score.d.ts +18 -5
  16. package/dist/entities/score.d.ts.map +1 -1
  17. package/dist/entities/thread.d.ts +8 -0
  18. package/dist/entities/thread.d.ts.map +1 -1
  19. package/dist/entities/trace.d.ts +8 -0
  20. package/dist/entities/trace.d.ts.map +1 -1
  21. package/dist/entities/utils.d.ts +90 -0
  22. package/dist/entities/utils.d.ts.map +1 -1
  23. package/dist/entities/workflow-snapshot.d.ts +8 -0
  24. package/dist/entities/workflow-snapshot.d.ts.map +1 -1
  25. package/dist/index.cjs +786 -1269
  26. package/dist/index.cjs.map +1 -1
  27. package/dist/index.js +780 -1270
  28. package/dist/index.js.map +1 -1
  29. package/dist/storage/db/index.d.ts +48 -0
  30. package/dist/storage/db/index.d.ts.map +1 -0
  31. package/dist/storage/domains/memory/index.d.ts +18 -45
  32. package/dist/storage/domains/memory/index.d.ts.map +1 -1
  33. package/dist/storage/domains/scores/index.d.ts +47 -0
  34. package/dist/storage/domains/scores/index.d.ts.map +1 -0
  35. package/dist/storage/domains/utils.d.ts +7 -0
  36. package/dist/storage/domains/utils.d.ts.map +1 -0
  37. package/dist/storage/domains/workflows/index.d.ts +26 -24
  38. package/dist/storage/domains/workflows/index.d.ts.map +1 -1
  39. package/dist/storage/index.d.ts +177 -221
  40. package/dist/storage/index.d.ts.map +1 -1
  41. package/dist/storage/ttl.d.ts +52 -0
  42. package/dist/storage/ttl.d.ts.map +1 -0
  43. package/package.json +17 -16
  44. package/dist/storage/domains/legacy-evals/index.d.ts +0 -19
  45. package/dist/storage/domains/legacy-evals/index.d.ts.map +0 -1
  46. package/dist/storage/domains/operations/index.d.ts +0 -69
  47. package/dist/storage/domains/operations/index.d.ts.map +0 -1
  48. package/dist/storage/domains/score/index.d.ts +0 -51
  49. package/dist/storage/domains/score/index.d.ts.map +0 -1
  50. package/dist/storage/domains/traces/index.d.ts +0 -28
  51. package/dist/storage/domains/traces/index.d.ts.map +0 -1
package/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import { DynamoDBClient, DescribeTableCommand } from '@aws-sdk/client-dynamodb';
2
2
  import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb';
3
3
  import { MastraError, ErrorCategory, ErrorDomain } from '@mastra/core/error';
4
- import { MastraStorage, StoreOperations, TracesStorage, TABLE_TRACES, WorkflowsStorage, MemoryStorage, resolveMessageLimit, ScoresStorage, LegacyEvalsStorage, TABLE_AI_SPANS, TABLE_RESOURCES, TABLE_SCORERS, TABLE_EVALS, TABLE_WORKFLOW_SNAPSHOT, TABLE_MESSAGES, TABLE_THREADS } from '@mastra/core/storage';
4
+ import { MemoryStorage, createStorageErrorId, normalizePerPage, calculatePagination, filterByDateRange, ScoresStorage, SCORERS_SCHEMA, WorkflowsStorage, MastraCompositeStore, TABLE_SCORERS, TABLE_WORKFLOW_SNAPSHOT, TABLE_RESOURCES, TABLE_MESSAGES, TABLE_THREADS } from '@mastra/core/storage';
5
5
  import { Entity, Service } from 'electrodb';
6
6
  import { MessageList } from '@mastra/core/agent';
7
- import { saveScorePayloadSchema } from '@mastra/core/scores';
7
+ import { saveScorePayloadSchema } from '@mastra/core/evals';
8
8
 
9
9
  // src/storage/index.ts
10
10
 
@@ -58,6 +58,25 @@ var baseAttributes = {
58
58
  }
59
59
  return value;
60
60
  }
61
+ },
62
+ /**
63
+ * TTL attribute for DynamoDB automatic item expiration.
64
+ * This is a Unix timestamp (epoch seconds) that indicates when the item should be deleted.
65
+ *
66
+ * Note: For TTL to work, you must enable TTL on your DynamoDB table
67
+ * specifying this attribute name (default: 'ttl').
68
+ */
69
+ ttl: {
70
+ type: "number",
71
+ required: false
72
+ },
73
+ /**
74
+ * Alternative TTL attribute with configurable name.
75
+ * Use this if you've configured TTL on your DynamoDB table with 'expiresAt' as the attribute name.
76
+ */
77
+ expiresAt: {
78
+ type: "number",
79
+ required: false
61
80
  }
62
81
  };
63
82
 
@@ -423,6 +442,10 @@ var scoreEntity = new Entity({
423
442
  return value;
424
443
  }
425
444
  },
445
+ preprocessPrompt: {
446
+ type: "string",
447
+ required: false
448
+ },
426
449
  preprocessStepResult: {
427
450
  type: "string",
428
451
  required: false,
@@ -562,7 +585,29 @@ var scoreEntity = new Entity({
562
585
  return value;
563
586
  }
564
587
  },
565
- runtimeContext: {
588
+ metadata: {
589
+ type: "string",
590
+ required: false,
591
+ set: (value) => {
592
+ if (value && typeof value !== "string") {
593
+ return JSON.stringify(value);
594
+ }
595
+ return value;
596
+ },
597
+ get: (value) => {
598
+ if (value && typeof value === "string") {
599
+ try {
600
+ if (value.startsWith("{") || value.startsWith("[")) {
601
+ return JSON.parse(value);
602
+ }
603
+ } catch {
604
+ return value;
605
+ }
606
+ }
607
+ return value;
608
+ }
609
+ },
610
+ requestContext: {
566
611
  type: "string",
567
612
  required: false,
568
613
  set: (value) => {
@@ -933,193 +978,146 @@ function getElectroDbService(client, tableName) {
933
978
  }
934
979
  );
935
980
  }
936
- var LegacyEvalsDynamoDB = class extends LegacyEvalsStorage {
981
+ function resolveDynamoDBConfig(config) {
982
+ if ("service" in config) {
983
+ return { service: config.service, ttl: config.ttl };
984
+ }
985
+ const dynamoClient = new DynamoDBClient({
986
+ region: config.region || "us-east-1",
987
+ endpoint: config.endpoint,
988
+ credentials: config.credentials
989
+ });
990
+ const client = DynamoDBDocumentClient.from(dynamoClient);
991
+ return {
992
+ service: getElectroDbService(client, config.tableName),
993
+ ttl: config.ttl
994
+ };
995
+ }
996
+
997
+ // src/storage/ttl.ts
998
+ function calculateTtl(entityName, ttlConfig, customTtlSeconds) {
999
+ const entityConfig = ttlConfig?.[entityName];
1000
+ if (!entityConfig?.enabled) {
1001
+ return void 0;
1002
+ }
1003
+ const ttlSeconds = customTtlSeconds ?? entityConfig.defaultTtlSeconds;
1004
+ if (ttlSeconds === void 0 || ttlSeconds <= 0) {
1005
+ return void 0;
1006
+ }
1007
+ return Math.floor(Date.now() / 1e3) + ttlSeconds;
1008
+ }
1009
+ function getTtlAttributeName(entityName, ttlConfig) {
1010
+ const entityConfig = ttlConfig?.[entityName];
1011
+ return entityConfig?.attributeName ?? "ttl";
1012
+ }
1013
+ function isTtlEnabled(entityName, ttlConfig) {
1014
+ return ttlConfig?.[entityName]?.enabled === true;
1015
+ }
1016
+ function getTtlProps(entityName, ttlConfig, customTtlSeconds) {
1017
+ const ttlValue = calculateTtl(entityName, ttlConfig, customTtlSeconds);
1018
+ if (ttlValue === void 0) return {};
1019
+ const attributeName = getTtlAttributeName(entityName, ttlConfig);
1020
+ return { [attributeName]: ttlValue };
1021
+ }
1022
+ var ENTITY_MAP = {
1023
+ [TABLE_THREADS]: "thread",
1024
+ [TABLE_MESSAGES]: "message",
1025
+ [TABLE_RESOURCES]: "resource",
1026
+ [TABLE_WORKFLOW_SNAPSHOT]: "workflow_snapshot",
1027
+ [TABLE_SCORERS]: "score"
1028
+ };
1029
+ function getDeleteKey(entityName, item) {
1030
+ const key = { entity: entityName };
1031
+ switch (entityName) {
1032
+ case "thread":
1033
+ case "message":
1034
+ case "resource":
1035
+ case "score":
1036
+ key.id = item.id;
1037
+ break;
1038
+ case "workflow_snapshot":
1039
+ key.workflow_name = item.workflow_name;
1040
+ key.run_id = item.run_id;
1041
+ break;
1042
+ default:
1043
+ key.id = item.id;
1044
+ }
1045
+ return key;
1046
+ }
1047
+ async function deleteTableData(service, tableName) {
1048
+ const entityName = ENTITY_MAP[tableName];
1049
+ if (!entityName || !service.entities[entityName]) {
1050
+ throw new Error(`No entity mapping found for table: ${tableName}`);
1051
+ }
1052
+ const entity = service.entities[entityName];
1053
+ const result = await entity.scan.go({ pages: "all" });
1054
+ if (!result.data.length) {
1055
+ return;
1056
+ }
1057
+ const batchSize = 25;
1058
+ for (let i = 0; i < result.data.length; i += batchSize) {
1059
+ const batch = result.data.slice(i, i + batchSize);
1060
+ const keysToDelete = batch.map((item) => getDeleteKey(entityName, item));
1061
+ await entity.delete(keysToDelete).go();
1062
+ }
1063
+ }
1064
+
1065
+ // src/storage/domains/memory/index.ts
1066
+ var MemoryStorageDynamoDB = class extends MemoryStorage {
937
1067
  service;
938
- tableName;
939
- constructor({ service, tableName }) {
1068
+ ttlConfig;
1069
+ constructor(config) {
940
1070
  super();
941
- this.service = service;
942
- this.tableName = tableName;
1071
+ const resolved = resolveDynamoDBConfig(config);
1072
+ this.service = resolved.service;
1073
+ this.ttlConfig = resolved.ttl;
943
1074
  }
944
- // Eval operations
945
- async getEvalsByAgentName(agentName, type) {
946
- this.logger.debug("Getting evals for agent", { agentName, type });
947
- try {
948
- const query = this.service.entities.eval.query.byAgent({ entity: "eval", agent_name: agentName });
949
- const results = await query.go({ order: "desc", limit: 100 });
950
- if (!results.data.length) {
951
- return [];
952
- }
953
- let filteredData = results.data;
954
- if (type) {
955
- filteredData = filteredData.filter((evalRecord) => {
956
- try {
957
- const testInfo = evalRecord.test_info && typeof evalRecord.test_info === "string" ? JSON.parse(evalRecord.test_info) : void 0;
958
- if (type === "test" && !testInfo) {
959
- return false;
960
- }
961
- if (type === "live" && testInfo) {
962
- return false;
963
- }
964
- } catch (e) {
965
- this.logger.warn("Failed to parse test_info during filtering", { record: evalRecord, error: e });
966
- }
967
- return true;
968
- });
969
- }
970
- return filteredData.map((evalRecord) => {
971
- try {
972
- return {
973
- input: evalRecord.input,
974
- output: evalRecord.output,
975
- // Safely parse result and test_info
976
- result: evalRecord.result && typeof evalRecord.result === "string" ? JSON.parse(evalRecord.result) : void 0,
977
- agentName: evalRecord.agent_name,
978
- createdAt: evalRecord.created_at,
979
- // Keep as string from DDB?
980
- metricName: evalRecord.metric_name,
981
- instructions: evalRecord.instructions,
982
- runId: evalRecord.run_id,
983
- globalRunId: evalRecord.global_run_id,
984
- testInfo: evalRecord.test_info && typeof evalRecord.test_info === "string" ? JSON.parse(evalRecord.test_info) : void 0
985
- };
986
- } catch (parseError) {
987
- this.logger.error("Failed to parse eval record", { record: evalRecord, error: parseError });
988
- return {
989
- agentName: evalRecord.agent_name,
990
- createdAt: evalRecord.created_at,
991
- runId: evalRecord.run_id,
992
- globalRunId: evalRecord.global_run_id
993
- };
994
- }
995
- });
996
- } catch (error) {
997
- throw new MastraError(
998
- {
999
- id: "STORAGE_DYNAMODB_STORE_GET_EVALS_BY_AGENT_NAME_FAILED",
1000
- domain: ErrorDomain.STORAGE,
1001
- category: ErrorCategory.THIRD_PARTY,
1002
- details: { agentName }
1003
- },
1004
- error
1005
- );
1006
- }
1075
+ async dangerouslyClearAll() {
1076
+ await deleteTableData(this.service, TABLE_THREADS);
1077
+ await deleteTableData(this.service, TABLE_MESSAGES);
1078
+ await deleteTableData(this.service, TABLE_RESOURCES);
1007
1079
  }
1008
- async getEvals(options = {}) {
1009
- const { agentName, type, page = 0, perPage = 100, dateRange } = options;
1010
- this.logger.debug("Getting evals with pagination", { agentName, type, page, perPage, dateRange });
1080
+ async deleteMessages(messageIds) {
1081
+ if (!messageIds || messageIds.length === 0) {
1082
+ return;
1083
+ }
1084
+ this.logger.debug("Deleting messages", { count: messageIds.length });
1011
1085
  try {
1012
- let query;
1013
- if (agentName) {
1014
- query = this.service.entities.eval.query.byAgent({ entity: "eval", agent_name: agentName });
1015
- } else {
1016
- query = this.service.entities.eval.query.byEntity({ entity: "eval" });
1017
- }
1018
- const results = await query.go({
1019
- order: "desc",
1020
- pages: "all"
1021
- // Get all pages to apply filtering and pagination
1022
- });
1023
- if (!results.data.length) {
1024
- return {
1025
- evals: [],
1026
- total: 0,
1027
- page,
1028
- perPage,
1029
- hasMore: false
1030
- };
1031
- }
1032
- let filteredData = results.data;
1033
- if (type) {
1034
- filteredData = filteredData.filter((evalRecord) => {
1035
- try {
1036
- const testInfo = evalRecord.test_info && typeof evalRecord.test_info === "string" ? JSON.parse(evalRecord.test_info) : void 0;
1037
- if (type === "test" && !testInfo) {
1038
- return false;
1039
- }
1040
- if (type === "live" && testInfo) {
1041
- return false;
1086
+ const threadIds = /* @__PURE__ */ new Set();
1087
+ const batchSize = 25;
1088
+ for (let i = 0; i < messageIds.length; i += batchSize) {
1089
+ const batch = messageIds.slice(i, i + batchSize);
1090
+ const messagesToDelete = await Promise.all(
1091
+ batch.map(async (id) => {
1092
+ const result = await this.service.entities.message.get({ entity: "message", id }).go();
1093
+ return result.data;
1094
+ })
1095
+ );
1096
+ for (const message of messagesToDelete) {
1097
+ if (message) {
1098
+ if (message.threadId) {
1099
+ threadIds.add(message.threadId);
1042
1100
  }
1043
- } catch (e) {
1044
- this.logger.warn("Failed to parse test_info during filtering", { record: evalRecord, error: e });
1101
+ await this.service.entities.message.delete({ entity: "message", id: message.id }).go();
1045
1102
  }
1046
- return true;
1047
- });
1103
+ }
1048
1104
  }
1049
- if (dateRange) {
1050
- const fromDate = dateRange.start;
1051
- const toDate = dateRange.end;
1052
- filteredData = filteredData.filter((evalRecord) => {
1053
- const recordDate = new Date(evalRecord.created_at);
1054
- if (fromDate && recordDate < fromDate) {
1055
- return false;
1056
- }
1057
- if (toDate && recordDate > toDate) {
1058
- return false;
1059
- }
1060
- return true;
1061
- });
1105
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1106
+ for (const threadId of threadIds) {
1107
+ await this.service.entities.thread.update({ entity: "thread", id: threadId }).set({ updatedAt: now }).go();
1062
1108
  }
1063
- const total = filteredData.length;
1064
- const start = page * perPage;
1065
- const end = start + perPage;
1066
- const paginatedData = filteredData.slice(start, end);
1067
- const evals = paginatedData.map((evalRecord) => {
1068
- try {
1069
- return {
1070
- input: evalRecord.input,
1071
- output: evalRecord.output,
1072
- result: evalRecord.result && typeof evalRecord.result === "string" ? JSON.parse(evalRecord.result) : void 0,
1073
- agentName: evalRecord.agent_name,
1074
- createdAt: evalRecord.created_at,
1075
- metricName: evalRecord.metric_name,
1076
- instructions: evalRecord.instructions,
1077
- runId: evalRecord.run_id,
1078
- globalRunId: evalRecord.global_run_id,
1079
- testInfo: evalRecord.test_info && typeof evalRecord.test_info === "string" ? JSON.parse(evalRecord.test_info) : void 0
1080
- };
1081
- } catch (parseError) {
1082
- this.logger.error("Failed to parse eval record", { record: evalRecord, error: parseError });
1083
- return {
1084
- agentName: evalRecord.agent_name,
1085
- createdAt: evalRecord.created_at,
1086
- runId: evalRecord.run_id,
1087
- globalRunId: evalRecord.global_run_id
1088
- };
1089
- }
1090
- });
1091
- const hasMore = end < total;
1092
- return {
1093
- evals,
1094
- total,
1095
- page,
1096
- perPage,
1097
- hasMore
1098
- };
1099
1109
  } catch (error) {
1100
1110
  throw new MastraError(
1101
1111
  {
1102
- id: "STORAGE_DYNAMODB_STORE_GET_EVALS_FAILED",
1112
+ id: createStorageErrorId("DYNAMODB", "DELETE_MESSAGES", "FAILED"),
1103
1113
  domain: ErrorDomain.STORAGE,
1104
1114
  category: ErrorCategory.THIRD_PARTY,
1105
- details: {
1106
- agentName: agentName || "all",
1107
- type: type || "all",
1108
- page,
1109
- perPage
1110
- }
1115
+ details: { count: messageIds.length }
1111
1116
  },
1112
1117
  error
1113
1118
  );
1114
1119
  }
1115
1120
  }
1116
- };
1117
- var MemoryStorageDynamoDB = class extends MemoryStorage {
1118
- service;
1119
- constructor({ service }) {
1120
- super();
1121
- this.service = service;
1122
- }
1123
1121
  // Helper function to parse message data (handle JSON fields)
1124
1122
  parseMessageData(data) {
1125
1123
  return {
@@ -1132,17 +1130,17 @@ var MemoryStorageDynamoDB = class extends MemoryStorage {
1132
1130
  };
1133
1131
  }
1134
1132
  // Helper function to transform and sort threads
1135
- transformAndSortThreads(rawThreads, orderBy, sortDirection) {
1133
+ transformAndSortThreads(rawThreads, field, direction) {
1136
1134
  return rawThreads.map((data) => ({
1137
1135
  ...data,
1138
1136
  // Convert date strings back to Date objects for consistency
1139
1137
  createdAt: typeof data.createdAt === "string" ? new Date(data.createdAt) : data.createdAt,
1140
1138
  updatedAt: typeof data.updatedAt === "string" ? new Date(data.updatedAt) : data.updatedAt
1141
1139
  })).sort((a, b) => {
1142
- const fieldA = orderBy === "createdAt" ? a.createdAt : a.updatedAt;
1143
- const fieldB = orderBy === "createdAt" ? b.createdAt : b.updatedAt;
1140
+ const fieldA = field === "createdAt" ? a.createdAt : a.updatedAt;
1141
+ const fieldB = field === "createdAt" ? b.createdAt : b.updatedAt;
1144
1142
  const comparison = fieldA.getTime() - fieldB.getTime();
1145
- return sortDirection === "DESC" ? -comparison : comparison;
1143
+ return direction === "DESC" ? -comparison : comparison;
1146
1144
  });
1147
1145
  }
1148
1146
  async getThreadById({ threadId }) {
@@ -1164,7 +1162,7 @@ var MemoryStorageDynamoDB = class extends MemoryStorage {
1164
1162
  } catch (error) {
1165
1163
  throw new MastraError(
1166
1164
  {
1167
- id: "STORAGE_DYNAMODB_STORE_GET_THREAD_BY_ID_FAILED",
1165
+ id: createStorageErrorId("DYNAMODB", "GET_THREAD_BY_ID", "FAILED"),
1168
1166
  domain: ErrorDomain.STORAGE,
1169
1167
  category: ErrorCategory.THIRD_PARTY,
1170
1168
  details: { threadId }
@@ -1173,32 +1171,6 @@ var MemoryStorageDynamoDB = class extends MemoryStorage {
1173
1171
  );
1174
1172
  }
1175
1173
  }
1176
- /**
1177
- * @deprecated use getThreadsByResourceIdPaginated instead for paginated results.
1178
- */
1179
- async getThreadsByResourceId(args) {
1180
- const resourceId = args.resourceId;
1181
- const orderBy = this.castThreadOrderBy(args.orderBy);
1182
- const sortDirection = this.castThreadSortDirection(args.sortDirection);
1183
- this.logger.debug("Getting threads by resource ID", { resourceId, orderBy, sortDirection });
1184
- try {
1185
- const result = await this.service.entities.thread.query.byResource({ entity: "thread", resourceId }).go();
1186
- if (!result.data.length) {
1187
- return [];
1188
- }
1189
- return this.transformAndSortThreads(result.data, orderBy, sortDirection);
1190
- } catch (error) {
1191
- throw new MastraError(
1192
- {
1193
- id: "STORAGE_DYNAMODB_STORE_GET_THREADS_BY_RESOURCE_ID_FAILED",
1194
- domain: ErrorDomain.STORAGE,
1195
- category: ErrorCategory.THIRD_PARTY,
1196
- details: { resourceId }
1197
- },
1198
- error
1199
- );
1200
- }
1201
- }
1202
1174
  async saveThread({ thread }) {
1203
1175
  this.logger.debug("Saving thread", { threadId: thread.id });
1204
1176
  const now = /* @__PURE__ */ new Date();
@@ -1209,7 +1181,8 @@ var MemoryStorageDynamoDB = class extends MemoryStorage {
1209
1181
  title: thread.title || `Thread ${thread.id}`,
1210
1182
  createdAt: thread.createdAt?.toISOString() || now.toISOString(),
1211
1183
  updatedAt: thread.updatedAt?.toISOString() || now.toISOString(),
1212
- metadata: thread.metadata ? JSON.stringify(thread.metadata) : void 0
1184
+ metadata: thread.metadata ? JSON.stringify(thread.metadata) : void 0,
1185
+ ...getTtlProps("thread", this.ttlConfig)
1213
1186
  };
1214
1187
  try {
1215
1188
  await this.service.entities.thread.upsert(threadData).go();
@@ -1218,13 +1191,13 @@ var MemoryStorageDynamoDB = class extends MemoryStorage {
1218
1191
  resourceId: thread.resourceId,
1219
1192
  title: threadData.title,
1220
1193
  createdAt: thread.createdAt || now,
1221
- updatedAt: now,
1194
+ updatedAt: thread.updatedAt || now,
1222
1195
  metadata: thread.metadata
1223
1196
  };
1224
1197
  } catch (error) {
1225
1198
  throw new MastraError(
1226
1199
  {
1227
- id: "STORAGE_DYNAMODB_STORE_SAVE_THREAD_FAILED",
1200
+ id: createStorageErrorId("DYNAMODB", "SAVE_THREAD", "FAILED"),
1228
1201
  domain: ErrorDomain.STORAGE,
1229
1202
  category: ErrorCategory.THIRD_PARTY,
1230
1203
  details: { threadId: thread.id }
@@ -1266,7 +1239,7 @@ var MemoryStorageDynamoDB = class extends MemoryStorage {
1266
1239
  } catch (error) {
1267
1240
  throw new MastraError(
1268
1241
  {
1269
- id: "STORAGE_DYNAMODB_STORE_UPDATE_THREAD_FAILED",
1242
+ id: createStorageErrorId("DYNAMODB", "UPDATE_THREAD", "FAILED"),
1270
1243
  domain: ErrorDomain.STORAGE,
1271
1244
  category: ErrorCategory.THIRD_PARTY,
1272
1245
  details: { threadId: id }
@@ -1278,7 +1251,7 @@ var MemoryStorageDynamoDB = class extends MemoryStorage {
1278
1251
  async deleteThread({ threadId }) {
1279
1252
  this.logger.debug("Deleting thread", { threadId });
1280
1253
  try {
1281
- const messages = await this.getMessages({ threadId });
1254
+ const { messages } = await this.listMessages({ threadId, perPage: false });
1282
1255
  if (messages.length > 0) {
1283
1256
  const batchSize = 25;
1284
1257
  for (let i = 0; i < messages.length; i += batchSize) {
@@ -1298,7 +1271,7 @@ var MemoryStorageDynamoDB = class extends MemoryStorage {
1298
1271
  } catch (error) {
1299
1272
  throw new MastraError(
1300
1273
  {
1301
- id: "STORAGE_DYNAMODB_STORE_DELETE_THREAD_FAILED",
1274
+ id: createStorageErrorId("DYNAMODB", "DELETE_THREAD", "FAILED"),
1302
1275
  domain: ErrorDomain.STORAGE,
1303
1276
  category: ErrorCategory.THIRD_PARTY,
1304
1277
  details: { threadId }
@@ -1307,104 +1280,166 @@ var MemoryStorageDynamoDB = class extends MemoryStorage {
1307
1280
  );
1308
1281
  }
1309
1282
  }
1310
- async getMessages({
1311
- threadId,
1312
- resourceId,
1313
- selectBy,
1314
- format
1315
- }) {
1316
- this.logger.debug("Getting messages", { threadId, selectBy });
1283
+ async listMessagesById({ messageIds }) {
1284
+ this.logger.debug("Getting messages by ID", { messageIds });
1285
+ if (messageIds.length === 0) return { messages: [] };
1317
1286
  try {
1318
- if (!threadId.trim()) throw new Error("threadId must be a non-empty string");
1319
- const messages = [];
1320
- const limit = resolveMessageLimit({ last: selectBy?.last, defaultLimit: Number.MAX_SAFE_INTEGER });
1321
- if (selectBy?.include?.length) {
1322
- const includeMessages = await this._getIncludedMessages(threadId, selectBy);
1323
- if (includeMessages) {
1324
- messages.push(...includeMessages);
1325
- }
1326
- }
1327
- if (limit !== 0) {
1328
- const query = this.service.entities.message.query.byThread({ entity: "message", threadId });
1329
- let results;
1330
- if (limit !== Number.MAX_SAFE_INTEGER && limit > 0) {
1331
- results = await query.go({ limit, order: "desc" });
1332
- results.data = results.data.reverse();
1333
- } else {
1334
- results = await query.go();
1335
- }
1336
- let allThreadMessages = results.data.map((data) => this.parseMessageData(data)).filter((msg) => "content" in msg);
1337
- allThreadMessages.sort((a, b) => {
1338
- const timeA = a.createdAt.getTime();
1339
- const timeB = b.createdAt.getTime();
1340
- if (timeA === timeB) {
1341
- return a.id.localeCompare(b.id);
1342
- }
1343
- return timeA - timeB;
1344
- });
1345
- messages.push(...allThreadMessages);
1346
- }
1347
- messages.sort((a, b) => {
1348
- const timeA = a.createdAt.getTime();
1349
- const timeB = b.createdAt.getTime();
1350
- if (timeA === timeB) {
1351
- return a.id.localeCompare(b.id);
1352
- }
1353
- return timeA - timeB;
1354
- });
1355
- const uniqueMessages = messages.filter(
1287
+ const results = await Promise.all(
1288
+ messageIds.map((id) => this.service.entities.message.query.primary({ entity: "message", id }).go())
1289
+ );
1290
+ const data = results.map((result) => result.data).flat(1);
1291
+ let parsedMessages = data.map((data2) => this.parseMessageData(data2)).filter((msg) => "content" in msg);
1292
+ const uniqueMessages = parsedMessages.filter(
1356
1293
  (message, index, self) => index === self.findIndex((m) => m.id === message.id)
1357
1294
  );
1358
- const list = new MessageList({ threadId, resourceId }).add(uniqueMessages, "memory");
1359
- if (format === `v2`) return list.get.all.v2();
1360
- return list.get.all.v1();
1295
+ const list = new MessageList().add(uniqueMessages, "memory");
1296
+ return { messages: list.get.all.db() };
1361
1297
  } catch (error) {
1362
1298
  throw new MastraError(
1363
1299
  {
1364
- id: "STORAGE_DYNAMODB_STORE_GET_MESSAGES_FAILED",
1300
+ id: createStorageErrorId("DYNAMODB", "LIST_MESSAGES_BY_ID", "FAILED"),
1365
1301
  domain: ErrorDomain.STORAGE,
1366
1302
  category: ErrorCategory.THIRD_PARTY,
1367
- details: { threadId, resourceId: resourceId ?? "" }
1303
+ details: { messageIds: JSON.stringify(messageIds) }
1368
1304
  },
1369
1305
  error
1370
1306
  );
1371
1307
  }
1372
1308
  }
1373
- async getMessagesById({
1374
- messageIds,
1375
- format
1376
- }) {
1377
- this.logger.debug("Getting messages by ID", { messageIds });
1378
- if (messageIds.length === 0) return [];
1379
- try {
1380
- const results = await Promise.all(
1381
- messageIds.map((id) => this.service.entities.message.query.primary({ entity: "message", id }).go())
1309
+ async listMessages(args) {
1310
+ const { threadId, resourceId, include, filter, perPage: perPageInput, page = 0, orderBy } = args;
1311
+ const threadIds = Array.isArray(threadId) ? threadId : [threadId];
1312
+ if (threadIds.length === 0 || threadIds.some((id) => !id.trim())) {
1313
+ throw new MastraError(
1314
+ {
1315
+ id: createStorageErrorId("DYNAMODB", "LIST_MESSAGES", "INVALID_THREAD_ID"),
1316
+ domain: ErrorDomain.STORAGE,
1317
+ category: ErrorCategory.THIRD_PARTY,
1318
+ details: { threadId: Array.isArray(threadId) ? threadId.join(",") : threadId }
1319
+ },
1320
+ new Error("threadId must be a non-empty string or array of non-empty strings")
1382
1321
  );
1383
- const data = results.map((result) => result.data).flat(1);
1384
- let parsedMessages = data.map((data2) => this.parseMessageData(data2)).filter((msg) => "content" in msg);
1385
- const uniqueMessages = parsedMessages.filter(
1386
- (message, index, self) => index === self.findIndex((m) => m.id === message.id)
1322
+ }
1323
+ const perPage = normalizePerPage(perPageInput, 40);
1324
+ const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
1325
+ try {
1326
+ if (page < 0) {
1327
+ throw new MastraError(
1328
+ {
1329
+ id: createStorageErrorId("DYNAMODB", "LIST_MESSAGES", "INVALID_PAGE"),
1330
+ domain: ErrorDomain.STORAGE,
1331
+ category: ErrorCategory.USER,
1332
+ details: { page }
1333
+ },
1334
+ new Error("page must be >= 0")
1335
+ );
1336
+ }
1337
+ const { field, direction } = this.parseOrderBy(orderBy, "ASC");
1338
+ this.logger.debug("Getting messages with listMessages", {
1339
+ threadId,
1340
+ resourceId,
1341
+ perPageInput,
1342
+ offset,
1343
+ perPage,
1344
+ page,
1345
+ field,
1346
+ direction
1347
+ });
1348
+ const query = this.service.entities.message.query.byThread({ entity: "message", threadId });
1349
+ const results = await query.go();
1350
+ let allThreadMessages = results.data.map((data) => this.parseMessageData(data)).filter((msg) => "content" in msg && typeof msg.content === "object");
1351
+ if (resourceId) {
1352
+ allThreadMessages = allThreadMessages.filter((msg) => msg.resourceId === resourceId);
1353
+ }
1354
+ allThreadMessages = filterByDateRange(
1355
+ allThreadMessages,
1356
+ (msg) => new Date(msg.createdAt),
1357
+ filter?.dateRange
1387
1358
  );
1388
- const list = new MessageList().add(uniqueMessages, "memory");
1389
- if (format === `v1`) return list.get.all.v1();
1390
- return list.get.all.v2();
1359
+ allThreadMessages.sort((a, b) => {
1360
+ const aValue = field === "createdAt" ? new Date(a.createdAt).getTime() : a[field];
1361
+ const bValue = field === "createdAt" ? new Date(b.createdAt).getTime() : b[field];
1362
+ if (aValue === bValue) {
1363
+ return a.id.localeCompare(b.id);
1364
+ }
1365
+ return direction === "ASC" ? aValue - bValue : bValue - aValue;
1366
+ });
1367
+ const total = allThreadMessages.length;
1368
+ const paginatedMessages = allThreadMessages.slice(offset, offset + perPage);
1369
+ const paginatedCount = paginatedMessages.length;
1370
+ if (total === 0 && paginatedCount === 0 && (!include || include.length === 0)) {
1371
+ return {
1372
+ messages: [],
1373
+ total: 0,
1374
+ page,
1375
+ perPage: perPageForResponse,
1376
+ hasMore: false
1377
+ };
1378
+ }
1379
+ const messageIds = new Set(paginatedMessages.map((m) => m.id));
1380
+ let includeMessages = [];
1381
+ if (include && include.length > 0) {
1382
+ const selectBy = { include };
1383
+ includeMessages = await this._getIncludedMessages(selectBy);
1384
+ for (const includeMsg of includeMessages) {
1385
+ if (!messageIds.has(includeMsg.id)) {
1386
+ paginatedMessages.push(includeMsg);
1387
+ messageIds.add(includeMsg.id);
1388
+ }
1389
+ }
1390
+ }
1391
+ const list = new MessageList().add(paginatedMessages, "memory");
1392
+ let finalMessages = list.get.all.db();
1393
+ finalMessages = finalMessages.sort((a, b) => {
1394
+ const aValue = field === "createdAt" ? new Date(a.createdAt).getTime() : a[field];
1395
+ const bValue = field === "createdAt" ? new Date(b.createdAt).getTime() : b[field];
1396
+ if (aValue === bValue) {
1397
+ return a.id.localeCompare(b.id);
1398
+ }
1399
+ return direction === "ASC" ? aValue - bValue : bValue - aValue;
1400
+ });
1401
+ const returnedThreadMessageIds = new Set(finalMessages.filter((m) => m.threadId === threadId).map((m) => m.id));
1402
+ const allThreadMessagesReturned = returnedThreadMessageIds.size >= total;
1403
+ let hasMore = false;
1404
+ if (perPageInput !== false && !allThreadMessagesReturned) {
1405
+ hasMore = offset + paginatedCount < total;
1406
+ }
1407
+ return {
1408
+ messages: finalMessages,
1409
+ total,
1410
+ page,
1411
+ perPage: perPageForResponse,
1412
+ hasMore
1413
+ };
1391
1414
  } catch (error) {
1392
- throw new MastraError(
1415
+ const mastraError = new MastraError(
1393
1416
  {
1394
- id: "STORAGE_DYNAMODB_STORE_GET_MESSAGES_BY_ID_FAILED",
1417
+ id: createStorageErrorId("DYNAMODB", "LIST_MESSAGES", "FAILED"),
1395
1418
  domain: ErrorDomain.STORAGE,
1396
1419
  category: ErrorCategory.THIRD_PARTY,
1397
- details: { messageIds: JSON.stringify(messageIds) }
1420
+ details: {
1421
+ threadId: Array.isArray(threadId) ? threadId.join(",") : threadId,
1422
+ resourceId: resourceId ?? ""
1423
+ }
1398
1424
  },
1399
1425
  error
1400
1426
  );
1427
+ this.logger?.error?.(mastraError.toString());
1428
+ this.logger?.trackException?.(mastraError);
1429
+ return {
1430
+ messages: [],
1431
+ total: 0,
1432
+ page,
1433
+ perPage: perPageForResponse,
1434
+ hasMore: false
1435
+ };
1401
1436
  }
1402
1437
  }
1403
1438
  async saveMessages(args) {
1404
- const { messages, format = "v1" } = args;
1439
+ const { messages } = args;
1405
1440
  this.logger.debug("Saving messages", { count: messages.length });
1406
1441
  if (!messages.length) {
1407
- return [];
1442
+ return { messages: [] };
1408
1443
  }
1409
1444
  const threadId = messages[0]?.threadId;
1410
1445
  if (!threadId) {
@@ -1414,20 +1449,18 @@ var MemoryStorageDynamoDB = class extends MemoryStorage {
1414
1449
  const now = (/* @__PURE__ */ new Date()).toISOString();
1415
1450
  return {
1416
1451
  entity: "message",
1417
- // Add entity type
1418
1452
  id: msg.id,
1419
1453
  threadId: msg.threadId,
1420
1454
  role: msg.role,
1421
1455
  type: msg.type,
1422
1456
  resourceId: msg.resourceId,
1423
- // Ensure complex fields are stringified if not handled by attribute setters
1424
1457
  content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content),
1425
1458
  toolCallArgs: `toolCallArgs` in msg && msg.toolCallArgs ? JSON.stringify(msg.toolCallArgs) : void 0,
1426
1459
  toolCallIds: `toolCallIds` in msg && msg.toolCallIds ? JSON.stringify(msg.toolCallIds) : void 0,
1427
1460
  toolNames: `toolNames` in msg && msg.toolNames ? JSON.stringify(msg.toolNames) : void 0,
1428
1461
  createdAt: msg.createdAt instanceof Date ? msg.createdAt.toISOString() : msg.createdAt || now,
1429
- updatedAt: now
1430
- // Add updatedAt
1462
+ updatedAt: now,
1463
+ ...getTtlProps("message", this.ttlConfig)
1431
1464
  };
1432
1465
  });
1433
1466
  try {
@@ -1458,12 +1491,11 @@ var MemoryStorageDynamoDB = class extends MemoryStorage {
1458
1491
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1459
1492
  }).go();
1460
1493
  const list = new MessageList().add(messages, "memory");
1461
- if (format === `v1`) return list.get.all.v1();
1462
- return list.get.all.v2();
1494
+ return { messages: list.get.all.db() };
1463
1495
  } catch (error) {
1464
1496
  throw new MastraError(
1465
1497
  {
1466
- id: "STORAGE_DYNAMODB_STORE_SAVE_MESSAGES_FAILED",
1498
+ id: createStorageErrorId("DYNAMODB", "SAVE_MESSAGES", "FAILED"),
1467
1499
  domain: ErrorDomain.STORAGE,
1468
1500
  category: ErrorCategory.THIRD_PARTY,
1469
1501
  details: { count: messages.length }
@@ -1472,137 +1504,103 @@ var MemoryStorageDynamoDB = class extends MemoryStorage {
1472
1504
  );
1473
1505
  }
1474
1506
  }
1475
- async getThreadsByResourceIdPaginated(args) {
1476
- const { resourceId, page = 0, perPage = 100 } = args;
1477
- const orderBy = this.castThreadOrderBy(args.orderBy);
1478
- const sortDirection = this.castThreadSortDirection(args.sortDirection);
1479
- this.logger.debug("Getting threads by resource ID with pagination", {
1480
- resourceId,
1507
+ async listThreads(args) {
1508
+ const { page = 0, perPage: perPageInput, orderBy, filter } = args;
1509
+ try {
1510
+ this.validatePaginationInput(page, perPageInput ?? 100);
1511
+ } catch (error) {
1512
+ throw new MastraError(
1513
+ {
1514
+ id: createStorageErrorId("DYNAMODB", "LIST_THREADS", "INVALID_PAGE"),
1515
+ domain: ErrorDomain.STORAGE,
1516
+ category: ErrorCategory.USER,
1517
+ details: {
1518
+ page,
1519
+ ...perPageInput !== void 0 && { perPage: perPageInput }
1520
+ }
1521
+ },
1522
+ error instanceof Error ? error : new Error("Invalid pagination parameters")
1523
+ );
1524
+ }
1525
+ const perPage = normalizePerPage(perPageInput, 100);
1526
+ const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
1527
+ const { field, direction } = this.parseOrderBy(orderBy);
1528
+ this.logger.debug("Listing threads with filters", {
1529
+ resourceId: filter?.resourceId,
1530
+ metadataKeys: filter?.metadata ? Object.keys(filter.metadata) : [],
1481
1531
  page,
1482
1532
  perPage,
1483
- orderBy,
1484
- sortDirection
1533
+ field,
1534
+ direction
1485
1535
  });
1486
1536
  try {
1487
- const query = this.service.entities.thread.query.byResource({ entity: "thread", resourceId });
1488
- const results = await query.go();
1489
- const allThreads = this.transformAndSortThreads(results.data, orderBy, sortDirection);
1490
- const startIndex = page * perPage;
1491
- const endIndex = startIndex + perPage;
1492
- const paginatedThreads = allThreads.slice(startIndex, endIndex);
1537
+ const rawThreads = filter?.resourceId ? (await this.service.entities.thread.query.byResource({
1538
+ entity: "thread",
1539
+ resourceId: filter.resourceId
1540
+ }).go({ pages: "all" })).data : (await this.service.entities.thread.scan.go({ pages: "all" })).data;
1541
+ let allThreads = this.transformAndSortThreads(rawThreads, field, direction);
1542
+ if (filter?.metadata && Object.keys(filter.metadata).length > 0) {
1543
+ allThreads = allThreads.filter((thread) => {
1544
+ let threadMeta = null;
1545
+ if (typeof thread.metadata === "string") {
1546
+ try {
1547
+ threadMeta = JSON.parse(thread.metadata);
1548
+ } catch {
1549
+ return false;
1550
+ }
1551
+ } else if (thread.metadata && typeof thread.metadata === "object") {
1552
+ threadMeta = thread.metadata;
1553
+ }
1554
+ if (!threadMeta) return false;
1555
+ return Object.entries(filter.metadata).every(([key, value]) => threadMeta[key] === value);
1556
+ });
1557
+ }
1558
+ const endIndex = offset + perPage;
1559
+ const paginatedThreads = allThreads.slice(offset, endIndex);
1493
1560
  const total = allThreads.length;
1494
- const hasMore = endIndex < total;
1561
+ const hasMore = offset + perPage < total;
1495
1562
  return {
1496
1563
  threads: paginatedThreads,
1497
1564
  total,
1498
1565
  page,
1499
- perPage,
1566
+ perPage: perPageForResponse,
1500
1567
  hasMore
1501
1568
  };
1502
1569
  } catch (error) {
1503
1570
  throw new MastraError(
1504
1571
  {
1505
- id: "STORAGE_DYNAMODB_STORE_GET_THREADS_BY_RESOURCE_ID_PAGINATED_FAILED",
1572
+ id: createStorageErrorId("DYNAMODB", "LIST_THREADS", "FAILED"),
1506
1573
  domain: ErrorDomain.STORAGE,
1507
1574
  category: ErrorCategory.THIRD_PARTY,
1508
- details: { resourceId, page, perPage }
1575
+ details: {
1576
+ ...filter?.resourceId && { resourceId: filter.resourceId },
1577
+ hasMetadataFilter: !!(filter?.metadata && Object.keys(filter.metadata).length),
1578
+ page,
1579
+ perPage: perPageForResponse
1580
+ }
1509
1581
  },
1510
1582
  error
1511
1583
  );
1512
1584
  }
1513
1585
  }
1514
- async getMessagesPaginated(args) {
1515
- const { threadId, resourceId, selectBy, format = "v1" } = args;
1516
- const { page = 0, perPage = 40, dateRange } = selectBy?.pagination || {};
1517
- const fromDate = dateRange?.start;
1518
- const toDate = dateRange?.end;
1519
- const limit = resolveMessageLimit({ last: selectBy?.last, defaultLimit: Number.MAX_SAFE_INTEGER });
1520
- this.logger.debug("Getting messages with pagination", { threadId, page, perPage, fromDate, toDate, limit });
1521
- try {
1522
- if (!threadId.trim()) throw new Error("threadId must be a non-empty string");
1523
- let messages = [];
1524
- if (selectBy?.include?.length) {
1525
- const includeMessages = await this._getIncludedMessages(threadId, selectBy);
1526
- if (includeMessages) {
1527
- messages.push(...includeMessages);
1528
- }
1529
- }
1530
- if (limit !== 0) {
1531
- const query = this.service.entities.message.query.byThread({ entity: "message", threadId });
1532
- let results;
1533
- if (limit !== Number.MAX_SAFE_INTEGER && limit > 0) {
1534
- results = await query.go({ limit, order: "desc" });
1535
- results.data = results.data.reverse();
1536
- } else {
1537
- results = await query.go();
1586
+ // Helper method to get included messages with context
1587
+ async _getIncludedMessages(selectBy) {
1588
+ if (!selectBy?.include?.length) {
1589
+ return [];
1590
+ }
1591
+ const includeMessages = [];
1592
+ for (const includeItem of selectBy.include) {
1593
+ try {
1594
+ const { id, withPreviousMessages = 0, withNextMessages = 0 } = includeItem;
1595
+ const targetResult = await this.service.entities.message.get({ entity: "message", id }).go();
1596
+ if (!targetResult.data) {
1597
+ this.logger.warn("Target message not found", { id });
1598
+ continue;
1538
1599
  }
1539
- let allThreadMessages = results.data.map((data) => this.parseMessageData(data)).filter((msg) => "content" in msg);
1540
- allThreadMessages.sort((a, b) => {
1541
- const timeA = a.createdAt.getTime();
1542
- const timeB = b.createdAt.getTime();
1543
- if (timeA === timeB) {
1544
- return a.id.localeCompare(b.id);
1545
- }
1546
- return timeA - timeB;
1547
- });
1548
- const excludeIds = messages.map((m) => m.id);
1549
- if (excludeIds.length > 0) {
1550
- allThreadMessages = allThreadMessages.filter((msg) => !excludeIds.includes(msg.id));
1551
- }
1552
- messages.push(...allThreadMessages);
1553
- }
1554
- messages.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
1555
- if (fromDate || toDate) {
1556
- messages = messages.filter((msg) => {
1557
- const createdAt = new Date(msg.createdAt).getTime();
1558
- if (fromDate && createdAt < new Date(fromDate).getTime()) return false;
1559
- if (toDate && createdAt > new Date(toDate).getTime()) return false;
1560
- return true;
1561
- });
1562
- }
1563
- const total = messages.length;
1564
- const start = page * perPage;
1565
- const end = start + perPage;
1566
- const paginatedMessages = messages.slice(start, end);
1567
- const hasMore = end < total;
1568
- const list = new MessageList({ threadId, resourceId }).add(paginatedMessages, "memory");
1569
- const finalMessages = format === "v2" ? list.get.all.v2() : list.get.all.v1();
1570
- return {
1571
- messages: finalMessages,
1572
- total,
1573
- page,
1574
- perPage,
1575
- hasMore
1576
- };
1577
- } catch (error) {
1578
- const mastraError = new MastraError(
1579
- {
1580
- id: "STORAGE_DYNAMODB_STORE_GET_MESSAGES_PAGINATED_FAILED",
1581
- domain: ErrorDomain.STORAGE,
1582
- category: ErrorCategory.THIRD_PARTY,
1583
- details: { threadId, resourceId: resourceId ?? "" }
1584
- },
1585
- error
1586
- );
1587
- this.logger?.trackException?.(mastraError);
1588
- this.logger?.error?.(mastraError.toString());
1589
- return { messages: [], total: 0, page, perPage, hasMore: false };
1590
- }
1591
- }
1592
- // Helper method to get included messages with context
1593
- async _getIncludedMessages(threadId, selectBy) {
1594
- if (!threadId.trim()) throw new Error("threadId must be a non-empty string");
1595
- if (!selectBy?.include?.length) {
1596
- return [];
1597
- }
1598
- const includeMessages = [];
1599
- for (const includeItem of selectBy.include) {
1600
- try {
1601
- const { id, threadId: targetThreadId, withPreviousMessages = 0, withNextMessages = 0 } = includeItem;
1602
- const searchThreadId = targetThreadId || threadId;
1600
+ const targetMessageData = targetResult.data;
1601
+ const searchThreadId = targetMessageData.threadId;
1603
1602
  this.logger.debug("Getting included messages for", {
1604
1603
  id,
1605
- targetThreadId,
1606
1604
  searchThreadId,
1607
1605
  withPreviousMessages,
1608
1606
  withNextMessages
@@ -1625,7 +1623,7 @@ var MemoryStorageDynamoDB = class extends MemoryStorage {
1625
1623
  });
1626
1624
  const targetIndex = allMessages.findIndex((msg) => msg.id === id);
1627
1625
  if (targetIndex === -1) {
1628
- this.logger.warn("Target message not found", { id, threadId: searchThreadId });
1626
+ this.logger.warn("Target message not found in thread", { id, threadId: searchThreadId });
1629
1627
  continue;
1630
1628
  }
1631
1629
  this.logger.debug("Found target message at index", { id, targetIndex, totalMessages: allMessages.length });
@@ -1710,7 +1708,7 @@ var MemoryStorageDynamoDB = class extends MemoryStorage {
1710
1708
  } catch (error) {
1711
1709
  throw new MastraError(
1712
1710
  {
1713
- id: "STORAGE_DYNAMODB_STORE_UPDATE_MESSAGES_FAILED",
1711
+ id: createStorageErrorId("DYNAMODB", "UPDATE_MESSAGES", "FAILED"),
1714
1712
  domain: ErrorDomain.STORAGE,
1715
1713
  category: ErrorCategory.THIRD_PARTY,
1716
1714
  details: { count: messages.length }
@@ -1739,7 +1737,7 @@ var MemoryStorageDynamoDB = class extends MemoryStorage {
1739
1737
  } catch (error) {
1740
1738
  throw new MastraError(
1741
1739
  {
1742
- id: "STORAGE_DYNAMODB_STORE_GET_RESOURCE_BY_ID_FAILED",
1740
+ id: createStorageErrorId("DYNAMODB", "GET_RESOURCE_BY_ID", "FAILED"),
1743
1741
  domain: ErrorDomain.STORAGE,
1744
1742
  category: ErrorCategory.THIRD_PARTY,
1745
1743
  details: { resourceId }
@@ -1757,7 +1755,8 @@ var MemoryStorageDynamoDB = class extends MemoryStorage {
1757
1755
  workingMemory: resource.workingMemory,
1758
1756
  metadata: resource.metadata ? JSON.stringify(resource.metadata) : void 0,
1759
1757
  createdAt: resource.createdAt?.toISOString() || now.toISOString(),
1760
- updatedAt: now.toISOString()
1758
+ updatedAt: now.toISOString(),
1759
+ ...getTtlProps("resource", this.ttlConfig)
1761
1760
  };
1762
1761
  try {
1763
1762
  await this.service.entities.resource.upsert(resourceData).go();
@@ -1771,7 +1770,7 @@ var MemoryStorageDynamoDB = class extends MemoryStorage {
1771
1770
  } catch (error) {
1772
1771
  throw new MastraError(
1773
1772
  {
1774
- id: "STORAGE_DYNAMODB_STORE_SAVE_RESOURCE_FAILED",
1773
+ id: createStorageErrorId("DYNAMODB", "SAVE_RESOURCE", "FAILED"),
1775
1774
  domain: ErrorDomain.STORAGE,
1776
1775
  category: ErrorCategory.THIRD_PARTY,
1777
1776
  details: { resourceId: resource.id }
@@ -1820,7 +1819,7 @@ var MemoryStorageDynamoDB = class extends MemoryStorage {
1820
1819
  } catch (error) {
1821
1820
  throw new MastraError(
1822
1821
  {
1823
- id: "STORAGE_DYNAMODB_STORE_UPDATE_RESOURCE_FAILED",
1822
+ id: createStorageErrorId("DYNAMODB", "UPDATE_RESOURCE", "FAILED"),
1824
1823
  domain: ErrorDomain.STORAGE,
1825
1824
  category: ErrorCategory.THIRD_PARTY,
1826
1825
  details: { resourceId }
@@ -1830,339 +1829,40 @@ var MemoryStorageDynamoDB = class extends MemoryStorage {
1830
1829
  }
1831
1830
  }
1832
1831
  };
1833
- var StoreOperationsDynamoDB = class extends StoreOperations {
1834
- client;
1835
- tableName;
1832
+ var ScoresStorageDynamoDB = class extends ScoresStorage {
1836
1833
  service;
1837
- constructor({
1838
- service,
1839
- tableName,
1840
- client
1841
- }) {
1834
+ ttlConfig;
1835
+ constructor(config) {
1842
1836
  super();
1843
- this.service = service;
1844
- this.client = client;
1845
- this.tableName = tableName;
1846
- }
1847
- async hasColumn() {
1848
- return true;
1837
+ const resolved = resolveDynamoDBConfig(config);
1838
+ this.service = resolved.service;
1839
+ this.ttlConfig = resolved.ttl;
1849
1840
  }
1850
- async dropTable() {
1851
- }
1852
- // Helper methods for entity/table mapping
1853
- getEntityNameForTable(tableName) {
1854
- const mapping = {
1855
- [TABLE_THREADS]: "thread",
1856
- [TABLE_MESSAGES]: "message",
1857
- [TABLE_WORKFLOW_SNAPSHOT]: "workflow_snapshot",
1858
- [TABLE_EVALS]: "eval",
1859
- [TABLE_SCORERS]: "score",
1860
- [TABLE_TRACES]: "trace",
1861
- [TABLE_RESOURCES]: "resource",
1862
- [TABLE_AI_SPANS]: "ai_span"
1863
- };
1864
- return mapping[tableName] || null;
1841
+ async dangerouslyClearAll() {
1842
+ await deleteTableData(this.service, TABLE_SCORERS);
1865
1843
  }
1866
1844
  /**
1867
- * Pre-processes a record to ensure Date objects are converted to ISO strings
1868
- * This is necessary because ElectroDB validation happens before setters are applied
1869
- */
1870
- preprocessRecord(record) {
1871
- const processed = { ...record };
1872
- if (processed.createdAt instanceof Date) {
1873
- processed.createdAt = processed.createdAt.toISOString();
1874
- }
1875
- if (processed.updatedAt instanceof Date) {
1876
- processed.updatedAt = processed.updatedAt.toISOString();
1877
- }
1878
- if (processed.created_at instanceof Date) {
1879
- processed.created_at = processed.created_at.toISOString();
1880
- }
1881
- if (processed.result && typeof processed.result === "object") {
1882
- processed.result = JSON.stringify(processed.result);
1883
- }
1884
- if (processed.test_info && typeof processed.test_info === "object") {
1885
- processed.test_info = JSON.stringify(processed.test_info);
1886
- } else if (processed.test_info === void 0 || processed.test_info === null) {
1887
- delete processed.test_info;
1888
- }
1889
- if (processed.snapshot && typeof processed.snapshot === "object") {
1890
- processed.snapshot = JSON.stringify(processed.snapshot);
1891
- }
1892
- if (processed.attributes && typeof processed.attributes === "object") {
1893
- processed.attributes = JSON.stringify(processed.attributes);
1894
- }
1895
- if (processed.status && typeof processed.status === "object") {
1896
- processed.status = JSON.stringify(processed.status);
1897
- }
1898
- if (processed.events && typeof processed.events === "object") {
1899
- processed.events = JSON.stringify(processed.events);
1900
- }
1901
- if (processed.links && typeof processed.links === "object") {
1902
- processed.links = JSON.stringify(processed.links);
1903
- }
1904
- return processed;
1905
- }
1906
- /**
1907
- * Validates that the required DynamoDB table exists and is accessible.
1908
- * This does not check the table structure - it assumes the table
1909
- * was created with the correct structure via CDK/CloudFormation.
1910
- */
1911
- async validateTableExists() {
1912
- try {
1913
- const command = new DescribeTableCommand({
1914
- TableName: this.tableName
1915
- });
1916
- await this.client.send(command);
1917
- return true;
1918
- } catch (error) {
1919
- if (error.name === "ResourceNotFoundException") {
1920
- return false;
1921
- }
1922
- throw new MastraError(
1923
- {
1924
- id: "STORAGE_DYNAMODB_STORE_VALIDATE_TABLE_EXISTS_FAILED",
1925
- domain: ErrorDomain.STORAGE,
1926
- category: ErrorCategory.THIRD_PARTY,
1927
- details: { tableName: this.tableName }
1928
- },
1929
- error
1930
- );
1931
- }
1932
- }
1933
- /**
1934
- * This method is modified for DynamoDB with ElectroDB single-table design.
1935
- * It assumes the table is created and managed externally via CDK/CloudFormation.
1845
+ * DynamoDB-specific score row transformation.
1936
1846
  *
1937
- * This implementation only validates that the required table exists and is accessible.
1938
- * No table creation is attempted - we simply check if we can access the table.
1939
- */
1940
- async createTable({ tableName }) {
1941
- this.logger.debug("Validating access to externally managed table", { tableName, physicalTable: this.tableName });
1942
- try {
1943
- const tableExists = await this.validateTableExists();
1944
- if (!tableExists) {
1945
- this.logger.error(
1946
- `Table ${this.tableName} does not exist or is not accessible. It should be created via CDK/CloudFormation.`
1947
- );
1948
- throw new Error(
1949
- `Table ${this.tableName} does not exist or is not accessible. Ensure it's created via CDK/CloudFormation before using this store.`
1950
- );
1951
- }
1952
- this.logger.debug(`Table ${this.tableName} exists and is accessible`);
1953
- } catch (error) {
1954
- this.logger.error("Error validating table access", { tableName: this.tableName, error });
1955
- throw new MastraError(
1956
- {
1957
- id: "STORAGE_DYNAMODB_STORE_VALIDATE_TABLE_ACCESS_FAILED",
1958
- domain: ErrorDomain.STORAGE,
1959
- category: ErrorCategory.THIRD_PARTY,
1960
- details: { tableName: this.tableName }
1961
- },
1962
- error
1963
- );
1964
- }
1965
- }
1966
- async insert({ tableName, record }) {
1967
- this.logger.debug("DynamoDB insert called", { tableName });
1968
- const entityName = this.getEntityNameForTable(tableName);
1969
- if (!entityName || !this.service.entities[entityName]) {
1970
- throw new MastraError({
1971
- id: "STORAGE_DYNAMODB_STORE_INSERT_INVALID_ARGS",
1972
- domain: ErrorDomain.STORAGE,
1973
- category: ErrorCategory.USER,
1974
- text: "No entity defined for tableName",
1975
- details: { tableName }
1976
- });
1977
- }
1978
- try {
1979
- const dataToSave = { entity: entityName, ...this.preprocessRecord(record) };
1980
- await this.service.entities[entityName].create(dataToSave).go();
1981
- } catch (error) {
1982
- throw new MastraError(
1983
- {
1984
- id: "STORAGE_DYNAMODB_STORE_INSERT_FAILED",
1985
- domain: ErrorDomain.STORAGE,
1986
- category: ErrorCategory.THIRD_PARTY,
1987
- details: { tableName }
1988
- },
1989
- error
1990
- );
1991
- }
1992
- }
1993
- async alterTable(_args) {
1994
- }
1995
- /**
1996
- * Clear all items from a logical "table" (entity type)
1997
- */
1998
- async clearTable({ tableName }) {
1999
- this.logger.debug("DynamoDB clearTable called", { tableName });
2000
- const entityName = this.getEntityNameForTable(tableName);
2001
- if (!entityName || !this.service.entities[entityName]) {
2002
- throw new MastraError({
2003
- id: "STORAGE_DYNAMODB_STORE_CLEAR_TABLE_INVALID_ARGS",
2004
- domain: ErrorDomain.STORAGE,
2005
- category: ErrorCategory.USER,
2006
- text: "No entity defined for tableName",
2007
- details: { tableName }
2008
- });
2009
- }
2010
- try {
2011
- const result = await this.service.entities[entityName].scan.go({ pages: "all" });
2012
- if (!result.data.length) {
2013
- this.logger.debug(`No records found to clear for ${tableName}`);
2014
- return;
2015
- }
2016
- this.logger.debug(`Found ${result.data.length} records to delete for ${tableName}`);
2017
- const keysToDelete = result.data.map((item) => {
2018
- const key = { entity: entityName };
2019
- switch (entityName) {
2020
- case "thread":
2021
- if (!item.id) throw new Error(`Missing required key 'id' for entity 'thread'`);
2022
- key.id = item.id;
2023
- break;
2024
- case "message":
2025
- if (!item.id) throw new Error(`Missing required key 'id' for entity 'message'`);
2026
- key.id = item.id;
2027
- break;
2028
- case "workflow_snapshot":
2029
- if (!item.workflow_name)
2030
- throw new Error(`Missing required key 'workflow_name' for entity 'workflow_snapshot'`);
2031
- if (!item.run_id) throw new Error(`Missing required key 'run_id' for entity 'workflow_snapshot'`);
2032
- key.workflow_name = item.workflow_name;
2033
- key.run_id = item.run_id;
2034
- break;
2035
- case "eval":
2036
- if (!item.run_id) throw new Error(`Missing required key 'run_id' for entity 'eval'`);
2037
- key.run_id = item.run_id;
2038
- break;
2039
- case "trace":
2040
- if (!item.id) throw new Error(`Missing required key 'id' for entity 'trace'`);
2041
- key.id = item.id;
2042
- break;
2043
- case "score":
2044
- if (!item.id) throw new Error(`Missing required key 'id' for entity 'score'`);
2045
- key.id = item.id;
2046
- break;
2047
- default:
2048
- this.logger.warn(`Unknown entity type encountered during clearTable: ${entityName}`);
2049
- throw new Error(`Cannot construct delete key for unknown entity type: ${entityName}`);
2050
- }
2051
- return key;
2052
- });
2053
- const batchSize = 25;
2054
- for (let i = 0; i < keysToDelete.length; i += batchSize) {
2055
- const batchKeys = keysToDelete.slice(i, i + batchSize);
2056
- await this.service.entities[entityName].delete(batchKeys).go();
2057
- }
2058
- this.logger.debug(`Successfully cleared all records for ${tableName}`);
2059
- } catch (error) {
2060
- throw new MastraError(
2061
- {
2062
- id: "STORAGE_DYNAMODB_STORE_CLEAR_TABLE_FAILED",
2063
- domain: ErrorDomain.STORAGE,
2064
- category: ErrorCategory.THIRD_PARTY,
2065
- details: { tableName }
2066
- },
2067
- error
2068
- );
2069
- }
2070
- }
2071
- /**
2072
- * Insert multiple records as a batch
1847
+ * Note: This implementation does NOT use coreTransformScoreRow because:
1848
+ * 1. ElectroDB already parses JSON fields via its entity getters
1849
+ * 2. DynamoDB stores empty strings for null values (which need special handling)
1850
+ * 3. 'entity' is a reserved ElectroDB key, so we use 'entityData' column
2073
1851
  */
2074
- async batchInsert({ tableName, records }) {
2075
- this.logger.debug("DynamoDB batchInsert called", { tableName, count: records.length });
2076
- const entityName = this.getEntityNameForTable(tableName);
2077
- if (!entityName || !this.service.entities[entityName]) {
2078
- throw new MastraError({
2079
- id: "STORAGE_DYNAMODB_STORE_BATCH_INSERT_INVALID_ARGS",
2080
- domain: ErrorDomain.STORAGE,
2081
- category: ErrorCategory.USER,
2082
- text: "No entity defined for tableName",
2083
- details: { tableName }
2084
- });
2085
- }
2086
- const recordsToSave = records.map((rec) => ({ entity: entityName, ...this.preprocessRecord(rec) }));
2087
- const batchSize = 25;
2088
- const batches = [];
2089
- for (let i = 0; i < recordsToSave.length; i += batchSize) {
2090
- const batch = recordsToSave.slice(i, i + batchSize);
2091
- batches.push(batch);
2092
- }
2093
- try {
2094
- for (const batch of batches) {
2095
- for (const recordData of batch) {
2096
- if (!recordData.entity) {
2097
- this.logger.error("Missing entity property in record data for batchInsert", { recordData, tableName });
2098
- throw new Error(`Internal error: Missing entity property during batchInsert for ${tableName}`);
2099
- }
2100
- this.logger.debug("Attempting to create record in batchInsert:", { entityName, recordData });
2101
- await this.service.entities[entityName].create(recordData).go();
2102
- }
2103
- }
2104
- } catch (error) {
2105
- throw new MastraError(
2106
- {
2107
- id: "STORAGE_DYNAMODB_STORE_BATCH_INSERT_FAILED",
2108
- domain: ErrorDomain.STORAGE,
2109
- category: ErrorCategory.THIRD_PARTY,
2110
- details: { tableName }
2111
- },
2112
- error
2113
- );
2114
- }
2115
- }
2116
- /**
2117
- * Load a record by its keys
2118
- */
2119
- async load({ tableName, keys }) {
2120
- this.logger.debug("DynamoDB load called", { tableName, keys });
2121
- const entityName = this.getEntityNameForTable(tableName);
2122
- if (!entityName || !this.service.entities[entityName]) {
2123
- throw new MastraError({
2124
- id: "STORAGE_DYNAMODB_STORE_LOAD_INVALID_ARGS",
2125
- domain: ErrorDomain.STORAGE,
2126
- category: ErrorCategory.USER,
2127
- text: "No entity defined for tableName",
2128
- details: { tableName }
2129
- });
2130
- }
2131
- try {
2132
- const keyObject = { entity: entityName, ...keys };
2133
- const result = await this.service.entities[entityName].get(keyObject).go();
2134
- if (!result.data) {
2135
- return null;
1852
+ parseScoreData(data) {
1853
+ const result = {};
1854
+ for (const key of Object.keys(SCORERS_SCHEMA)) {
1855
+ if (["traceId", "resourceId", "threadId", "spanId"].includes(key)) {
1856
+ result[key] = data[key] === "" ? null : data[key];
1857
+ continue;
2136
1858
  }
2137
- let data = result.data;
2138
- return data;
2139
- } catch (error) {
2140
- throw new MastraError(
2141
- {
2142
- id: "STORAGE_DYNAMODB_STORE_LOAD_FAILED",
2143
- domain: ErrorDomain.STORAGE,
2144
- category: ErrorCategory.THIRD_PARTY,
2145
- details: { tableName }
2146
- },
2147
- error
2148
- );
1859
+ result[key] = data[key];
2149
1860
  }
2150
- }
2151
- };
2152
- var ScoresStorageDynamoDB = class extends ScoresStorage {
2153
- service;
2154
- constructor({ service }) {
2155
- super();
2156
- this.service = service;
2157
- }
2158
- // Helper function to parse score data (handle JSON fields)
2159
- parseScoreData(data) {
1861
+ result.entity = data.entityData ?? null;
2160
1862
  return {
2161
- ...data,
2162
- // Convert date strings back to Date objects for consistency
1863
+ ...result,
2163
1864
  createdAt: data.createdAt ? new Date(data.createdAt) : /* @__PURE__ */ new Date(),
2164
1865
  updatedAt: data.updatedAt ? new Date(data.updatedAt) : /* @__PURE__ */ new Date()
2165
- // JSON fields are already transformed by the entity's getters
2166
1866
  };
2167
1867
  }
2168
1868
  async getScoreById({ id }) {
@@ -2176,7 +1876,7 @@ var ScoresStorageDynamoDB = class extends ScoresStorage {
2176
1876
  } catch (error) {
2177
1877
  throw new MastraError(
2178
1878
  {
2179
- id: "STORAGE_DYNAMODB_STORE_GET_SCORE_BY_ID_FAILED",
1879
+ id: createStorageErrorId("DYNAMODB", "GET_SCORE_BY_ID", "FAILED"),
2180
1880
  domain: ErrorDomain.STORAGE,
2181
1881
  category: ErrorCategory.THIRD_PARTY,
2182
1882
  details: { id }
@@ -2192,57 +1892,68 @@ var ScoresStorageDynamoDB = class extends ScoresStorage {
2192
1892
  } catch (error) {
2193
1893
  throw new MastraError(
2194
1894
  {
2195
- id: "STORAGE_DYNAMODB_STORE_SAVE_SCORE_FAILED",
1895
+ id: createStorageErrorId("DYNAMODB", "SAVE_SCORE", "VALIDATION_FAILED"),
2196
1896
  domain: ErrorDomain.STORAGE,
2197
- category: ErrorCategory.THIRD_PARTY
1897
+ category: ErrorCategory.USER,
1898
+ details: {
1899
+ scorer: typeof score.scorer?.id === "string" ? score.scorer.id : String(score.scorer?.id ?? "unknown"),
1900
+ entityId: score.entityId ?? "unknown",
1901
+ entityType: score.entityType ?? "unknown",
1902
+ traceId: score.traceId ?? "",
1903
+ spanId: score.spanId ?? ""
1904
+ }
2198
1905
  },
2199
1906
  error
2200
1907
  );
2201
1908
  }
2202
1909
  const now = /* @__PURE__ */ new Date();
2203
- const scoreId = `score-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
2204
- const scoreData = {
2205
- entity: "score",
2206
- id: scoreId,
2207
- scorerId: validatedScore.scorerId,
2208
- traceId: validatedScore.traceId || "",
2209
- spanId: validatedScore.spanId || "",
2210
- runId: validatedScore.runId,
2211
- scorer: typeof validatedScore.scorer === "string" ? validatedScore.scorer : JSON.stringify(validatedScore.scorer),
2212
- preprocessStepResult: typeof validatedScore.preprocessStepResult === "string" ? validatedScore.preprocessStepResult : JSON.stringify(validatedScore.preprocessStepResult),
2213
- analyzeStepResult: typeof validatedScore.analyzeStepResult === "string" ? validatedScore.analyzeStepResult : JSON.stringify(validatedScore.analyzeStepResult),
2214
- score: validatedScore.score,
2215
- reason: validatedScore.reason,
2216
- preprocessPrompt: validatedScore.preprocessPrompt,
2217
- generateScorePrompt: validatedScore.generateScorePrompt,
2218
- generateReasonPrompt: validatedScore.generateReasonPrompt,
2219
- analyzePrompt: validatedScore.analyzePrompt,
2220
- input: typeof validatedScore.input === "string" ? validatedScore.input : JSON.stringify(validatedScore.input),
2221
- output: typeof validatedScore.output === "string" ? validatedScore.output : JSON.stringify(validatedScore.output),
2222
- additionalContext: typeof validatedScore.additionalContext === "string" ? validatedScore.additionalContext : JSON.stringify(validatedScore.additionalContext),
2223
- runtimeContext: typeof validatedScore.runtimeContext === "string" ? validatedScore.runtimeContext : JSON.stringify(validatedScore.runtimeContext),
2224
- entityType: validatedScore.entityType,
2225
- entityData: typeof validatedScore.entity === "string" ? validatedScore.entity : JSON.stringify(validatedScore.entity),
2226
- entityId: validatedScore.entityId,
2227
- source: validatedScore.source,
2228
- resourceId: validatedScore.resourceId || "",
2229
- threadId: validatedScore.threadId || "",
2230
- createdAt: now.toISOString(),
2231
- updatedAt: now.toISOString()
2232
- };
1910
+ const scoreId = crypto.randomUUID();
1911
+ const scorer = typeof validatedScore.scorer === "string" ? validatedScore.scorer : JSON.stringify(validatedScore.scorer);
1912
+ const preprocessStepResult = typeof validatedScore.preprocessStepResult === "string" ? validatedScore.preprocessStepResult : JSON.stringify(validatedScore.preprocessStepResult);
1913
+ const analyzeStepResult = typeof validatedScore.analyzeStepResult === "string" ? validatedScore.analyzeStepResult : JSON.stringify(validatedScore.analyzeStepResult);
1914
+ const input = typeof validatedScore.input === "string" ? validatedScore.input : JSON.stringify(validatedScore.input);
1915
+ const output = typeof validatedScore.output === "string" ? validatedScore.output : JSON.stringify(validatedScore.output);
1916
+ const requestContext = typeof validatedScore.requestContext === "string" ? validatedScore.requestContext : JSON.stringify(validatedScore.requestContext);
1917
+ const entity = typeof validatedScore.entity === "string" ? validatedScore.entity : JSON.stringify(validatedScore.entity);
1918
+ const metadata = typeof validatedScore.metadata === "string" ? validatedScore.metadata : validatedScore.metadata ? JSON.stringify(validatedScore.metadata) : void 0;
1919
+ const additionalContext = typeof validatedScore.additionalContext === "string" ? validatedScore.additionalContext : validatedScore.additionalContext ? JSON.stringify(validatedScore.additionalContext) : void 0;
1920
+ const scoreData = Object.fromEntries(
1921
+ Object.entries({
1922
+ ...validatedScore,
1923
+ entity: "score",
1924
+ id: scoreId,
1925
+ scorer,
1926
+ preprocessStepResult,
1927
+ analyzeStepResult,
1928
+ input,
1929
+ output,
1930
+ requestContext,
1931
+ metadata,
1932
+ additionalContext,
1933
+ entityData: entity,
1934
+ traceId: validatedScore.traceId || "",
1935
+ resourceId: validatedScore.resourceId || "",
1936
+ threadId: validatedScore.threadId || "",
1937
+ spanId: validatedScore.spanId || "",
1938
+ createdAt: now.toISOString(),
1939
+ updatedAt: now.toISOString(),
1940
+ ...getTtlProps("score", this.ttlConfig)
1941
+ }).filter(([_, value]) => value !== void 0 && value !== null)
1942
+ );
2233
1943
  try {
2234
1944
  await this.service.entities.score.upsert(scoreData).go();
2235
- const savedScore = {
2236
- ...score,
2237
- id: scoreId,
2238
- createdAt: now,
2239
- updatedAt: now
1945
+ return {
1946
+ score: {
1947
+ ...validatedScore,
1948
+ id: scoreId,
1949
+ createdAt: now,
1950
+ updatedAt: now
1951
+ }
2240
1952
  };
2241
- return { score: savedScore };
2242
1953
  } catch (error) {
2243
1954
  throw new MastraError(
2244
1955
  {
2245
- id: "STORAGE_DYNAMODB_STORE_SAVE_SCORE_FAILED",
1956
+ id: createStorageErrorId("DYNAMODB", "SAVE_SCORE", "FAILED"),
2246
1957
  domain: ErrorDomain.STORAGE,
2247
1958
  category: ErrorCategory.THIRD_PARTY,
2248
1959
  details: { scorerId: score.scorerId, runId: score.runId }
@@ -2251,7 +1962,7 @@ var ScoresStorageDynamoDB = class extends ScoresStorage {
2251
1962
  );
2252
1963
  }
2253
1964
  }
2254
- async getScoresByScorerId({
1965
+ async listScoresByScorerId({
2255
1966
  scorerId,
2256
1967
  pagination,
2257
1968
  entityId,
@@ -2272,24 +1983,25 @@ var ScoresStorageDynamoDB = class extends ScoresStorage {
2272
1983
  allScores = allScores.filter((score) => score.source === source);
2273
1984
  }
2274
1985
  allScores.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
2275
- const startIndex = pagination.page * pagination.perPage;
2276
- const endIndex = startIndex + pagination.perPage;
2277
- const paginatedScores = allScores.slice(startIndex, endIndex);
1986
+ const { page, perPage: perPageInput } = pagination;
1987
+ const perPage = normalizePerPage(perPageInput, Number.MAX_SAFE_INTEGER);
1988
+ const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
2278
1989
  const total = allScores.length;
2279
- const hasMore = endIndex < total;
1990
+ const end = perPageInput === false ? allScores.length : start + perPage;
1991
+ const paginatedScores = allScores.slice(start, end);
2280
1992
  return {
2281
1993
  scores: paginatedScores,
2282
1994
  pagination: {
2283
1995
  total,
2284
- page: pagination.page,
2285
- perPage: pagination.perPage,
2286
- hasMore
1996
+ page,
1997
+ perPage: perPageForResponse,
1998
+ hasMore: end < total
2287
1999
  }
2288
2000
  };
2289
2001
  } catch (error) {
2290
2002
  throw new MastraError(
2291
2003
  {
2292
- id: "STORAGE_DYNAMODB_STORE_GET_SCORES_BY_SCORER_ID_FAILED",
2004
+ id: createStorageErrorId("DYNAMODB", "LIST_SCORES_BY_SCORER_ID", "FAILED"),
2293
2005
  domain: ErrorDomain.STORAGE,
2294
2006
  category: ErrorCategory.THIRD_PARTY,
2295
2007
  details: {
@@ -2305,7 +2017,7 @@ var ScoresStorageDynamoDB = class extends ScoresStorage {
2305
2017
  );
2306
2018
  }
2307
2019
  }
2308
- async getScoresByRunId({
2020
+ async listScoresByRunId({
2309
2021
  runId,
2310
2022
  pagination
2311
2023
  }) {
@@ -2315,24 +2027,25 @@ var ScoresStorageDynamoDB = class extends ScoresStorage {
2315
2027
  const results = await query.go();
2316
2028
  const allScores = results.data.map((data) => this.parseScoreData(data));
2317
2029
  allScores.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
2318
- const startIndex = pagination.page * pagination.perPage;
2319
- const endIndex = startIndex + pagination.perPage;
2320
- const paginatedScores = allScores.slice(startIndex, endIndex);
2030
+ const { page, perPage: perPageInput } = pagination;
2031
+ const perPage = normalizePerPage(perPageInput, Number.MAX_SAFE_INTEGER);
2032
+ const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
2321
2033
  const total = allScores.length;
2322
- const hasMore = endIndex < total;
2034
+ const end = perPageInput === false ? allScores.length : start + perPage;
2035
+ const paginatedScores = allScores.slice(start, end);
2323
2036
  return {
2324
2037
  scores: paginatedScores,
2325
2038
  pagination: {
2326
2039
  total,
2327
- page: pagination.page,
2328
- perPage: pagination.perPage,
2329
- hasMore
2040
+ page,
2041
+ perPage: perPageForResponse,
2042
+ hasMore: end < total
2330
2043
  }
2331
2044
  };
2332
2045
  } catch (error) {
2333
2046
  throw new MastraError(
2334
2047
  {
2335
- id: "STORAGE_DYNAMODB_STORE_GET_SCORES_BY_RUN_ID_FAILED",
2048
+ id: createStorageErrorId("DYNAMODB", "LIST_SCORES_BY_RUN_ID", "FAILED"),
2336
2049
  domain: ErrorDomain.STORAGE,
2337
2050
  category: ErrorCategory.THIRD_PARTY,
2338
2051
  details: { runId, page: pagination.page, perPage: pagination.perPage }
@@ -2341,7 +2054,7 @@ var ScoresStorageDynamoDB = class extends ScoresStorage {
2341
2054
  );
2342
2055
  }
2343
2056
  }
2344
- async getScoresByEntityId({
2057
+ async listScoresByEntityId({
2345
2058
  entityId,
2346
2059
  entityType,
2347
2060
  pagination
@@ -2353,24 +2066,25 @@ var ScoresStorageDynamoDB = class extends ScoresStorage {
2353
2066
  let allScores = results.data.map((data) => this.parseScoreData(data));
2354
2067
  allScores = allScores.filter((score) => score.entityType === entityType);
2355
2068
  allScores.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
2356
- const startIndex = pagination.page * pagination.perPage;
2357
- const endIndex = startIndex + pagination.perPage;
2358
- const paginatedScores = allScores.slice(startIndex, endIndex);
2069
+ const { page, perPage: perPageInput } = pagination;
2070
+ const perPage = normalizePerPage(perPageInput, Number.MAX_SAFE_INTEGER);
2071
+ const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
2359
2072
  const total = allScores.length;
2360
- const hasMore = endIndex < total;
2073
+ const end = perPageInput === false ? allScores.length : start + perPage;
2074
+ const paginatedScores = allScores.slice(start, end);
2361
2075
  return {
2362
2076
  scores: paginatedScores,
2363
2077
  pagination: {
2364
2078
  total,
2365
- page: pagination.page,
2366
- perPage: pagination.perPage,
2367
- hasMore
2079
+ page,
2080
+ perPage: perPageForResponse,
2081
+ hasMore: end < total
2368
2082
  }
2369
2083
  };
2370
2084
  } catch (error) {
2371
2085
  throw new MastraError(
2372
2086
  {
2373
- id: "STORAGE_DYNAMODB_STORE_GET_SCORES_BY_ENTITY_ID_FAILED",
2087
+ id: createStorageErrorId("DYNAMODB", "LIST_SCORES_BY_ENTITY_ID", "FAILED"),
2374
2088
  domain: ErrorDomain.STORAGE,
2375
2089
  category: ErrorCategory.THIRD_PARTY,
2376
2090
  details: { entityId, entityType, page: pagination.page, perPage: pagination.perPage }
@@ -2379,7 +2093,7 @@ var ScoresStorageDynamoDB = class extends ScoresStorage {
2379
2093
  );
2380
2094
  }
2381
2095
  }
2382
- async getScoresBySpan({
2096
+ async listScoresBySpan({
2383
2097
  traceId,
2384
2098
  spanId,
2385
2099
  pagination
@@ -2390,24 +2104,25 @@ var ScoresStorageDynamoDB = class extends ScoresStorage {
2390
2104
  const results = await query.go();
2391
2105
  const allScores = results.data.map((data) => this.parseScoreData(data));
2392
2106
  allScores.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
2393
- const startIndex = pagination.page * pagination.perPage;
2394
- const endIndex = startIndex + pagination.perPage;
2395
- const paginatedScores = allScores.slice(startIndex, endIndex);
2107
+ const { page, perPage: perPageInput } = pagination;
2108
+ const perPage = normalizePerPage(perPageInput, Number.MAX_SAFE_INTEGER);
2109
+ const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
2396
2110
  const total = allScores.length;
2397
- const hasMore = endIndex < total;
2111
+ const end = perPageInput === false ? allScores.length : start + perPage;
2112
+ const paginatedScores = allScores.slice(start, end);
2398
2113
  return {
2399
2114
  scores: paginatedScores,
2400
2115
  pagination: {
2401
2116
  total,
2402
- page: pagination.page,
2403
- perPage: pagination.perPage,
2404
- hasMore
2117
+ page,
2118
+ perPage: perPageForResponse,
2119
+ hasMore: end < total
2405
2120
  }
2406
2121
  };
2407
2122
  } catch (error) {
2408
2123
  throw new MastraError(
2409
2124
  {
2410
- id: "STORAGE_DYNAMODB_STORE_GET_SCORES_BY_SPAN_FAILED",
2125
+ id: createStorageErrorId("DYNAMODB", "LIST_SCORES_BY_SPAN", "FAILED"),
2411
2126
  domain: ErrorDomain.STORAGE,
2412
2127
  category: ErrorCategory.THIRD_PARTY,
2413
2128
  details: { traceId, spanId, page: pagination.page, perPage: pagination.perPage }
@@ -2417,239 +2132,6 @@ var ScoresStorageDynamoDB = class extends ScoresStorage {
2417
2132
  }
2418
2133
  }
2419
2134
  };
2420
- var TracesStorageDynamoDB = class extends TracesStorage {
2421
- service;
2422
- operations;
2423
- constructor({ service, operations }) {
2424
- super();
2425
- this.service = service;
2426
- this.operations = operations;
2427
- }
2428
- // Trace operations
2429
- async getTraces(args) {
2430
- const { name, scope, page, perPage } = args;
2431
- this.logger.debug("Getting traces", { name, scope, page, perPage });
2432
- try {
2433
- let query;
2434
- if (name) {
2435
- query = this.service.entities.trace.query.byName({ entity: "trace", name });
2436
- } else if (scope) {
2437
- query = this.service.entities.trace.query.byScope({ entity: "trace", scope });
2438
- } else {
2439
- this.logger.warn("Performing a scan operation on traces - consider using a more specific query");
2440
- query = this.service.entities.trace.scan;
2441
- }
2442
- let items = [];
2443
- let cursor = null;
2444
- let pagesFetched = 0;
2445
- const startPage = page > 0 ? page : 1;
2446
- do {
2447
- const results = await query.go({ cursor, limit: perPage });
2448
- pagesFetched++;
2449
- if (pagesFetched === startPage) {
2450
- items = results.data;
2451
- break;
2452
- }
2453
- cursor = results.cursor;
2454
- if (!cursor && results.data.length > 0 && pagesFetched < startPage) {
2455
- break;
2456
- }
2457
- } while (cursor && pagesFetched < startPage);
2458
- return items;
2459
- } catch (error) {
2460
- throw new MastraError(
2461
- {
2462
- id: "STORAGE_DYNAMODB_STORE_GET_TRACES_FAILED",
2463
- domain: ErrorDomain.STORAGE,
2464
- category: ErrorCategory.THIRD_PARTY
2465
- },
2466
- error
2467
- );
2468
- }
2469
- }
2470
- async batchTraceInsert({ records }) {
2471
- this.logger.debug("Batch inserting traces", { count: records.length });
2472
- if (!records.length) {
2473
- return;
2474
- }
2475
- try {
2476
- const recordsToSave = records.map((rec) => ({ entity: "trace", ...rec }));
2477
- await this.operations.batchInsert({
2478
- tableName: TABLE_TRACES,
2479
- records: recordsToSave
2480
- // Pass records with 'entity' included
2481
- });
2482
- } catch (error) {
2483
- throw new MastraError(
2484
- {
2485
- id: "STORAGE_DYNAMODB_STORE_BATCH_TRACE_INSERT_FAILED",
2486
- domain: ErrorDomain.STORAGE,
2487
- category: ErrorCategory.THIRD_PARTY,
2488
- details: { count: records.length }
2489
- },
2490
- error
2491
- );
2492
- }
2493
- }
2494
- async getTracesPaginated(args) {
2495
- const { name, scope, page = 0, perPage = 100, attributes, filters, dateRange } = args;
2496
- this.logger.debug("Getting traces with pagination", { name, scope, page, perPage, attributes, filters, dateRange });
2497
- try {
2498
- let query;
2499
- if (name) {
2500
- query = this.service.entities.trace.query.byName({ entity: "trace", name });
2501
- } else if (scope) {
2502
- query = this.service.entities.trace.query.byScope({ entity: "trace", scope });
2503
- } else {
2504
- this.logger.warn("Performing a scan operation on traces - consider using a more specific query");
2505
- query = this.service.entities.trace.scan;
2506
- }
2507
- const results = await query.go({
2508
- order: "desc",
2509
- pages: "all"
2510
- // Get all pages to apply filtering and pagination
2511
- });
2512
- if (!results.data.length) {
2513
- return {
2514
- traces: [],
2515
- total: 0,
2516
- page,
2517
- perPage,
2518
- hasMore: false
2519
- };
2520
- }
2521
- let filteredData = results.data;
2522
- if (attributes) {
2523
- filteredData = filteredData.filter((item) => {
2524
- try {
2525
- let itemAttributes = {};
2526
- if (item.attributes) {
2527
- if (typeof item.attributes === "string") {
2528
- if (item.attributes === "[object Object]") {
2529
- itemAttributes = {};
2530
- } else {
2531
- try {
2532
- itemAttributes = JSON.parse(item.attributes);
2533
- } catch {
2534
- itemAttributes = {};
2535
- }
2536
- }
2537
- } else if (typeof item.attributes === "object") {
2538
- itemAttributes = item.attributes;
2539
- }
2540
- }
2541
- return Object.entries(attributes).every(([key, value]) => itemAttributes[key] === value);
2542
- } catch (e) {
2543
- this.logger.warn("Failed to parse attributes during filtering", { item, error: e });
2544
- return false;
2545
- }
2546
- });
2547
- }
2548
- if (dateRange?.start) {
2549
- filteredData = filteredData.filter((item) => {
2550
- const itemDate = new Date(item.createdAt);
2551
- return itemDate >= dateRange.start;
2552
- });
2553
- }
2554
- if (dateRange?.end) {
2555
- filteredData = filteredData.filter((item) => {
2556
- const itemDate = new Date(item.createdAt);
2557
- return itemDate <= dateRange.end;
2558
- });
2559
- }
2560
- const total = filteredData.length;
2561
- const start = page * perPage;
2562
- const end = start + perPage;
2563
- const paginatedData = filteredData.slice(start, end);
2564
- const traces = paginatedData.map((item) => {
2565
- let attributes2;
2566
- if (item.attributes) {
2567
- if (typeof item.attributes === "string") {
2568
- if (item.attributes === "[object Object]") {
2569
- attributes2 = void 0;
2570
- } else {
2571
- try {
2572
- attributes2 = JSON.parse(item.attributes);
2573
- } catch {
2574
- attributes2 = void 0;
2575
- }
2576
- }
2577
- } else if (typeof item.attributes === "object") {
2578
- attributes2 = item.attributes;
2579
- }
2580
- }
2581
- let status;
2582
- if (item.status) {
2583
- if (typeof item.status === "string") {
2584
- try {
2585
- status = JSON.parse(item.status);
2586
- } catch {
2587
- status = void 0;
2588
- }
2589
- } else if (typeof item.status === "object") {
2590
- status = item.status;
2591
- }
2592
- }
2593
- let events;
2594
- if (item.events) {
2595
- if (typeof item.events === "string") {
2596
- try {
2597
- events = JSON.parse(item.events);
2598
- } catch {
2599
- events = void 0;
2600
- }
2601
- } else if (Array.isArray(item.events)) {
2602
- events = item.events;
2603
- }
2604
- }
2605
- let links;
2606
- if (item.links) {
2607
- if (typeof item.links === "string") {
2608
- try {
2609
- links = JSON.parse(item.links);
2610
- } catch {
2611
- links = void 0;
2612
- }
2613
- } else if (Array.isArray(item.links)) {
2614
- links = item.links;
2615
- }
2616
- }
2617
- return {
2618
- id: item.id,
2619
- parentSpanId: item.parentSpanId,
2620
- name: item.name,
2621
- traceId: item.traceId,
2622
- scope: item.scope,
2623
- kind: item.kind,
2624
- attributes: attributes2,
2625
- status,
2626
- events,
2627
- links,
2628
- other: item.other,
2629
- startTime: item.startTime,
2630
- endTime: item.endTime,
2631
- createdAt: item.createdAt
2632
- };
2633
- });
2634
- return {
2635
- traces,
2636
- total,
2637
- page,
2638
- perPage,
2639
- hasMore: end < total
2640
- };
2641
- } catch (error) {
2642
- throw new MastraError(
2643
- {
2644
- id: "STORAGE_DYNAMODB_STORE_GET_TRACES_PAGINATED_FAILED",
2645
- domain: ErrorDomain.STORAGE,
2646
- category: ErrorCategory.THIRD_PARTY
2647
- },
2648
- error
2649
- );
2650
- }
2651
- }
2652
- };
2653
2135
  function formatWorkflowRun(snapshotData) {
2654
2136
  return {
2655
2137
  workflowName: snapshotData.workflow_name,
@@ -2660,54 +2142,228 @@ function formatWorkflowRun(snapshotData) {
2660
2142
  resourceId: snapshotData.resourceId
2661
2143
  };
2662
2144
  }
2145
+ var MAX_RETRIES = 5;
2146
+ var BASE_DELAY_MS = 50;
2663
2147
  var WorkflowStorageDynamoDB = class extends WorkflowsStorage {
2664
2148
  service;
2665
- constructor({ service }) {
2149
+ ttlConfig;
2150
+ constructor(config) {
2666
2151
  super();
2667
- this.service = service;
2152
+ const resolved = resolveDynamoDBConfig(config);
2153
+ this.service = resolved.service;
2154
+ this.ttlConfig = resolved.ttl;
2155
+ }
2156
+ supportsConcurrentUpdates() {
2157
+ return true;
2158
+ }
2159
+ async dangerouslyClearAll() {
2160
+ await deleteTableData(this.service, TABLE_WORKFLOW_SNAPSHOT);
2161
+ }
2162
+ /**
2163
+ * Helper to check if an error is a DynamoDB conditional check failure
2164
+ */
2165
+ isConditionalCheckFailed(error) {
2166
+ if (error && typeof error === "object") {
2167
+ const err = error;
2168
+ if (err.name === "ConditionalCheckFailedException" || err.code === "ConditionalCheckFailedException" || (err.__type?.includes("ConditionalCheckFailedException") ?? false)) {
2169
+ return true;
2170
+ }
2171
+ if (err.message?.includes("conditional request failed")) {
2172
+ return true;
2173
+ }
2174
+ if (err.cause && typeof err.cause === "object") {
2175
+ const cause = err.cause;
2176
+ if (cause.name === "ConditionalCheckFailedException" || cause.code === "ConditionalCheckFailedException") {
2177
+ return true;
2178
+ }
2179
+ }
2180
+ }
2181
+ return false;
2182
+ }
2183
+ /**
2184
+ * Helper to delay with exponential backoff and jitter
2185
+ */
2186
+ async delay(attempt) {
2187
+ const backoff = BASE_DELAY_MS * Math.pow(2, attempt);
2188
+ const jitter = Math.random() * backoff * 0.5;
2189
+ await new Promise((resolve) => setTimeout(resolve, backoff + jitter));
2668
2190
  }
2669
- updateWorkflowResults({
2670
- // workflowName,
2671
- // runId,
2672
- // stepId,
2673
- // result,
2674
- // runtimeContext,
2191
+ async updateWorkflowResults({
2192
+ workflowName,
2193
+ runId,
2194
+ stepId,
2195
+ result,
2196
+ requestContext
2675
2197
  }) {
2676
- throw new Error("Method not implemented.");
2198
+ for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
2199
+ try {
2200
+ const existingRecord = await this.service.entities.workflow_snapshot.get({
2201
+ entity: "workflow_snapshot",
2202
+ workflow_name: workflowName,
2203
+ run_id: runId
2204
+ }).go();
2205
+ const now = /* @__PURE__ */ new Date();
2206
+ let snapshot;
2207
+ let previousUpdatedAt;
2208
+ if (!existingRecord.data) {
2209
+ snapshot = {
2210
+ context: {},
2211
+ activePaths: [],
2212
+ timestamp: Date.now(),
2213
+ suspendedPaths: {},
2214
+ activeStepsPath: {},
2215
+ resumeLabels: {},
2216
+ serializedStepGraph: [],
2217
+ status: "pending",
2218
+ value: {},
2219
+ waitingPaths: {},
2220
+ runId,
2221
+ requestContext: {}
2222
+ };
2223
+ } else {
2224
+ snapshot = existingRecord.data.snapshot;
2225
+ previousUpdatedAt = existingRecord.data.updatedAt;
2226
+ }
2227
+ snapshot.context[stepId] = result;
2228
+ snapshot.requestContext = { ...snapshot.requestContext, ...requestContext };
2229
+ const data = {
2230
+ entity: "workflow_snapshot",
2231
+ workflow_name: workflowName,
2232
+ run_id: runId,
2233
+ snapshot: JSON.stringify(snapshot),
2234
+ createdAt: existingRecord.data?.createdAt ?? now.toISOString(),
2235
+ updatedAt: now.toISOString(),
2236
+ ...getTtlProps("workflow_snapshot", this.ttlConfig)
2237
+ };
2238
+ if (previousUpdatedAt) {
2239
+ await this.service.entities.workflow_snapshot.upsert(data).where((attr, op) => op.eq(attr.updatedAt, previousUpdatedAt)).go();
2240
+ } else {
2241
+ await this.service.entities.workflow_snapshot.create(data).where((attr, op) => op.notExists(attr.run_id)).go();
2242
+ }
2243
+ return snapshot.context;
2244
+ } catch (error) {
2245
+ if (this.isConditionalCheckFailed(error)) {
2246
+ if (attempt < MAX_RETRIES - 1) {
2247
+ this.logger.debug(
2248
+ `Optimistic locking conflict in updateWorkflowResults, retrying (attempt ${attempt + 1})`
2249
+ );
2250
+ await this.delay(attempt);
2251
+ continue;
2252
+ }
2253
+ }
2254
+ if (error instanceof MastraError) throw error;
2255
+ throw new MastraError(
2256
+ {
2257
+ id: createStorageErrorId("DYNAMODB", "UPDATE_WORKFLOW_RESULTS", "FAILED"),
2258
+ domain: ErrorDomain.STORAGE,
2259
+ category: ErrorCategory.THIRD_PARTY,
2260
+ details: { workflowName, runId, stepId }
2261
+ },
2262
+ error
2263
+ );
2264
+ }
2265
+ }
2266
+ throw new MastraError(
2267
+ {
2268
+ id: createStorageErrorId("DYNAMODB", "UPDATE_WORKFLOW_RESULTS", "MAX_RETRIES_EXCEEDED"),
2269
+ domain: ErrorDomain.STORAGE,
2270
+ category: ErrorCategory.THIRD_PARTY,
2271
+ details: { workflowName, runId, stepId }
2272
+ },
2273
+ new Error("Max retries exceeded for optimistic locking")
2274
+ );
2677
2275
  }
2678
- updateWorkflowState({
2679
- // workflowName,
2680
- // runId,
2681
- // opts,
2276
+ async updateWorkflowState({
2277
+ workflowName,
2278
+ runId,
2279
+ opts
2682
2280
  }) {
2683
- throw new Error("Method not implemented.");
2281
+ for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
2282
+ try {
2283
+ const existingRecord = await this.service.entities.workflow_snapshot.get({
2284
+ entity: "workflow_snapshot",
2285
+ workflow_name: workflowName,
2286
+ run_id: runId
2287
+ }).go();
2288
+ if (!existingRecord.data) {
2289
+ return void 0;
2290
+ }
2291
+ const existingSnapshot = existingRecord.data.snapshot;
2292
+ if (!existingSnapshot || !existingSnapshot.context) {
2293
+ return void 0;
2294
+ }
2295
+ const previousUpdatedAt = existingRecord.data.updatedAt;
2296
+ const updatedSnapshot = { ...existingSnapshot, ...opts };
2297
+ const now = /* @__PURE__ */ new Date();
2298
+ const data = {
2299
+ entity: "workflow_snapshot",
2300
+ workflow_name: workflowName,
2301
+ run_id: runId,
2302
+ snapshot: JSON.stringify(updatedSnapshot),
2303
+ createdAt: existingRecord.data.createdAt,
2304
+ updatedAt: now.toISOString(),
2305
+ resourceId: existingRecord.data.resourceId,
2306
+ ...getTtlProps("workflow_snapshot", this.ttlConfig)
2307
+ };
2308
+ await this.service.entities.workflow_snapshot.upsert(data).where((attr, op) => op.eq(attr.updatedAt, previousUpdatedAt)).go();
2309
+ return updatedSnapshot;
2310
+ } catch (error) {
2311
+ if (this.isConditionalCheckFailed(error)) {
2312
+ if (attempt < MAX_RETRIES - 1) {
2313
+ this.logger.debug(`Optimistic locking conflict in updateWorkflowState, retrying (attempt ${attempt + 1})`);
2314
+ await this.delay(attempt);
2315
+ continue;
2316
+ }
2317
+ }
2318
+ if (error instanceof MastraError) throw error;
2319
+ throw new MastraError(
2320
+ {
2321
+ id: createStorageErrorId("DYNAMODB", "UPDATE_WORKFLOW_STATE", "FAILED"),
2322
+ domain: ErrorDomain.STORAGE,
2323
+ category: ErrorCategory.THIRD_PARTY,
2324
+ details: { workflowName, runId }
2325
+ },
2326
+ error
2327
+ );
2328
+ }
2329
+ }
2330
+ throw new MastraError(
2331
+ {
2332
+ id: createStorageErrorId("DYNAMODB", "UPDATE_WORKFLOW_STATE", "MAX_RETRIES_EXCEEDED"),
2333
+ domain: ErrorDomain.STORAGE,
2334
+ category: ErrorCategory.THIRD_PARTY,
2335
+ details: { workflowName, runId }
2336
+ },
2337
+ new Error("Max retries exceeded for optimistic locking")
2338
+ );
2684
2339
  }
2685
2340
  // Workflow operations
2686
2341
  async persistWorkflowSnapshot({
2687
2342
  workflowName,
2688
2343
  runId,
2689
2344
  resourceId,
2690
- snapshot
2345
+ snapshot,
2346
+ createdAt,
2347
+ updatedAt
2691
2348
  }) {
2692
2349
  this.logger.debug("Persisting workflow snapshot", { workflowName, runId });
2693
2350
  try {
2694
- const now = (/* @__PURE__ */ new Date()).toISOString();
2351
+ const now = /* @__PURE__ */ new Date();
2695
2352
  const data = {
2696
2353
  entity: "workflow_snapshot",
2697
- // Add entity type
2698
2354
  workflow_name: workflowName,
2699
2355
  run_id: runId,
2700
2356
  snapshot: JSON.stringify(snapshot),
2701
- // Stringify the snapshot object
2702
- createdAt: now,
2703
- updatedAt: now,
2704
- resourceId
2357
+ createdAt: (createdAt ?? now).toISOString(),
2358
+ updatedAt: (updatedAt ?? now).toISOString(),
2359
+ resourceId,
2360
+ ...getTtlProps("workflow_snapshot", this.ttlConfig)
2705
2361
  };
2706
2362
  await this.service.entities.workflow_snapshot.upsert(data).go();
2707
2363
  } catch (error) {
2708
2364
  throw new MastraError(
2709
2365
  {
2710
- id: "STORAGE_DYNAMODB_STORE_PERSIST_WORKFLOW_SNAPSHOT_FAILED",
2366
+ id: createStorageErrorId("DYNAMODB", "PERSIST_WORKFLOW_SNAPSHOT", "FAILED"),
2711
2367
  domain: ErrorDomain.STORAGE,
2712
2368
  category: ErrorCategory.THIRD_PARTY,
2713
2369
  details: { workflowName, runId }
@@ -2735,7 +2391,7 @@ var WorkflowStorageDynamoDB = class extends WorkflowsStorage {
2735
2391
  } catch (error) {
2736
2392
  throw new MastraError(
2737
2393
  {
2738
- id: "STORAGE_DYNAMODB_STORE_LOAD_WORKFLOW_SNAPSHOT_FAILED",
2394
+ id: createStorageErrorId("DYNAMODB", "LOAD_WORKFLOW_SNAPSHOT", "FAILED"),
2739
2395
  domain: ErrorDomain.STORAGE,
2740
2396
  category: ErrorCategory.THIRD_PARTY,
2741
2397
  details: { workflowName, runId }
@@ -2744,11 +2400,24 @@ var WorkflowStorageDynamoDB = class extends WorkflowsStorage {
2744
2400
  );
2745
2401
  }
2746
2402
  }
2747
- async getWorkflowRuns(args) {
2403
+ async listWorkflowRuns(args) {
2748
2404
  this.logger.debug("Getting workflow runs", { args });
2749
2405
  try {
2750
- const limit = args?.limit || 10;
2751
- const offset = args?.offset || 0;
2406
+ const perPage = args?.perPage !== void 0 ? args.perPage : 10;
2407
+ const page = args?.page !== void 0 ? args.page : 0;
2408
+ if (page < 0) {
2409
+ throw new MastraError(
2410
+ {
2411
+ id: createStorageErrorId("DYNAMODB", "LIST_WORKFLOW_RUNS", "INVALID_PAGE"),
2412
+ domain: ErrorDomain.STORAGE,
2413
+ category: ErrorCategory.USER,
2414
+ details: { page }
2415
+ },
2416
+ new Error("page must be >= 0")
2417
+ );
2418
+ }
2419
+ const normalizedPerPage = normalizePerPage(perPage, 10);
2420
+ const offset = page * normalizedPerPage;
2752
2421
  let query;
2753
2422
  if (args?.workflowName) {
2754
2423
  query = this.service.entities.workflow_snapshot.query.primary({
@@ -2770,6 +2439,11 @@ var WorkflowStorageDynamoDB = class extends WorkflowsStorage {
2770
2439
  });
2771
2440
  if (pageResults.data && pageResults.data.length > 0) {
2772
2441
  let pageFilteredData = pageResults.data;
2442
+ if (args?.status) {
2443
+ pageFilteredData = pageFilteredData.filter((snapshot) => {
2444
+ return snapshot.snapshot.status === args.status;
2445
+ });
2446
+ }
2773
2447
  if (args?.fromDate || args?.toDate) {
2774
2448
  pageFilteredData = pageFilteredData.filter((snapshot) => {
2775
2449
  const createdAt = new Date(snapshot.createdAt);
@@ -2795,7 +2469,7 @@ var WorkflowStorageDynamoDB = class extends WorkflowsStorage {
2795
2469
  return { runs: [], total: 0 };
2796
2470
  }
2797
2471
  const total = allMatchingSnapshots.length;
2798
- const paginatedData = allMatchingSnapshots.slice(offset, offset + limit);
2472
+ const paginatedData = allMatchingSnapshots.slice(offset, offset + normalizedPerPage);
2799
2473
  const runs = paginatedData.map((snapshot) => formatWorkflowRun(snapshot));
2800
2474
  return {
2801
2475
  runs,
@@ -2804,7 +2478,7 @@ var WorkflowStorageDynamoDB = class extends WorkflowsStorage {
2804
2478
  } catch (error) {
2805
2479
  throw new MastraError(
2806
2480
  {
2807
- id: "STORAGE_DYNAMODB_STORE_GET_WORKFLOW_RUNS_FAILED",
2481
+ id: createStorageErrorId("DYNAMODB", "LIST_WORKFLOW_RUNS", "FAILED"),
2808
2482
  domain: ErrorDomain.STORAGE,
2809
2483
  category: ErrorCategory.THIRD_PARTY,
2810
2484
  details: { workflowName: args?.workflowName || "", resourceId: args?.resourceId || "" }
@@ -2858,7 +2532,7 @@ var WorkflowStorageDynamoDB = class extends WorkflowsStorage {
2858
2532
  } catch (error) {
2859
2533
  throw new MastraError(
2860
2534
  {
2861
- id: "STORAGE_DYNAMODB_STORE_GET_WORKFLOW_RUN_BY_ID_FAILED",
2535
+ id: createStorageErrorId("DYNAMODB", "GET_WORKFLOW_RUN_BY_ID", "FAILED"),
2862
2536
  domain: ErrorDomain.STORAGE,
2863
2537
  category: ErrorCategory.THIRD_PARTY,
2864
2538
  details: { runId, workflowName: args?.workflowName || "" }
@@ -2867,17 +2541,41 @@ var WorkflowStorageDynamoDB = class extends WorkflowsStorage {
2867
2541
  );
2868
2542
  }
2869
2543
  }
2544
+ async deleteWorkflowRunById({ runId, workflowName }) {
2545
+ this.logger.debug("Deleting workflow run by ID", { runId, workflowName });
2546
+ try {
2547
+ await this.service.entities.workflow_snapshot.delete({
2548
+ entity: "workflow_snapshot",
2549
+ workflow_name: workflowName,
2550
+ run_id: runId
2551
+ }).go();
2552
+ } catch (error) {
2553
+ throw new MastraError(
2554
+ {
2555
+ id: createStorageErrorId("DYNAMODB", "DELETE_WORKFLOW_RUN_BY_ID", "FAILED"),
2556
+ domain: ErrorDomain.STORAGE,
2557
+ category: ErrorCategory.THIRD_PARTY,
2558
+ details: { runId, workflowName }
2559
+ },
2560
+ error
2561
+ );
2562
+ }
2563
+ }
2870
2564
  };
2871
2565
 
2872
2566
  // src/storage/index.ts
2873
- var DynamoDBStore = class extends MastraStorage {
2567
+ var isClientConfig = (config) => {
2568
+ return "client" in config;
2569
+ };
2570
+ var DynamoDBStore = class extends MastraCompositeStore {
2874
2571
  tableName;
2875
2572
  client;
2876
2573
  service;
2574
+ ttlConfig;
2877
2575
  hasInitialized = null;
2878
2576
  stores;
2879
2577
  constructor({ name, config }) {
2880
- super({ name });
2578
+ super({ id: config.id, name, disableInit: config.disableInit });
2881
2579
  try {
2882
2580
  if (!config.tableName || typeof config.tableName !== "string" || config.tableName.trim() === "") {
2883
2581
  throw new Error("DynamoDBStore: config.tableName must be provided and cannot be empty.");
@@ -2887,27 +2585,24 @@ var DynamoDBStore = class extends MastraStorage {
2887
2585
  `DynamoDBStore: config.tableName "${config.tableName}" contains invalid characters or is not between 3 and 255 characters long.`
2888
2586
  );
2889
2587
  }
2890
- const dynamoClient = new DynamoDBClient({
2891
- region: config.region || "us-east-1",
2892
- endpoint: config.endpoint,
2893
- credentials: config.credentials
2894
- });
2895
2588
  this.tableName = config.tableName;
2896
- this.client = DynamoDBDocumentClient.from(dynamoClient);
2589
+ this.ttlConfig = config.ttl;
2590
+ if (isClientConfig(config)) {
2591
+ this.client = config.client;
2592
+ } else {
2593
+ const dynamoClient = new DynamoDBClient({
2594
+ region: config.region || "us-east-1",
2595
+ endpoint: config.endpoint,
2596
+ credentials: config.credentials
2597
+ });
2598
+ this.client = DynamoDBDocumentClient.from(dynamoClient);
2599
+ }
2897
2600
  this.service = getElectroDbService(this.client, this.tableName);
2898
- const operations = new StoreOperationsDynamoDB({
2899
- service: this.service,
2900
- tableName: this.tableName,
2901
- client: this.client
2902
- });
2903
- const traces = new TracesStorageDynamoDB({ service: this.service, operations });
2904
- const workflows = new WorkflowStorageDynamoDB({ service: this.service });
2905
- const memory = new MemoryStorageDynamoDB({ service: this.service });
2906
- const scores = new ScoresStorageDynamoDB({ service: this.service });
2601
+ const domainConfig = { service: this.service, ttl: this.ttlConfig };
2602
+ const workflows = new WorkflowStorageDynamoDB(domainConfig);
2603
+ const memory = new MemoryStorageDynamoDB(domainConfig);
2604
+ const scores = new ScoresStorageDynamoDB(domainConfig);
2907
2605
  this.stores = {
2908
- operations,
2909
- legacyEvals: new LegacyEvalsDynamoDB({ service: this.service, tableName: this.tableName }),
2910
- traces,
2911
2606
  workflows,
2912
2607
  memory,
2913
2608
  scores
@@ -2915,7 +2610,7 @@ var DynamoDBStore = class extends MastraStorage {
2915
2610
  } catch (error) {
2916
2611
  throw new MastraError(
2917
2612
  {
2918
- id: "STORAGE_DYNAMODB_STORE_CONSTRUCTOR_FAILED",
2613
+ id: createStorageErrorId("DYNAMODB", "CONSTRUCTOR", "FAILED"),
2919
2614
  domain: ErrorDomain.STORAGE,
2920
2615
  category: ErrorCategory.USER
2921
2616
  },
@@ -2923,16 +2618,6 @@ var DynamoDBStore = class extends MastraStorage {
2923
2618
  );
2924
2619
  }
2925
2620
  }
2926
- get supports() {
2927
- return {
2928
- selectByIncludeResourceScope: true,
2929
- resourceWorkingMemory: true,
2930
- hasColumn: false,
2931
- createTable: false,
2932
- deleteMessages: false,
2933
- getScoresBySpan: true
2934
- };
2935
- }
2936
2621
  /**
2937
2622
  * Validates that the required DynamoDB table exists and is accessible.
2938
2623
  * This does not check the table structure - it assumes the table
@@ -2951,7 +2636,7 @@ var DynamoDBStore = class extends MastraStorage {
2951
2636
  }
2952
2637
  throw new MastraError(
2953
2638
  {
2954
- id: "STORAGE_DYNAMODB_STORE_VALIDATE_TABLE_EXISTS_FAILED",
2639
+ id: createStorageErrorId("DYNAMODB", "VALIDATE_TABLE_EXISTS", "FAILED"),
2955
2640
  domain: ErrorDomain.STORAGE,
2956
2641
  category: ErrorCategory.THIRD_PARTY,
2957
2642
  details: { tableName: this.tableName }
@@ -2974,7 +2659,7 @@ var DynamoDBStore = class extends MastraStorage {
2974
2659
  } catch (error) {
2975
2660
  throw new MastraError(
2976
2661
  {
2977
- id: "STORAGE_DYNAMODB_STORE_INIT_FAILED",
2662
+ id: createStorageErrorId("DYNAMODB", "INIT", "FAILED"),
2978
2663
  domain: ErrorDomain.STORAGE,
2979
2664
  category: ErrorCategory.THIRD_PARTY,
2980
2665
  details: { tableName: this.tableName }
@@ -3000,143 +2685,10 @@ var DynamoDBStore = class extends MastraStorage {
3000
2685
  throw err;
3001
2686
  });
3002
2687
  }
3003
- async createTable({ tableName, schema }) {
3004
- return this.stores.operations.createTable({ tableName, schema });
3005
- }
3006
- async alterTable(_args) {
3007
- return this.stores.operations.alterTable(_args);
3008
- }
3009
- async clearTable({ tableName }) {
3010
- return this.stores.operations.clearTable({ tableName });
3011
- }
3012
- async dropTable({ tableName }) {
3013
- return this.stores.operations.dropTable({ tableName });
3014
- }
3015
- async insert({ tableName, record }) {
3016
- return this.stores.operations.insert({ tableName, record });
3017
- }
3018
- async batchInsert({ tableName, records }) {
3019
- return this.stores.operations.batchInsert({ tableName, records });
3020
- }
3021
- async load({ tableName, keys }) {
3022
- return this.stores.operations.load({ tableName, keys });
3023
- }
3024
- // Thread operations
3025
- async getThreadById({ threadId }) {
3026
- return this.stores.memory.getThreadById({ threadId });
3027
- }
3028
- async getThreadsByResourceId(args) {
3029
- return this.stores.memory.getThreadsByResourceId(args);
3030
- }
3031
- async saveThread({ thread }) {
3032
- return this.stores.memory.saveThread({ thread });
3033
- }
3034
- async updateThread({
3035
- id,
3036
- title,
3037
- metadata
3038
- }) {
3039
- return this.stores.memory.updateThread({ id, title, metadata });
3040
- }
3041
- async deleteThread({ threadId }) {
3042
- return this.stores.memory.deleteThread({ threadId });
3043
- }
3044
- async getMessages({
3045
- threadId,
3046
- resourceId,
3047
- selectBy,
3048
- format
3049
- }) {
3050
- return this.stores.memory.getMessages({ threadId, resourceId, selectBy, format });
3051
- }
3052
- async getMessagesById({
3053
- messageIds,
3054
- format
3055
- }) {
3056
- return this.stores.memory.getMessagesById({ messageIds, format });
3057
- }
3058
- async saveMessages(args) {
3059
- return this.stores.memory.saveMessages(args);
3060
- }
3061
- async getThreadsByResourceIdPaginated(args) {
3062
- return this.stores.memory.getThreadsByResourceIdPaginated(args);
3063
- }
3064
- async getMessagesPaginated(args) {
3065
- return this.stores.memory.getMessagesPaginated(args);
3066
- }
3067
- async updateMessages(_args) {
3068
- return this.stores.memory.updateMessages(_args);
3069
- }
3070
- // Trace operations
3071
- async getTraces(args) {
3072
- return this.stores.traces.getTraces(args);
3073
- }
3074
- async batchTraceInsert({ records }) {
3075
- return this.stores.traces.batchTraceInsert({ records });
3076
- }
3077
- async getTracesPaginated(_args) {
3078
- return this.stores.traces.getTracesPaginated(_args);
3079
- }
3080
- // Workflow operations
3081
- async updateWorkflowResults({
3082
- workflowName,
3083
- runId,
3084
- stepId,
3085
- result,
3086
- runtimeContext
3087
- }) {
3088
- return this.stores.workflows.updateWorkflowResults({ workflowName, runId, stepId, result, runtimeContext });
3089
- }
3090
- async updateWorkflowState({
3091
- workflowName,
3092
- runId,
3093
- opts
3094
- }) {
3095
- return this.stores.workflows.updateWorkflowState({ workflowName, runId, opts });
3096
- }
3097
- async persistWorkflowSnapshot({
3098
- workflowName,
3099
- runId,
3100
- resourceId,
3101
- snapshot
3102
- }) {
3103
- return this.stores.workflows.persistWorkflowSnapshot({ workflowName, runId, resourceId, snapshot });
3104
- }
3105
- async loadWorkflowSnapshot({
3106
- workflowName,
3107
- runId
3108
- }) {
3109
- return this.stores.workflows.loadWorkflowSnapshot({ workflowName, runId });
3110
- }
3111
- async getWorkflowRuns(args) {
3112
- return this.stores.workflows.getWorkflowRuns(args);
3113
- }
3114
- async getWorkflowRunById(args) {
3115
- return this.stores.workflows.getWorkflowRunById(args);
3116
- }
3117
- async getResourceById({ resourceId }) {
3118
- return this.stores.memory.getResourceById({ resourceId });
3119
- }
3120
- async saveResource({ resource }) {
3121
- return this.stores.memory.saveResource({ resource });
3122
- }
3123
- async updateResource({
3124
- resourceId,
3125
- workingMemory,
3126
- metadata
3127
- }) {
3128
- return this.stores.memory.updateResource({ resourceId, workingMemory, metadata });
3129
- }
3130
- // Eval operations
3131
- async getEvalsByAgentName(agentName, type) {
3132
- return this.stores.legacyEvals.getEvalsByAgentName(agentName, type);
3133
- }
3134
- async getEvals(options) {
3135
- return this.stores.legacyEvals.getEvals(options);
3136
- }
3137
2688
  /**
3138
2689
  * Closes the DynamoDB client connection and cleans up resources.
3139
- * Should be called when the store is no longer needed, e.g., at the end of tests or application shutdown.
2690
+ *
2691
+ * This will close the DynamoDB client, including pre-configured clients.
3140
2692
  */
3141
2693
  async close() {
3142
2694
  this.logger.debug("Closing DynamoDB client for store:", { name: this.name });
@@ -3146,7 +2698,7 @@ var DynamoDBStore = class extends MastraStorage {
3146
2698
  } catch (error) {
3147
2699
  throw new MastraError(
3148
2700
  {
3149
- id: "STORAGE_DYNAMODB_STORE_CLOSE_FAILED",
2701
+ id: createStorageErrorId("DYNAMODB", "CLOSE", "FAILED"),
3150
2702
  domain: ErrorDomain.STORAGE,
3151
2703
  category: ErrorCategory.THIRD_PARTY
3152
2704
  },
@@ -3154,50 +2706,8 @@ var DynamoDBStore = class extends MastraStorage {
3154
2706
  );
3155
2707
  }
3156
2708
  }
3157
- /**
3158
- * SCORERS - Not implemented
3159
- */
3160
- async getScoreById({ id: _id }) {
3161
- return this.stores.scores.getScoreById({ id: _id });
3162
- }
3163
- async saveScore(_score) {
3164
- return this.stores.scores.saveScore(_score);
3165
- }
3166
- async getScoresByRunId({
3167
- runId: _runId,
3168
- pagination: _pagination
3169
- }) {
3170
- return this.stores.scores.getScoresByRunId({ runId: _runId, pagination: _pagination });
3171
- }
3172
- async getScoresByEntityId({
3173
- entityId: _entityId,
3174
- entityType: _entityType,
3175
- pagination: _pagination
3176
- }) {
3177
- return this.stores.scores.getScoresByEntityId({
3178
- entityId: _entityId,
3179
- entityType: _entityType,
3180
- pagination: _pagination
3181
- });
3182
- }
3183
- async getScoresByScorerId({
3184
- scorerId,
3185
- source,
3186
- entityId,
3187
- entityType,
3188
- pagination
3189
- }) {
3190
- return this.stores.scores.getScoresByScorerId({ scorerId, source, entityId, entityType, pagination });
3191
- }
3192
- async getScoresBySpan({
3193
- traceId,
3194
- spanId,
3195
- pagination
3196
- }) {
3197
- return this.stores.scores.getScoresBySpan({ traceId, spanId, pagination });
3198
- }
3199
2709
  };
3200
2710
 
3201
- export { DynamoDBStore };
2711
+ export { DynamoDBStore, MemoryStorageDynamoDB, ScoresStorageDynamoDB, WorkflowStorageDynamoDB, calculateTtl, getTtlAttributeName, getTtlProps, isTtlEnabled };
3202
2712
  //# sourceMappingURL=index.js.map
3203
2713
  //# sourceMappingURL=index.js.map