@mastra/dynamodb 0.0.0-pass-headers-for-create-mastra-client-20250529200245 → 0.0.0-share-agent-metadata-with-cloud-20250718110128

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,10 @@
1
1
  import { DynamoDBClient, DescribeTableCommand } from '@aws-sdk/client-dynamodb';
2
2
  import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb';
3
- import type { StorageThreadType, WorkflowRunState, MastraMessageV1 } from '@mastra/core';
4
- import type { MastraMessageV2 } from '@mastra/core/agent';
3
+ import type { MastraMessageContentV2 } from '@mastra/core/agent';
5
4
  import { MessageList } from '@mastra/core/agent';
5
+ import { ErrorCategory, ErrorDomain, MastraError } from '@mastra/core/error';
6
+ import type { StorageThreadType, MastraMessageV2, MastraMessageV1 } from '@mastra/core/memory';
7
+
6
8
  import {
7
9
  MastraStorage,
8
10
  TABLE_THREADS,
@@ -11,7 +13,19 @@ import {
11
13
  TABLE_EVALS,
12
14
  TABLE_TRACES,
13
15
  } from '@mastra/core/storage';
14
- import type { EvalRow, StorageGetMessagesArg, WorkflowRun, WorkflowRuns, TABLE_NAMES } from '@mastra/core/storage';
16
+ import type {
17
+ EvalRow,
18
+ StorageGetMessagesArg,
19
+ WorkflowRun,
20
+ WorkflowRuns,
21
+ TABLE_NAMES,
22
+ StorageGetTracesArg,
23
+ PaginationInfo,
24
+ StorageColumn,
25
+ TABLE_RESOURCES,
26
+ } from '@mastra/core/storage';
27
+ import type { Trace } from '@mastra/core/telemetry';
28
+ import type { WorkflowRunState } from '@mastra/core/workflows';
15
29
  import type { Service } from 'electrodb';
16
30
  import { getElectroDbService } from '../entities';
17
31
 
@@ -25,6 +39,8 @@ export interface DynamoDBStoreConfig {
25
39
  };
26
40
  }
27
41
 
42
+ type SUPPORTED_TABLE_NAMES = Exclude<TABLE_NAMES, typeof TABLE_RESOURCES>;
43
+
28
44
  // Define a type for our service that allows string indexing
29
45
  type MastraService = Service<Record<string, any>> & {
30
46
  [key: string]: any;
@@ -51,25 +67,36 @@ export class DynamoDBStore extends MastraStorage {
51
67
  super({ name });
52
68
 
53
69
  // Validate required config
54
- if (!config.tableName || typeof config.tableName !== 'string' || config.tableName.trim() === '') {
55
- throw new Error('DynamoDBStore: config.tableName must be provided and cannot be empty.');
56
- }
57
- // Validate tableName characters (basic check)
58
- if (!/^[a-zA-Z0-9_.-]{3,255}$/.test(config.tableName)) {
59
- throw new Error(
60
- `DynamoDBStore: config.tableName "${config.tableName}" contains invalid characters or is not between 3 and 255 characters long.`,
61
- );
62
- }
70
+ try {
71
+ if (!config.tableName || typeof config.tableName !== 'string' || config.tableName.trim() === '') {
72
+ throw new Error('DynamoDBStore: config.tableName must be provided and cannot be empty.');
73
+ }
74
+ // Validate tableName characters (basic check)
75
+ if (!/^[a-zA-Z0-9_.-]{3,255}$/.test(config.tableName)) {
76
+ throw new Error(
77
+ `DynamoDBStore: config.tableName "${config.tableName}" contains invalid characters or is not between 3 and 255 characters long.`,
78
+ );
79
+ }
63
80
 
64
- const dynamoClient = new DynamoDBClient({
65
- region: config.region || 'us-east-1',
66
- endpoint: config.endpoint,
67
- credentials: config.credentials,
68
- });
81
+ const dynamoClient = new DynamoDBClient({
82
+ region: config.region || 'us-east-1',
83
+ endpoint: config.endpoint,
84
+ credentials: config.credentials,
85
+ });
69
86
 
70
- this.tableName = config.tableName;
71
- this.client = DynamoDBDocumentClient.from(dynamoClient);
72
- this.service = getElectroDbService(this.client, this.tableName) as MastraService;
87
+ this.tableName = config.tableName;
88
+ this.client = DynamoDBDocumentClient.from(dynamoClient);
89
+ this.service = getElectroDbService(this.client, this.tableName) as MastraService;
90
+ } catch (error) {
91
+ throw new MastraError(
92
+ {
93
+ id: 'STORAGE_DYNAMODB_STORE_CONSTRUCTOR_FAILED',
94
+ domain: ErrorDomain.STORAGE,
95
+ category: ErrorCategory.USER,
96
+ },
97
+ error,
98
+ );
99
+ }
73
100
 
74
101
  // We're using a single table design with ElectroDB,
75
102
  // so we don't need to create multiple tables
@@ -101,7 +128,15 @@ export class DynamoDBStore extends MastraStorage {
101
128
  this.logger.debug(`Table ${this.tableName} exists and is accessible`);
102
129
  } catch (error) {
103
130
  this.logger.error('Error validating table access', { tableName: this.tableName, error });
104
- throw error;
131
+ throw new MastraError(
132
+ {
133
+ id: 'STORAGE_DYNAMODB_STORE_VALIDATE_TABLE_ACCESS_FAILED',
134
+ domain: ErrorDomain.STORAGE,
135
+ category: ErrorCategory.THIRD_PARTY,
136
+ details: { tableName: this.tableName },
137
+ },
138
+ error,
139
+ );
105
140
  }
106
141
  }
107
142
 
@@ -127,7 +162,15 @@ export class DynamoDBStore extends MastraStorage {
127
162
  }
128
163
 
129
164
  // For other errors (like permissions issues), we should throw
130
- throw error;
165
+ throw new MastraError(
166
+ {
167
+ id: 'STORAGE_DYNAMODB_STORE_VALIDATE_TABLE_EXISTS_FAILED',
168
+ domain: ErrorDomain.STORAGE,
169
+ category: ErrorCategory.THIRD_PARTY,
170
+ details: { tableName: this.tableName },
171
+ },
172
+ error,
173
+ );
131
174
  }
132
175
  }
133
176
 
@@ -154,7 +197,15 @@ export class DynamoDBStore extends MastraStorage {
154
197
  // The error has already been handled by _performInitializationAndStore
155
198
  // (i.e., this.hasInitialized was reset). Re-throwing here ensures
156
199
  // the caller of init() is aware of the failure.
157
- throw error;
200
+ throw new MastraError(
201
+ {
202
+ id: 'STORAGE_DYNAMODB_STORE_INIT_FAILED',
203
+ domain: ErrorDomain.STORAGE,
204
+ category: ErrorCategory.THIRD_PARTY,
205
+ details: { tableName: this.tableName },
206
+ },
207
+ error,
208
+ );
158
209
  }
159
210
  }
160
211
 
@@ -181,15 +232,52 @@ export class DynamoDBStore extends MastraStorage {
181
232
  });
182
233
  }
183
234
 
235
+ /**
236
+ * Pre-processes a record to ensure Date objects are converted to ISO strings
237
+ * This is necessary because ElectroDB validation happens before setters are applied
238
+ */
239
+ private preprocessRecord(record: Record<string, any>): Record<string, any> {
240
+ const processed = { ...record };
241
+
242
+ // Convert Date objects to ISO strings for date fields
243
+ // This prevents ElectroDB validation errors that occur when Date objects are passed
244
+ // to string-typed attributes, even when the attribute has a setter that converts dates
245
+ if (processed.createdAt instanceof Date) {
246
+ processed.createdAt = processed.createdAt.toISOString();
247
+ }
248
+ if (processed.updatedAt instanceof Date) {
249
+ processed.updatedAt = processed.updatedAt.toISOString();
250
+ }
251
+ if (processed.created_at instanceof Date) {
252
+ processed.created_at = processed.created_at.toISOString();
253
+ }
254
+
255
+ return processed;
256
+ }
257
+
258
+ async alterTable(_args: {
259
+ tableName: TABLE_NAMES;
260
+ schema: Record<string, StorageColumn>;
261
+ ifNotExists: string[];
262
+ }): Promise<void> {
263
+ // Nothing to do here, DynamoDB has a flexible schema and handles new attributes automatically upon insertion/update.
264
+ }
265
+
184
266
  /**
185
267
  * Clear all items from a logical "table" (entity type)
186
268
  */
187
- async clearTable({ tableName }: { tableName: TABLE_NAMES }): Promise<void> {
269
+ async clearTable({ tableName }: { tableName: SUPPORTED_TABLE_NAMES }): Promise<void> {
188
270
  this.logger.debug('DynamoDB clearTable called', { tableName });
189
271
 
190
272
  const entityName = this.getEntityNameForTable(tableName);
191
273
  if (!entityName || !this.service.entities[entityName]) {
192
- throw new Error(`No entity defined for ${tableName}`);
274
+ throw new MastraError({
275
+ id: 'STORAGE_DYNAMODB_STORE_CLEAR_TABLE_INVALID_ARGS',
276
+ domain: ErrorDomain.STORAGE,
277
+ category: ErrorCategory.USER,
278
+ text: 'No entity defined for tableName',
279
+ details: { tableName },
280
+ });
193
281
  }
194
282
 
195
283
  try {
@@ -259,45 +347,83 @@ export class DynamoDBStore extends MastraStorage {
259
347
 
260
348
  this.logger.debug(`Successfully cleared all records for ${tableName}`);
261
349
  } catch (error) {
262
- this.logger.error('Failed to clear table', { tableName, error });
263
- throw error;
350
+ throw new MastraError(
351
+ {
352
+ id: 'STORAGE_DYNAMODB_STORE_CLEAR_TABLE_FAILED',
353
+ domain: ErrorDomain.STORAGE,
354
+ category: ErrorCategory.THIRD_PARTY,
355
+ details: { tableName },
356
+ },
357
+ error,
358
+ );
264
359
  }
265
360
  }
266
361
 
267
362
  /**
268
363
  * Insert a record into the specified "table" (entity)
269
364
  */
270
- async insert({ tableName, record }: { tableName: TABLE_NAMES; record: Record<string, any> }): Promise<void> {
365
+ async insert({
366
+ tableName,
367
+ record,
368
+ }: {
369
+ tableName: SUPPORTED_TABLE_NAMES;
370
+ record: Record<string, any>;
371
+ }): Promise<void> {
271
372
  this.logger.debug('DynamoDB insert called', { tableName });
272
373
 
273
374
  const entityName = this.getEntityNameForTable(tableName);
274
375
  if (!entityName || !this.service.entities[entityName]) {
275
- throw new Error(`No entity defined for ${tableName}`);
376
+ throw new MastraError({
377
+ id: 'STORAGE_DYNAMODB_STORE_INSERT_INVALID_ARGS',
378
+ domain: ErrorDomain.STORAGE,
379
+ category: ErrorCategory.USER,
380
+ text: 'No entity defined for tableName',
381
+ details: { tableName },
382
+ });
276
383
  }
277
384
 
278
385
  try {
279
- // Add the entity type to the record before creating
280
- const dataToSave = { entity: entityName, ...record };
386
+ // Add the entity type to the record and preprocess before creating
387
+ const dataToSave = { entity: entityName, ...this.preprocessRecord(record) };
281
388
  await this.service.entities[entityName].create(dataToSave).go();
282
389
  } catch (error) {
283
- this.logger.error('Failed to insert record', { tableName, error });
284
- throw error;
390
+ throw new MastraError(
391
+ {
392
+ id: 'STORAGE_DYNAMODB_STORE_INSERT_FAILED',
393
+ domain: ErrorDomain.STORAGE,
394
+ category: ErrorCategory.THIRD_PARTY,
395
+ details: { tableName },
396
+ },
397
+ error,
398
+ );
285
399
  }
286
400
  }
287
401
 
288
402
  /**
289
403
  * Insert multiple records as a batch
290
404
  */
291
- async batchInsert({ tableName, records }: { tableName: TABLE_NAMES; records: Record<string, any>[] }): Promise<void> {
405
+ async batchInsert({
406
+ tableName,
407
+ records,
408
+ }: {
409
+ tableName: SUPPORTED_TABLE_NAMES;
410
+ records: Record<string, any>[];
411
+ }): Promise<void> {
292
412
  this.logger.debug('DynamoDB batchInsert called', { tableName, count: records.length });
293
413
 
294
414
  const entityName = this.getEntityNameForTable(tableName);
295
415
  if (!entityName || !this.service.entities[entityName]) {
296
- throw new Error(`No entity defined for ${tableName}`);
416
+ throw new MastraError({
417
+ id: 'STORAGE_DYNAMODB_STORE_BATCH_INSERT_INVALID_ARGS',
418
+ domain: ErrorDomain.STORAGE,
419
+ category: ErrorCategory.USER,
420
+ text: 'No entity defined for tableName',
421
+ details: { tableName },
422
+ });
297
423
  }
298
424
 
299
- // Add entity type to each record
300
- const recordsToSave = records.map(rec => ({ entity: entityName, ...rec }));
425
+ // Add entity type and preprocess each record
426
+ const recordsToSave = records.map(rec => ({ entity: entityName, ...this.preprocessRecord(rec) }));
301
427
 
302
428
  // ElectroDB has batch limits of 25 items, so we need to chunk
303
429
  const batchSize = 25;
@@ -323,20 +449,39 @@ export class DynamoDBStore extends MastraStorage {
323
449
  // Original batch call: await this.service.entities[entityName].create(batch).go();
324
450
  }
325
451
  } catch (error) {
326
- this.logger.error('Failed to batch insert records', { tableName, error });
327
- throw error;
452
+ throw new MastraError(
453
+ {
454
+ id: 'STORAGE_DYNAMODB_STORE_BATCH_INSERT_FAILED',
455
+ domain: ErrorDomain.STORAGE,
456
+ category: ErrorCategory.THIRD_PARTY,
457
+ details: { tableName },
458
+ },
459
+ error,
460
+ );
328
461
  }
329
462
  }
330
463
 
331
464
  /**
332
465
  * Load a record by its keys
333
466
  */
334
- async load<R>({ tableName, keys }: { tableName: TABLE_NAMES; keys: Record<string, string> }): Promise<R | null> {
467
+ async load<R>({
468
+ tableName,
469
+ keys,
470
+ }: {
471
+ tableName: SUPPORTED_TABLE_NAMES;
472
+ keys: Record<string, string>;
473
+ }): Promise<R | null> {
335
474
  this.logger.debug('DynamoDB load called', { tableName, keys });
336
475
 
337
476
  const entityName = this.getEntityNameForTable(tableName);
338
477
  if (!entityName || !this.service.entities[entityName]) {
339
- throw new Error(`No entity defined for ${tableName}`);
478
+ throw new MastraError({
479
+ id: 'STORAGE_DYNAMODB_STORE_LOAD_INVALID_ARGS',
480
+ domain: ErrorDomain.STORAGE,
481
+ category: ErrorCategory.USER,
482
+ text: 'No entity defined for tableName',
483
+ details: { tableName },
484
+ });
340
485
  }
341
486
 
342
487
  try {
@@ -361,8 +506,15 @@ export class DynamoDBStore extends MastraStorage {
361
506
 
362
507
  return data as R;
363
508
  } catch (error) {
364
- this.logger.error('Failed to load record', { tableName, keys, error });
365
- throw error;
509
+ throw new MastraError(
510
+ {
511
+ id: 'STORAGE_DYNAMODB_STORE_LOAD_FAILED',
512
+ domain: ErrorDomain.STORAGE,
513
+ category: ErrorCategory.THIRD_PARTY,
514
+ details: { tableName },
515
+ },
516
+ error,
517
+ );
366
518
  }
367
519
  }
368
520
 
@@ -380,12 +532,22 @@ export class DynamoDBStore extends MastraStorage {
380
532
  const data = result.data;
381
533
  return {
382
534
  ...data,
535
+ // Convert date strings back to Date objects for consistency
536
+ createdAt: typeof data.createdAt === 'string' ? new Date(data.createdAt) : data.createdAt,
537
+ updatedAt: typeof data.updatedAt === 'string' ? new Date(data.updatedAt) : data.updatedAt,
383
538
  // metadata: data.metadata ? JSON.parse(data.metadata) : undefined, // REMOVED by AI
384
539
  // metadata is already transformed by the entity's getter
385
540
  } as StorageThreadType;
386
541
  } catch (error) {
387
- this.logger.error('Failed to get thread by ID', { threadId, error });
388
- throw error;
542
+ throw new MastraError(
543
+ {
544
+ id: 'STORAGE_DYNAMODB_STORE_GET_THREAD_BY_ID_FAILED',
545
+ domain: ErrorDomain.STORAGE,
546
+ category: ErrorCategory.THIRD_PARTY,
547
+ details: { threadId },
548
+ },
549
+ error,
550
+ );
389
551
  }
390
552
  }
391
553
 
@@ -401,12 +563,22 @@ export class DynamoDBStore extends MastraStorage {
401
563
  // ElectroDB handles the transformation with attribute getters
402
564
  return result.data.map((data: any) => ({
403
565
  ...data,
566
+ // Convert date strings back to Date objects for consistency
567
+ createdAt: typeof data.createdAt === 'string' ? new Date(data.createdAt) : data.createdAt,
568
+ updatedAt: typeof data.updatedAt === 'string' ? new Date(data.updatedAt) : data.updatedAt,
404
569
  // metadata: data.metadata ? JSON.parse(data.metadata) : undefined, // REMOVED by AI
405
570
  // metadata is already transformed by the entity's getter
406
571
  })) as StorageThreadType[];
407
572
  } catch (error) {
408
- this.logger.error('Failed to get threads by resource ID', { resourceId, error });
409
- throw error;
573
+ throw new MastraError(
574
+ {
575
+ id: 'STORAGE_DYNAMODB_STORE_GET_THREADS_BY_RESOURCE_ID_FAILED',
576
+ domain: ErrorDomain.STORAGE,
577
+ category: ErrorCategory.THIRD_PARTY,
578
+ details: { resourceId },
579
+ },
580
+ error,
581
+ );
410
582
  }
411
583
  }
412
584
 
@@ -426,7 +598,7 @@ export class DynamoDBStore extends MastraStorage {
426
598
  };
427
599
 
428
600
  try {
429
- await this.service.entities.thread.create(threadData).go();
601
+ await this.service.entities.thread.upsert(threadData).go();
430
602
 
431
603
  return {
432
604
  id: thread.id,
@@ -437,8 +609,15 @@ export class DynamoDBStore extends MastraStorage {
437
609
  metadata: thread.metadata,
438
610
  };
439
611
  } catch (error) {
440
- this.logger.error('Failed to save thread', { threadId: thread.id, error });
441
- throw error;
612
+ throw new MastraError(
613
+ {
614
+ id: 'STORAGE_DYNAMODB_STORE_SAVE_THREAD_FAILED',
615
+ domain: ErrorDomain.STORAGE,
616
+ category: ErrorCategory.THIRD_PARTY,
617
+ details: { threadId: thread.id },
618
+ },
619
+ error,
620
+ );
442
621
  }
443
622
  }
444
623
 
@@ -493,8 +672,15 @@ export class DynamoDBStore extends MastraStorage {
493
672
  updatedAt: now,
494
673
  };
495
674
  } catch (error) {
496
- this.logger.error('Failed to update thread', { threadId: id, error });
497
- throw error;
675
+ throw new MastraError(
676
+ {
677
+ id: 'STORAGE_DYNAMODB_STORE_UPDATE_THREAD_FAILED',
678
+ domain: ErrorDomain.STORAGE,
679
+ category: ErrorCategory.THIRD_PARTY,
680
+ details: { threadId: id },
681
+ },
682
+ error,
683
+ );
498
684
  }
499
685
  }
500
686
 
@@ -510,8 +696,15 @@ export class DynamoDBStore extends MastraStorage {
510
696
  // 2. Delete any vector embeddings related to this thread
511
697
  // These would be additional operations
512
698
  } catch (error) {
513
- this.logger.error('Failed to delete thread', { threadId, error });
514
- throw error;
699
+ throw new MastraError(
700
+ {
701
+ id: 'STORAGE_DYNAMODB_STORE_DELETE_THREAD_FAILED',
702
+ domain: ErrorDomain.STORAGE,
703
+ category: ErrorCategory.THIRD_PARTY,
704
+ details: { threadId },
705
+ },
706
+ error,
707
+ );
515
708
  }
516
709
  }
517
710
 
@@ -531,18 +724,19 @@ export class DynamoDBStore extends MastraStorage {
531
724
  // Provide *all* composite key components for the 'byThread' index ('entity', 'threadId')
532
725
  const query = this.service.entities.message.query.byThread({ entity: 'message', threadId });
533
726
 
727
+ const limit = this.resolveMessageLimit({ last: selectBy?.last, defaultLimit: Number.MAX_SAFE_INTEGER });
534
728
  // Apply the 'last' limit if provided
535
- if (selectBy?.last && typeof selectBy.last === 'number') {
536
- // Use ElectroDB's limit parameter (descending sort assumed on GSI SK)
537
- // Ensure GSI sk (createdAt) is sorted descending for 'last' to work correctly
538
- // Assuming default sort is ascending on SK, use reverse: true for descending
539
- const results = await query.go({ limit: selectBy.last, reverse: true });
729
+ if (limit !== Number.MAX_SAFE_INTEGER) {
730
+ // Use ElectroDB's limit parameter
731
+ // DDB GSIs are sorted in ascending order
732
+ // Use ElectroDB's order parameter to sort in descending order to retrieve 'latest' messages
733
+ const results = await query.go({ limit, order: 'desc' });
540
734
  // Use arrow function in map to preserve 'this' context for parseMessageData
541
735
  const list = new MessageList({ threadId, resourceId }).add(
542
736
  results.data.map((data: any) => this.parseMessageData(data)),
543
737
  'memory',
544
738
  );
545
- if (format === `v2`) return list.get.all.mastra();
739
+ if (format === `v2`) return list.get.all.v2();
546
740
  return list.get.all.v1();
547
741
  }
548
742
 
@@ -553,11 +747,18 @@ export class DynamoDBStore extends MastraStorage {
553
747
  results.data.map((data: any) => this.parseMessageData(data)),
554
748
  'memory',
555
749
  );
556
- if (format === `v2`) return list.get.all.mastra();
750
+ if (format === `v2`) return list.get.all.v2();
557
751
  return list.get.all.v1();
558
752
  } catch (error) {
559
- this.logger.error('Failed to get messages', { threadId, error });
560
- throw error;
753
+ throw new MastraError(
754
+ {
755
+ id: 'STORAGE_DYNAMODB_STORE_GET_MESSAGES_FAILED',
756
+ domain: ErrorDomain.STORAGE,
757
+ category: ErrorCategory.THIRD_PARTY,
758
+ details: { threadId },
759
+ },
760
+ error,
761
+ );
561
762
  }
562
763
  }
563
764
  async saveMessages(args: { messages: MastraMessageV1[]; format?: undefined | 'v1' }): Promise<MastraMessageV1[]>;
@@ -572,6 +773,11 @@ export class DynamoDBStore extends MastraStorage {
572
773
  return [];
573
774
  }
574
775
 
776
+ const threadId = messages[0]?.threadId;
777
+ if (!threadId) {
778
+ throw new Error('Thread ID is required');
779
+ }
780
+
575
781
  // Ensure 'entity' is added and complex fields are handled
576
782
  const messagesToSave = messages.map(msg => {
577
783
  const now = new Date().toISOString();
@@ -602,26 +808,41 @@ export class DynamoDBStore extends MastraStorage {
602
808
  batches.push(batch);
603
809
  }
604
810
 
605
- // Process each batch
606
- for (const batch of batches) {
607
- // Try creating each item individually instead of passing the whole batch
608
- for (const messageData of batch) {
609
- // Ensure each item has the entity property before sending
610
- if (!messageData.entity) {
611
- this.logger.error('Missing entity property in message data for create', { messageData });
612
- throw new Error('Internal error: Missing entity property during saveMessages');
811
+ // Process each batch and update thread's updatedAt in parallel for better performance
812
+ await Promise.all([
813
+ // Process message batches
814
+ ...batches.map(async batch => {
815
+ for (const messageData of batch) {
816
+ // Ensure each item has the entity property before sending
817
+ if (!messageData.entity) {
818
+ this.logger.error('Missing entity property in message data for create', { messageData });
819
+ throw new Error('Internal error: Missing entity property during saveMessages');
820
+ }
821
+ await this.service.entities.message.put(messageData).go();
613
822
  }
614
- await this.service.entities.message.create(messageData).go();
615
- }
616
- // Original batch call: await this.service.entities.message.create(batch).go();
617
- }
823
+ }),
824
+ // Update thread's updatedAt timestamp
825
+ this.service.entities.thread
826
+ .update({ entity: 'thread', id: threadId })
827
+ .set({
828
+ updatedAt: new Date().toISOString(),
829
+ })
830
+ .go(),
831
+ ]);
618
832
 
619
833
  const list = new MessageList().add(messages, 'memory');
620
834
  if (format === `v1`) return list.get.all.v1();
621
- return list.get.all.mastra();
835
+ return list.get.all.v2();
622
836
  } catch (error) {
623
- this.logger.error('Failed to save messages', { error });
624
- throw error;
837
+ throw new MastraError(
838
+ {
839
+ id: 'STORAGE_DYNAMODB_STORE_SAVE_MESSAGES_FAILED',
840
+ domain: ErrorDomain.STORAGE,
841
+ category: ErrorCategory.THIRD_PARTY,
842
+ details: { count: messages.length },
843
+ },
844
+ error,
845
+ );
625
846
  }
626
847
  }
627
848
 
@@ -685,8 +906,14 @@ export class DynamoDBStore extends MastraStorage {
685
906
 
686
907
  return items;
687
908
  } catch (error) {
688
- this.logger.error('Failed to get traces', { error });
689
- throw error;
909
+ throw new MastraError(
910
+ {
911
+ id: 'STORAGE_DYNAMODB_STORE_GET_TRACES_FAILED',
912
+ domain: ErrorDomain.STORAGE,
913
+ category: ErrorCategory.THIRD_PARTY,
914
+ },
915
+ error,
916
+ );
690
917
  }
691
918
  }
692
919
 
@@ -705,8 +932,15 @@ export class DynamoDBStore extends MastraStorage {
705
932
  records: recordsToSave, // Pass records with 'entity' included
706
933
  });
707
934
  } catch (error) {
708
- this.logger.error('Failed to batch insert traces', { error });
709
- throw error;
935
+ throw new MastraError(
936
+ {
937
+ id: 'STORAGE_DYNAMODB_STORE_BATCH_TRACE_INSERT_FAILED',
938
+ domain: ErrorDomain.STORAGE,
939
+ category: ErrorCategory.THIRD_PARTY,
940
+ details: { count: records.length },
941
+ },
942
+ error,
943
+ );
710
944
  }
711
945
  }
712
946
 
@@ -735,11 +969,18 @@ export class DynamoDBStore extends MastraStorage {
735
969
  updatedAt: now,
736
970
  resourceId,
737
971
  };
738
- // Pass the data including 'entity'
739
- await this.service.entities.workflowSnapshot.create(data).go();
972
+ // Use upsert instead of create to handle both create and update cases
973
+ await this.service.entities.workflowSnapshot.upsert(data).go();
740
974
  } catch (error) {
741
- this.logger.error('Failed to persist workflow snapshot', { workflowName, runId, error });
742
- throw error;
975
+ throw new MastraError(
976
+ {
977
+ id: 'STORAGE_DYNAMODB_STORE_PERSIST_WORKFLOW_SNAPSHOT_FAILED',
978
+ domain: ErrorDomain.STORAGE,
979
+ category: ErrorCategory.THIRD_PARTY,
980
+ details: { workflowName, runId },
981
+ },
982
+ error,
983
+ );
743
984
  }
744
985
  }
745
986
 
@@ -770,8 +1011,15 @@ export class DynamoDBStore extends MastraStorage {
770
1011
  // Parse the snapshot string
771
1012
  return result.data.snapshot as WorkflowRunState;
772
1013
  } catch (error) {
773
- this.logger.error('Failed to load workflow snapshot', { workflowName, runId, error });
774
- throw error;
1014
+ throw new MastraError(
1015
+ {
1016
+ id: 'STORAGE_DYNAMODB_STORE_LOAD_WORKFLOW_SNAPSHOT_FAILED',
1017
+ domain: ErrorDomain.STORAGE,
1018
+ category: ErrorCategory.THIRD_PARTY,
1019
+ details: { workflowName, runId },
1020
+ },
1021
+ error,
1022
+ );
775
1023
  }
776
1024
  }
777
1025
 
@@ -861,8 +1109,15 @@ export class DynamoDBStore extends MastraStorage {
861
1109
  total,
862
1110
  };
863
1111
  } catch (error) {
864
- this.logger.error('Failed to get workflow runs', { error });
865
- throw error;
1112
+ throw new MastraError(
1113
+ {
1114
+ id: 'STORAGE_DYNAMODB_STORE_GET_WORKFLOW_RUNS_FAILED',
1115
+ domain: ErrorDomain.STORAGE,
1116
+ category: ErrorCategory.THIRD_PARTY,
1117
+ details: { workflowName: args?.workflowName || '', resourceId: args?.resourceId || '' },
1118
+ },
1119
+ error,
1120
+ );
866
1121
  }
867
1122
  }
868
1123
 
@@ -933,8 +1188,15 @@ export class DynamoDBStore extends MastraStorage {
933
1188
  resourceId: matchingRunDbItem.resourceId,
934
1189
  };
935
1190
  } catch (error) {
936
- this.logger.error('Failed to get workflow run by ID', { runId, workflowName, error });
937
- throw error;
1191
+ throw new MastraError(
1192
+ {
1193
+ id: 'STORAGE_DYNAMODB_STORE_GET_WORKFLOW_RUN_BY_ID_FAILED',
1194
+ domain: ErrorDomain.STORAGE,
1195
+ category: ErrorCategory.THIRD_PARTY,
1196
+ details: { runId, workflowName: args?.workflowName || '' },
1197
+ },
1198
+ error,
1199
+ );
938
1200
  }
939
1201
  }
940
1202
 
@@ -951,8 +1213,8 @@ export class DynamoDBStore extends MastraStorage {
951
1213
  }
952
1214
 
953
1215
  // Helper methods for entity/table mapping
954
- private getEntityNameForTable(tableName: TABLE_NAMES): string | null {
955
- const mapping: Record<TABLE_NAMES, string> = {
1216
+ private getEntityNameForTable(tableName: SUPPORTED_TABLE_NAMES): string | null {
1217
+ const mapping: Record<SUPPORTED_TABLE_NAMES, string> = {
956
1218
  [TABLE_THREADS]: 'thread',
957
1219
  [TABLE_MESSAGES]: 'message',
958
1220
  [TABLE_WORKFLOW_SNAPSHOT]: 'workflowSnapshot',
@@ -1036,11 +1298,57 @@ export class DynamoDBStore extends MastraStorage {
1036
1298
  }
1037
1299
  });
1038
1300
  } catch (error) {
1039
- this.logger.error('Failed to get evals by agent name', { agentName, type, error });
1040
- throw error;
1301
+ throw new MastraError(
1302
+ {
1303
+ id: 'STORAGE_DYNAMODB_STORE_GET_EVALS_BY_AGENT_NAME_FAILED',
1304
+ domain: ErrorDomain.STORAGE,
1305
+ category: ErrorCategory.THIRD_PARTY,
1306
+ details: { agentName },
1307
+ },
1308
+ error,
1309
+ );
1041
1310
  }
1042
1311
  }
1043
1312
 
1313
+ async getTracesPaginated(_args: StorageGetTracesArg): Promise<PaginationInfo & { traces: Trace[] }> {
1314
+ throw new MastraError(
1315
+ {
1316
+ id: 'STORAGE_DYNAMODB_STORE_GET_TRACES_PAGINATED_FAILED',
1317
+ domain: ErrorDomain.STORAGE,
1318
+ category: ErrorCategory.THIRD_PARTY,
1319
+ },
1320
+ new Error('Method not implemented.'),
1321
+ );
1322
+ }
1323
+
1324
+ async getThreadsByResourceIdPaginated(_args: {
1325
+ resourceId: string;
1326
+ page?: number;
1327
+ perPage?: number;
1328
+ }): Promise<PaginationInfo & { threads: StorageThreadType[] }> {
1329
+ throw new MastraError(
1330
+ {
1331
+ id: 'STORAGE_DYNAMODB_STORE_GET_THREADS_BY_RESOURCE_ID_PAGINATED_FAILED',
1332
+ domain: ErrorDomain.STORAGE,
1333
+ category: ErrorCategory.THIRD_PARTY,
1334
+ },
1335
+ new Error('Method not implemented.'),
1336
+ );
1337
+ }
1338
+
1339
+ async getMessagesPaginated(
1340
+ _args: StorageGetMessagesArg,
1341
+ ): Promise<PaginationInfo & { messages: MastraMessageV1[] | MastraMessageV2[] }> {
1342
+ throw new MastraError(
1343
+ {
1344
+ id: 'STORAGE_DYNAMODB_STORE_GET_MESSAGES_PAGINATED_FAILED',
1345
+ domain: ErrorDomain.STORAGE,
1346
+ category: ErrorCategory.THIRD_PARTY,
1347
+ },
1348
+ new Error('Method not implemented.'),
1349
+ );
1350
+ }
1351
+
1044
1352
  /**
1045
1353
  * Closes the DynamoDB client connection and cleans up resources.
1046
1354
  * Should be called when the store is no longer needed, e.g., at the end of tests or application shutdown.
@@ -1051,9 +1359,25 @@ export class DynamoDBStore extends MastraStorage {
1051
1359
  this.client.destroy();
1052
1360
  this.logger.debug('DynamoDB client closed successfully for store:', { name: this.name });
1053
1361
  } catch (error) {
1054
- this.logger.error('Error closing DynamoDB client for store:', { name: this.name, error });
1055
- // Optionally re-throw or handle as appropriate for your application's error handling strategy
1056
- throw error;
1362
+ throw new MastraError(
1363
+ {
1364
+ id: 'STORAGE_DYNAMODB_STORE_CLOSE_FAILED',
1365
+ domain: ErrorDomain.STORAGE,
1366
+ category: ErrorCategory.THIRD_PARTY,
1367
+ },
1368
+ error,
1369
+ );
1057
1370
  }
1058
1371
  }
1372
+
1373
+ async updateMessages(_args: {
1374
+ messages: Partial<Omit<MastraMessageV2, 'createdAt'>> &
1375
+ {
1376
+ id: string;
1377
+ content?: { metadata?: MastraMessageContentV2['metadata']; content?: MastraMessageContentV2['content'] };
1378
+ }[];
1379
+ }): Promise<MastraMessageV2[]> {
1380
+ this.logger.error('updateMessages is not yet implemented in DynamoDBStore');
1381
+ throw new Error('Method not implemented');
1382
+ }
1059
1383
  }