@mastra/dsql 0.0.0 → 1.0.0

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 (37) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/LICENSE.md +30 -0
  3. package/dist/index.cjs +4476 -0
  4. package/dist/index.cjs.map +1 -0
  5. package/dist/index.d.ts +4 -0
  6. package/dist/index.d.ts.map +1 -0
  7. package/dist/index.js +4467 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/shared/batch.d.ts +55 -0
  10. package/dist/shared/batch.d.ts.map +1 -0
  11. package/dist/shared/config.d.ts +129 -0
  12. package/dist/shared/config.d.ts.map +1 -0
  13. package/dist/shared/retry.d.ts +207 -0
  14. package/dist/shared/retry.d.ts.map +1 -0
  15. package/dist/storage/client.d.ts +91 -0
  16. package/dist/storage/client.d.ts.map +1 -0
  17. package/dist/storage/db/index.d.ts +179 -0
  18. package/dist/storage/db/index.d.ts.map +1 -0
  19. package/dist/storage/domains/agents/index.d.ts +47 -0
  20. package/dist/storage/domains/agents/index.d.ts.map +1 -0
  21. package/dist/storage/domains/memory/index.d.ts +80 -0
  22. package/dist/storage/domains/memory/index.d.ts.map +1 -0
  23. package/dist/storage/domains/observability/index.d.ts +37 -0
  24. package/dist/storage/domains/observability/index.d.ts.map +1 -0
  25. package/dist/storage/domains/scores/index.d.ts +53 -0
  26. package/dist/storage/domains/scores/index.d.ts.map +1 -0
  27. package/dist/storage/domains/utils.d.ts +14 -0
  28. package/dist/storage/domains/utils.d.ts.map +1 -0
  29. package/dist/storage/domains/workflows/index.d.ts +61 -0
  30. package/dist/storage/domains/workflows/index.d.ts.map +1 -0
  31. package/dist/storage/index.d.ts +46 -0
  32. package/dist/storage/index.d.ts.map +1 -0
  33. package/dist/storage/performance-indexes/performance-test.d.ts +47 -0
  34. package/dist/storage/performance-indexes/performance-test.d.ts.map +1 -0
  35. package/dist/storage/test-utils.d.ts +14 -0
  36. package/dist/storage/test-utils.d.ts.map +1 -0
  37. package/package.json +17 -17
package/dist/index.cjs ADDED
@@ -0,0 +1,4476 @@
1
+ 'use strict';
2
+
3
+ var auroraDsqlNodePostgresConnector = require('@aws/aurora-dsql-node-postgres-connector');
4
+ var error = require('@mastra/core/error');
5
+ var storage = require('@mastra/core/storage');
6
+ var pg = require('pg');
7
+ var base = require('@mastra/core/base');
8
+ var utils = require('@mastra/core/utils');
9
+ var agent = require('@mastra/core/agent');
10
+ var evals = require('@mastra/core/evals');
11
+
12
+ // src/storage/index.ts
13
+
14
+ // src/shared/config.ts
15
+ var DSQL_POOL_DEFAULTS = {
16
+ /** Maximum connections in the pool */
17
+ max: 10,
18
+ /** Minimum connections in the pool */
19
+ min: 0,
20
+ /** Close idle connections after 10 minutes */
21
+ idleTimeoutMillis: 6e5,
22
+ /** Force connection rotation before DSQL's 60-minute limit */
23
+ maxLifetimeSeconds: 3300,
24
+ /** Connection acquisition timeout */
25
+ connectionTimeoutMillis: 5e3,
26
+ /** Allow process to exit when idle */
27
+ allowExitOnIdle: true
28
+ };
29
+ var isPoolConfig = (cfg) => {
30
+ return "pool" in cfg;
31
+ };
32
+ var isHostConfig = (cfg) => {
33
+ return "host" in cfg;
34
+ };
35
+ var validateConfig = (config) => {
36
+ if (!config.id || typeof config.id !== "string" || config.id.trim() === "") {
37
+ throw new Error("DSQLStore: id must be provided and cannot be empty.");
38
+ }
39
+ if (isPoolConfig(config)) {
40
+ if (!config.pool) {
41
+ throw new Error("DSQLStore: pool must be provided when using pool config.");
42
+ }
43
+ return;
44
+ }
45
+ if (!isHostConfig(config)) {
46
+ throw new Error("DSQLStore: host must be provided and cannot be empty.");
47
+ }
48
+ if (!config.host || config.host.trim() === "") {
49
+ throw new Error("DSQLStore: host must be provided and cannot be empty.");
50
+ }
51
+ if (config.maxLifetimeSeconds !== void 0 && config.maxLifetimeSeconds >= 3600) {
52
+ throw new Error(
53
+ "DSQLStore: maxLifetimeSeconds must be less than 3600 (60 minutes) due to Aurora DSQL connection duration limit."
54
+ );
55
+ }
56
+ };
57
+ var extractRegionFromHost = (host) => {
58
+ const match = host.match(/\.dsql\.([a-z0-9-]+)\.on\.aws$/);
59
+ return match?.[1];
60
+ };
61
+ var getEffectiveRegion = (config) => {
62
+ if (config.region) {
63
+ return config.region;
64
+ }
65
+ const extractedRegion = extractRegionFromHost(config.host);
66
+ if (extractedRegion) {
67
+ return extractedRegion;
68
+ }
69
+ throw new Error(
70
+ "DSQLStore: region could not be determined. Provide region in config or use a standard DSQL endpoint."
71
+ );
72
+ };
73
+
74
+ // src/storage/client.ts
75
+ function truncateQuery(query, maxLength = 100) {
76
+ const normalized = query.replace(/\s+/g, " ").trim();
77
+ if (normalized.length <= maxLength) {
78
+ return normalized;
79
+ }
80
+ return normalized.slice(0, maxLength) + "...";
81
+ }
82
+ var PoolAdapter = class {
83
+ constructor($pool) {
84
+ this.$pool = $pool;
85
+ }
86
+ connect() {
87
+ return this.$pool.connect();
88
+ }
89
+ async none(query, values) {
90
+ await this.$pool.query(query, values);
91
+ return null;
92
+ }
93
+ async one(query, values) {
94
+ const result = await this.$pool.query(query, values);
95
+ if (result.rows.length === 0) {
96
+ throw new Error(`No data returned from query: ${truncateQuery(query)}`);
97
+ }
98
+ if (result.rows.length > 1) {
99
+ throw new Error(`Multiple rows returned when one was expected: ${truncateQuery(query)}`);
100
+ }
101
+ return result.rows[0];
102
+ }
103
+ async oneOrNone(query, values) {
104
+ const result = await this.$pool.query(query, values);
105
+ if (result.rows.length === 0) {
106
+ return null;
107
+ }
108
+ if (result.rows.length > 1) {
109
+ throw new Error(`Multiple rows returned when one or none was expected: ${truncateQuery(query)}`);
110
+ }
111
+ return result.rows[0];
112
+ }
113
+ async any(query, values) {
114
+ const result = await this.$pool.query(query, values);
115
+ return result.rows;
116
+ }
117
+ async manyOrNone(query, values) {
118
+ return this.any(query, values);
119
+ }
120
+ async many(query, values) {
121
+ const result = await this.$pool.query(query, values);
122
+ if (result.rows.length === 0) {
123
+ throw new Error(`No data returned from query: ${truncateQuery(query)}`);
124
+ }
125
+ return result.rows;
126
+ }
127
+ async query(query, values) {
128
+ return this.$pool.query(query, values);
129
+ }
130
+ async tx(callback) {
131
+ const client = await this.$pool.connect();
132
+ try {
133
+ await client.query("BEGIN");
134
+ const txClient = new TransactionClient(client);
135
+ const result = await callback(txClient);
136
+ await client.query("COMMIT");
137
+ return result;
138
+ } catch (error) {
139
+ try {
140
+ await client.query("ROLLBACK");
141
+ } catch (rollbackError) {
142
+ console.error("Transaction rollback failed:", rollbackError);
143
+ }
144
+ throw error;
145
+ } finally {
146
+ client.release();
147
+ }
148
+ }
149
+ };
150
+ var TransactionClient = class {
151
+ constructor(client) {
152
+ this.client = client;
153
+ }
154
+ async none(query, values) {
155
+ await this.client.query(query, values);
156
+ return null;
157
+ }
158
+ async one(query, values) {
159
+ const result = await this.client.query(query, values);
160
+ if (result.rows.length === 0) {
161
+ throw new Error(`No data returned from query: ${truncateQuery(query)}`);
162
+ }
163
+ if (result.rows.length > 1) {
164
+ throw new Error(`Multiple rows returned when one was expected: ${truncateQuery(query)}`);
165
+ }
166
+ return result.rows[0];
167
+ }
168
+ async oneOrNone(query, values) {
169
+ const result = await this.client.query(query, values);
170
+ if (result.rows.length === 0) {
171
+ return null;
172
+ }
173
+ if (result.rows.length > 1) {
174
+ throw new Error(`Multiple rows returned when one or none was expected: ${truncateQuery(query)}`);
175
+ }
176
+ return result.rows[0];
177
+ }
178
+ async any(query, values) {
179
+ const result = await this.client.query(query, values);
180
+ return result.rows;
181
+ }
182
+ async manyOrNone(query, values) {
183
+ return this.any(query, values);
184
+ }
185
+ async many(query, values) {
186
+ const result = await this.client.query(query, values);
187
+ if (result.rows.length === 0) {
188
+ throw new Error(`No data returned from query: ${truncateQuery(query)}`);
189
+ }
190
+ return result.rows;
191
+ }
192
+ async query(query, values) {
193
+ return this.client.query(query, values);
194
+ }
195
+ async batch(promises) {
196
+ return Promise.all(promises);
197
+ }
198
+ };
199
+
200
+ // src/shared/retry.ts
201
+ var RETRIABLE_ERROR_CODES = {
202
+ /** Serialization failure - OCC conflict in Aurora DSQL (default retriable) */
203
+ SERIALIZATION_FAILURE: "40001"};
204
+ var DEFAULT_RETRIABLE_SQLSTATES = /* @__PURE__ */ new Set([RETRIABLE_ERROR_CODES.SERIALIZATION_FAILURE]);
205
+ var DEFAULT_RETRY_OPTIONS = {
206
+ maxAttempts: 5,
207
+ initialDelayMs: 100,
208
+ maxDelayMs: 2e3,
209
+ backoffMultiplier: 2,
210
+ jitter: true
211
+ };
212
+ function isPostgresError(error) {
213
+ return error instanceof Error && "code" in error && typeof error.code === "string";
214
+ }
215
+ function getErrorCode(error) {
216
+ if (isPostgresError(error)) {
217
+ return error.code;
218
+ }
219
+ return void 0;
220
+ }
221
+ var SQLSTATE_PATTERN = /^[0-9A-Z]{5}$/;
222
+ function getPostgresSqlStateCode(error) {
223
+ const raw = getErrorCode(error);
224
+ if (!raw) return void 0;
225
+ const code = raw.toUpperCase();
226
+ if (SQLSTATE_PATTERN.test(code)) {
227
+ return code;
228
+ }
229
+ return void 0;
230
+ }
231
+ function isRetriableError(error) {
232
+ const sqlstate = getPostgresSqlStateCode(error);
233
+ if (!sqlstate) {
234
+ return false;
235
+ }
236
+ return DEFAULT_RETRIABLE_SQLSTATES.has(sqlstate);
237
+ }
238
+ function calculateRetryDelay(attempt, options = {}) {
239
+ const {
240
+ initialDelayMs = DEFAULT_RETRY_OPTIONS.initialDelayMs,
241
+ maxDelayMs = DEFAULT_RETRY_OPTIONS.maxDelayMs,
242
+ backoffMultiplier = DEFAULT_RETRY_OPTIONS.backoffMultiplier,
243
+ jitter = DEFAULT_RETRY_OPTIONS.jitter
244
+ } = options;
245
+ const baseDelay = initialDelayMs * Math.pow(backoffMultiplier, attempt - 1);
246
+ const cappedDelay = Math.min(baseDelay, maxDelayMs);
247
+ if (jitter) {
248
+ return Math.floor(Math.random() * (cappedDelay + 1));
249
+ }
250
+ return cappedDelay;
251
+ }
252
+ function sleep(ms) {
253
+ return new Promise((resolve) => setTimeout(resolve, ms));
254
+ }
255
+ function validateRetryOptions(options) {
256
+ const { maxAttempts, initialDelayMs, maxDelayMs, backoffMultiplier } = options;
257
+ if (maxAttempts < 1) {
258
+ throw new Error(`Invalid retry option: maxAttempts must be >= 1, got ${maxAttempts}`);
259
+ }
260
+ if (initialDelayMs < 0) {
261
+ throw new Error(`Invalid retry option: initialDelayMs must be >= 0, got ${initialDelayMs}`);
262
+ }
263
+ if (maxDelayMs <= 0) {
264
+ throw new Error(`Invalid retry option: maxDelayMs must be > 0, got ${maxDelayMs}`);
265
+ }
266
+ if (backoffMultiplier < 1) {
267
+ throw new Error(`Invalid retry option: backoffMultiplier must be >= 1, got ${backoffMultiplier}`);
268
+ }
269
+ if (maxDelayMs < initialDelayMs) {
270
+ throw new Error(`Invalid retry option: maxDelayMs (${maxDelayMs}) must be >= initialDelayMs (${initialDelayMs})`);
271
+ }
272
+ }
273
+ async function withRetry(fn, options = {}) {
274
+ const {
275
+ maxAttempts = DEFAULT_RETRY_OPTIONS.maxAttempts,
276
+ initialDelayMs = DEFAULT_RETRY_OPTIONS.initialDelayMs,
277
+ maxDelayMs = DEFAULT_RETRY_OPTIONS.maxDelayMs,
278
+ backoffMultiplier = DEFAULT_RETRY_OPTIONS.backoffMultiplier,
279
+ jitter = DEFAULT_RETRY_OPTIONS.jitter,
280
+ onRetry,
281
+ isRetriable = isRetriableError
282
+ } = options;
283
+ validateRetryOptions({ maxAttempts, initialDelayMs, maxDelayMs, backoffMultiplier });
284
+ const startTime = Date.now();
285
+ let lastError;
286
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
287
+ try {
288
+ const result = await fn();
289
+ return {
290
+ result,
291
+ attempts: attempt,
292
+ totalTimeMs: Date.now() - startTime
293
+ };
294
+ } catch (error) {
295
+ lastError = error instanceof Error ? error : new Error(String(error));
296
+ if (attempt === maxAttempts || !isRetriable(error)) {
297
+ throw lastError;
298
+ }
299
+ const delay = calculateRetryDelay(attempt, {
300
+ initialDelayMs,
301
+ maxDelayMs,
302
+ backoffMultiplier,
303
+ jitter
304
+ });
305
+ if (onRetry) {
306
+ onRetry(lastError, attempt, delay);
307
+ }
308
+ await sleep(delay);
309
+ }
310
+ }
311
+ throw lastError;
312
+ }
313
+
314
+ // src/shared/batch.ts
315
+ var DEFAULT_MAX_ROWS_PER_BATCH = 3e3;
316
+ function splitIntoBatches(records, options = {}) {
317
+ const maxRows = options.maxRows ?? DEFAULT_MAX_ROWS_PER_BATCH;
318
+ if (records.length === 0) {
319
+ return {
320
+ batches: [],
321
+ totalRecords: 0,
322
+ batchCount: 0
323
+ };
324
+ }
325
+ if (maxRows <= 0) {
326
+ throw new Error(`maxRows must be a positive number, got: ${maxRows}`);
327
+ }
328
+ const batches = [];
329
+ for (let i = 0; i < records.length; i += maxRows) {
330
+ batches.push(records.slice(i, i + maxRows));
331
+ }
332
+ return {
333
+ batches,
334
+ totalRecords: records.length,
335
+ batchCount: batches.length
336
+ };
337
+ }
338
+
339
+ // src/storage/db/index.ts
340
+ function resolveDsqlConfig(config) {
341
+ if ("client" in config) {
342
+ return {
343
+ client: config.client,
344
+ schemaName: config.schemaName,
345
+ skipDefaultIndexes: config.skipDefaultIndexes,
346
+ indexes: config.indexes
347
+ };
348
+ }
349
+ if ("pool" in config) {
350
+ return {
351
+ client: new PoolAdapter(config.pool),
352
+ schemaName: config.schemaName,
353
+ skipDefaultIndexes: config.skipDefaultIndexes,
354
+ indexes: config.indexes
355
+ };
356
+ }
357
+ const pool = new pg.Pool({
358
+ host: config.host,
359
+ database: config.database,
360
+ user: config.user,
361
+ Client: auroraDsqlNodePostgresConnector.AuroraDSQLClient
362
+ });
363
+ return {
364
+ client: new PoolAdapter(pool),
365
+ schemaName: config.schemaName,
366
+ skipDefaultIndexes: config.skipDefaultIndexes,
367
+ indexes: config.indexes
368
+ };
369
+ }
370
+ function getSchemaName(schema) {
371
+ return schema ? `"${utils.parseSqlIdentifier(schema, "schema name")}"` : '"public"';
372
+ }
373
+ function getTableName({ indexName, schemaName }) {
374
+ const parsedIndexName = utils.parseSqlIdentifier(indexName, "index name");
375
+ const quotedIndexName = `"${parsedIndexName}"`;
376
+ const quotedSchemaName = schemaName;
377
+ return quotedSchemaName ? `${quotedSchemaName}.${quotedIndexName}` : quotedIndexName;
378
+ }
379
+ var schemaSetupRegistry = /* @__PURE__ */ new Map();
380
+ var DsqlDB = class extends base.MastraBase {
381
+ client;
382
+ schemaName;
383
+ skipDefaultIndexes;
384
+ constructor(config) {
385
+ super({
386
+ component: "STORAGE",
387
+ name: "DSQL_DB_LAYER"
388
+ });
389
+ this.client = config.client;
390
+ this.schemaName = config.schemaName;
391
+ this.skipDefaultIndexes = config.skipDefaultIndexes;
392
+ }
393
+ async hasColumn(table, column) {
394
+ const schema = this.schemaName || "public";
395
+ const result = await this.client.oneOrNone(
396
+ `SELECT 1 FROM information_schema.columns WHERE table_schema = $1 AND table_name = $2 AND (column_name = $3 OR column_name = $4)`,
397
+ [schema, table, column, column.toLowerCase()]
398
+ );
399
+ return !!result;
400
+ }
401
+ /**
402
+ * Prepares values for insertion, handling TEXT columns storing JSON by stringifying them.
403
+ * Aurora DSQL does not support JSONB natively, so we store JSON as TEXT.
404
+ */
405
+ prepareValuesForInsert(record, tableName) {
406
+ return Object.entries(record).map(([key, value]) => {
407
+ const schema = storage.TABLE_SCHEMAS[tableName];
408
+ const columnSchema = schema?.[key];
409
+ if (columnSchema?.type === "jsonb" && value !== null && value !== void 0 && typeof value === "object") {
410
+ return JSON.stringify(value);
411
+ }
412
+ return value;
413
+ });
414
+ }
415
+ /**
416
+ * Adds timestamp Z columns to a record if timestamp columns exist
417
+ */
418
+ addTimestampZColumns(record) {
419
+ if (record.createdAt) {
420
+ record.createdAtZ = record.createdAt;
421
+ }
422
+ if (record.created_at) {
423
+ record.created_atZ = record.created_at;
424
+ }
425
+ if (record.updatedAt) {
426
+ record.updatedAtZ = record.updatedAt;
427
+ }
428
+ }
429
+ /**
430
+ * Prepares a value for database operations, handling Date objects and JSON serialization.
431
+ * This is schema-aware and stringifies objects for TEXT columns storing JSON.
432
+ * Aurora DSQL: We use TEXT instead of JSONB and cast to ::jsonb when filtering.
433
+ */
434
+ prepareValue(value, columnName, tableName) {
435
+ if (value === null || value === void 0) {
436
+ return value;
437
+ }
438
+ if (value instanceof Date) {
439
+ return value.toISOString();
440
+ }
441
+ const schema = storage.TABLE_SCHEMAS[tableName];
442
+ const columnSchema = schema?.[columnName];
443
+ if (columnSchema?.type === "jsonb") {
444
+ if (typeof value === "object") {
445
+ return JSON.stringify(value);
446
+ }
447
+ }
448
+ if (typeof value === "object") {
449
+ return JSON.stringify(value);
450
+ }
451
+ return value;
452
+ }
453
+ async setupSchema() {
454
+ if (!this.schemaName) {
455
+ return;
456
+ }
457
+ let registryEntry = schemaSetupRegistry.get(this.schemaName);
458
+ if (registryEntry?.complete) {
459
+ return;
460
+ }
461
+ const quotedSchemaName = getSchemaName(this.schemaName);
462
+ if (!registryEntry?.promise) {
463
+ const schemaNameCapture = this.schemaName;
464
+ const setupPromise = (async () => {
465
+ try {
466
+ const schemaExists = await this.client.oneOrNone(
467
+ `
468
+ SELECT EXISTS (
469
+ SELECT 1 FROM information_schema.schemata
470
+ WHERE schema_name = $1
471
+ )
472
+ `,
473
+ [schemaNameCapture]
474
+ );
475
+ if (!schemaExists?.exists) {
476
+ try {
477
+ await this.client.none(`CREATE SCHEMA IF NOT EXISTS ${quotedSchemaName}`);
478
+ this.logger.info(`Schema "${schemaNameCapture}" created successfully`);
479
+ } catch (error) {
480
+ this.logger.error(`Failed to create schema "${schemaNameCapture}"`, { error });
481
+ throw new Error(
482
+ `Unable to create schema "${schemaNameCapture}". This requires CREATE privilege on the database. Either create the schema manually or grant CREATE privilege to the user.`
483
+ );
484
+ }
485
+ }
486
+ const entry = schemaSetupRegistry.get(schemaNameCapture);
487
+ if (entry) {
488
+ entry.complete = true;
489
+ }
490
+ this.logger.debug(`Schema "${quotedSchemaName}" is ready for use`);
491
+ } catch (error) {
492
+ schemaSetupRegistry.delete(schemaNameCapture);
493
+ throw error;
494
+ }
495
+ })();
496
+ schemaSetupRegistry.set(this.schemaName, { promise: setupPromise, complete: false });
497
+ registryEntry = schemaSetupRegistry.get(this.schemaName);
498
+ }
499
+ await registryEntry.promise;
500
+ }
501
+ /**
502
+ * Override getSqlType to map JSONB to TEXT for Aurora DSQL compatibility.
503
+ * Aurora DSQL does not fully support native JSONB, so we store JSON as TEXT
504
+ * and cast to ::jsonb only when filtering/querying.
505
+ */
506
+ getSqlType(type) {
507
+ switch (type) {
508
+ case "jsonb":
509
+ return "TEXT";
510
+ case "uuid":
511
+ return "UUID";
512
+ case "boolean":
513
+ return "BOOLEAN";
514
+ default:
515
+ return storage.getSqlType(type);
516
+ }
517
+ }
518
+ getDefaultValue(type) {
519
+ switch (type) {
520
+ case "timestamp":
521
+ return "DEFAULT NOW()";
522
+ case "jsonb":
523
+ return "DEFAULT '{}'";
524
+ default:
525
+ return storage.getDefaultValue(type);
526
+ }
527
+ }
528
+ async insert({ tableName, record }) {
529
+ this.addTimestampZColumns(record);
530
+ const schemaName = getSchemaName(this.schemaName);
531
+ const columns = Object.keys(record).map((col) => utils.parseSqlIdentifier(col, "column name"));
532
+ const values = this.prepareValuesForInsert(record, tableName);
533
+ const placeholders = values.map((_, i) => `$${i + 1}`).join(", ");
534
+ await withRetry(
535
+ async () => {
536
+ await this.client.none(
537
+ `INSERT INTO ${getTableName({ indexName: tableName, schemaName })} (${columns.map((c) => `"${c}"`).join(", ")}) VALUES (${placeholders})`,
538
+ values
539
+ );
540
+ },
541
+ {
542
+ onRetry: (error, attempt, delay) => {
543
+ this.logger?.warn?.(`insert retry ${attempt} for table ${tableName} after ${delay}ms: ${error.message}`);
544
+ }
545
+ }
546
+ ).catch((error$1) => {
547
+ throw new error.MastraError(
548
+ {
549
+ id: storage.createStorageErrorId("DSQL", "INSERT", "FAILED"),
550
+ domain: error.ErrorDomain.STORAGE,
551
+ category: error.ErrorCategory.THIRD_PARTY,
552
+ details: {
553
+ tableName
554
+ }
555
+ },
556
+ error$1
557
+ );
558
+ });
559
+ }
560
+ async clearTable({ tableName }) {
561
+ try {
562
+ await withRetry(
563
+ async () => {
564
+ const schemaName = getSchemaName(this.schemaName);
565
+ const tableNameWithSchema = getTableName({ indexName: tableName, schemaName });
566
+ await this.client.none(`DELETE FROM ${tableNameWithSchema}`);
567
+ },
568
+ {
569
+ onRetry: (error, attempt, delay) => {
570
+ this.logger?.warn?.(`clearTable retry ${attempt} for ${tableName} after ${delay}ms: ${error.message}`);
571
+ }
572
+ }
573
+ );
574
+ } catch (error$1) {
575
+ throw new error.MastraError(
576
+ {
577
+ id: storage.createStorageErrorId("DSQL", "CLEAR_TABLE", "FAILED"),
578
+ domain: error.ErrorDomain.STORAGE,
579
+ category: error.ErrorCategory.THIRD_PARTY,
580
+ details: {
581
+ tableName
582
+ }
583
+ },
584
+ error$1
585
+ );
586
+ }
587
+ }
588
+ async createTable({
589
+ tableName,
590
+ schema
591
+ }) {
592
+ await withRetry(
593
+ async () => {
594
+ const timeZColumnNames = Object.entries(schema).filter(([_, def]) => def.type === "timestamp").map(([name]) => name);
595
+ const timeZColumns = Object.entries(schema).filter(([_, def]) => def.type === "timestamp").map(([name]) => {
596
+ const parsedName = utils.parseSqlIdentifier(name, "column name");
597
+ return `"${parsedName}Z" TIMESTAMPTZ DEFAULT NOW()`;
598
+ });
599
+ const columns = Object.entries(schema).map(([name, def]) => {
600
+ const parsedName = utils.parseSqlIdentifier(name, "column name");
601
+ const constraints = [];
602
+ if (def.primaryKey) constraints.push("PRIMARY KEY");
603
+ if (!def.nullable) constraints.push("NOT NULL");
604
+ const sqlType = this.getSqlType(def.type);
605
+ return `"${parsedName}" ${sqlType} ${constraints.join(" ")}`;
606
+ });
607
+ if (this.schemaName) {
608
+ await this.setupSchema();
609
+ }
610
+ const finalColumns = [...columns, ...timeZColumns].join(",\n");
611
+ const constraintPrefix = this.schemaName ? `${this.schemaName}_` : "";
612
+ const schemaName = getSchemaName(this.schemaName);
613
+ const createTableSql = `
614
+ CREATE TABLE IF NOT EXISTS ${getTableName({ indexName: tableName, schemaName })} (
615
+ ${finalColumns}
616
+ );
617
+ `;
618
+ await this.client.none(createTableSql);
619
+ if (tableName === storage.TABLE_WORKFLOW_SNAPSHOT) {
620
+ const indexName = `${constraintPrefix}mastra_workflow_snapshot_workflow_name_run_id_key`;
621
+ const fullTableName = getTableName({ indexName: tableName, schemaName });
622
+ try {
623
+ const indexExists = await this.client.oneOrNone(`SELECT 1 FROM pg_indexes WHERE indexname = $1`, [
624
+ indexName
625
+ ]);
626
+ if (!indexExists) {
627
+ const result = await this.client.oneOrNone(
628
+ `CREATE UNIQUE INDEX ASYNC "${indexName}" ON ${fullTableName} ("workflow_name", "run_id")`
629
+ );
630
+ if (result?.job_uuid) {
631
+ await this.waitForDSQLJob(result.job_uuid);
632
+ }
633
+ this.logger?.debug?.(`Created unique index ${indexName} on ${fullTableName}`);
634
+ }
635
+ } catch (error) {
636
+ this.logger?.warn?.(`Failed to create unique index ${indexName}:`, error);
637
+ }
638
+ }
639
+ await this.alterTable({
640
+ tableName,
641
+ schema,
642
+ ifNotExists: timeZColumnNames
643
+ });
644
+ if (tableName === storage.TABLE_SPANS) {
645
+ await this.setupTimestampTriggers(tableName);
646
+ await this.migrateSpansTable();
647
+ }
648
+ },
649
+ {
650
+ onRetry: (error, attempt, delay) => {
651
+ this.logger?.warn?.(`createTable retry ${attempt} for ${tableName} after ${delay}ms: ${error.message}`);
652
+ }
653
+ }
654
+ ).catch((error$1) => {
655
+ throw new error.MastraError(
656
+ {
657
+ id: storage.createStorageErrorId("DSQL", "CREATE_TABLE", "FAILED"),
658
+ domain: error.ErrorDomain.STORAGE,
659
+ category: error.ErrorCategory.THIRD_PARTY,
660
+ details: {
661
+ tableName
662
+ }
663
+ },
664
+ error$1
665
+ );
666
+ });
667
+ }
668
+ /**
669
+ * Set up timestamp triggers for a table to automatically manage createdAt/updatedAt
670
+ * Note: Aurora DSQL doesn't support triggers, PL/pgSQL, or CREATE FUNCTION.
671
+ * Timestamps are managed at the application level in insert/update operations.
672
+ * This method is kept as a no-op for API compatibility.
673
+ */
674
+ async setupTimestampTriggers(_tableName) {
675
+ return;
676
+ }
677
+ /**
678
+ * Migrates the spans table schema from OLD_SPAN_SCHEMA to current SPAN_SCHEMA.
679
+ * This adds new columns that don't exist in old schema.
680
+ */
681
+ async migrateSpansTable() {
682
+ const fullTableName = getTableName({ indexName: storage.TABLE_SPANS, schemaName: getSchemaName(this.schemaName) });
683
+ const schema = storage.TABLE_SCHEMAS[storage.TABLE_SPANS];
684
+ try {
685
+ for (const [columnName, columnDef] of Object.entries(schema)) {
686
+ const columnExists = await this.hasColumn(storage.TABLE_SPANS, columnName);
687
+ if (!columnExists) {
688
+ const parsedColumnName = utils.parseSqlIdentifier(columnName, "column name");
689
+ const sqlType = this.getSqlType(columnDef.type);
690
+ const nullable = columnDef.nullable ? "" : "NOT NULL";
691
+ const defaultValue = !columnDef.nullable ? this.getDefaultValue(columnDef.type) : "";
692
+ const alterSql = `ALTER TABLE ${fullTableName} ADD COLUMN IF NOT EXISTS "${parsedColumnName}" ${sqlType} ${nullable} ${defaultValue}`.trim();
693
+ await this.client.none(alterSql);
694
+ this.logger?.debug?.(`Added column '${columnName}' to ${fullTableName}`);
695
+ if (sqlType === "TIMESTAMP") {
696
+ const timestampZSql = `ALTER TABLE ${fullTableName} ADD COLUMN IF NOT EXISTS "${parsedColumnName}Z" TIMESTAMPTZ DEFAULT NOW()`.trim();
697
+ await this.client.none(timestampZSql);
698
+ this.logger?.debug?.(`Added timezone column '${columnName}Z' to ${fullTableName}`);
699
+ }
700
+ }
701
+ }
702
+ for (const [columnName, columnDef] of Object.entries(schema)) {
703
+ if (columnDef.type === "timestamp") {
704
+ const tzColumnName = `${columnName}Z`;
705
+ const tzColumnExists = await this.hasColumn(storage.TABLE_SPANS, tzColumnName);
706
+ if (!tzColumnExists) {
707
+ const parsedTzColumnName = utils.parseSqlIdentifier(tzColumnName, "column name");
708
+ const timestampZSql = `ALTER TABLE ${fullTableName} ADD COLUMN IF NOT EXISTS "${parsedTzColumnName}" TIMESTAMPTZ DEFAULT NOW()`.trim();
709
+ await this.client.none(timestampZSql);
710
+ this.logger?.debug?.(`Added timezone column '${tzColumnName}' to ${fullTableName}`);
711
+ }
712
+ }
713
+ }
714
+ this.logger?.info?.(`Migration completed for ${fullTableName}`);
715
+ } catch (error) {
716
+ this.logger?.warn?.(`Failed to migrate spans table ${fullTableName}:`, error);
717
+ }
718
+ }
719
+ /**
720
+ * Alters table schema to add columns if they don't exist
721
+ * @param tableName Name of the table
722
+ * @param schema Schema of the table
723
+ * @param ifNotExists Array of column names to add if they don't exist
724
+ */
725
+ async alterTable({
726
+ tableName,
727
+ schema,
728
+ ifNotExists
729
+ }) {
730
+ const fullTableName = getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) });
731
+ try {
732
+ for (const columnName of ifNotExists) {
733
+ if (schema[columnName]) {
734
+ const columnDef = schema[columnName];
735
+ const sqlType = this.getSqlType(columnDef.type);
736
+ const parsedColumnName = utils.parseSqlIdentifier(columnName, "column name");
737
+ const alterSql = `ALTER TABLE ${fullTableName} ADD COLUMN IF NOT EXISTS "${parsedColumnName}" ${sqlType}`;
738
+ await this.client.none(alterSql);
739
+ if (sqlType === "TIMESTAMP") {
740
+ const alterSqlZ = `ALTER TABLE ${fullTableName} ADD COLUMN IF NOT EXISTS "${parsedColumnName}Z" TIMESTAMPTZ`;
741
+ await this.client.none(alterSqlZ);
742
+ }
743
+ this.logger?.debug?.(`Ensured column ${parsedColumnName} exists in table ${fullTableName}`);
744
+ }
745
+ }
746
+ } catch (error$1) {
747
+ throw new error.MastraError(
748
+ {
749
+ id: storage.createStorageErrorId("DSQL", "ALTER_TABLE", "FAILED"),
750
+ domain: error.ErrorDomain.STORAGE,
751
+ category: error.ErrorCategory.THIRD_PARTY,
752
+ details: {
753
+ tableName
754
+ }
755
+ },
756
+ error$1
757
+ );
758
+ }
759
+ }
760
+ async load({ tableName, keys }) {
761
+ try {
762
+ const keyEntries = Object.entries(keys).map(([key, value]) => [utils.parseSqlIdentifier(key, "column name"), value]);
763
+ const conditions = keyEntries.map(([key], index) => `"${key}" = $${index + 1}`).join(" AND ");
764
+ const values = keyEntries.map(([_, value]) => value);
765
+ const result = await this.client.oneOrNone(
766
+ `SELECT * FROM ${getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) })} WHERE ${conditions} ORDER BY "createdAt" DESC LIMIT 1`,
767
+ values
768
+ );
769
+ if (!result) {
770
+ return null;
771
+ }
772
+ if (tableName === storage.TABLE_WORKFLOW_SNAPSHOT) {
773
+ const snapshot = result;
774
+ if (typeof snapshot.snapshot === "string") {
775
+ snapshot.snapshot = JSON.parse(snapshot.snapshot);
776
+ }
777
+ return snapshot;
778
+ }
779
+ return result;
780
+ } catch (error$1) {
781
+ throw new error.MastraError(
782
+ {
783
+ id: storage.createStorageErrorId("DSQL", "LOAD", "FAILED"),
784
+ domain: error.ErrorDomain.STORAGE,
785
+ category: error.ErrorCategory.THIRD_PARTY,
786
+ details: {
787
+ tableName
788
+ }
789
+ },
790
+ error$1
791
+ );
792
+ }
793
+ }
794
+ async batchInsert({ tableName, records }) {
795
+ if (records.length === 0) {
796
+ return;
797
+ }
798
+ try {
799
+ const { batches } = splitIntoBatches(records, { maxRows: DEFAULT_MAX_ROWS_PER_BATCH });
800
+ for (const batch of batches) {
801
+ await withRetry(
802
+ async () => {
803
+ await this.client.tx(async (t) => {
804
+ for (const record of batch) {
805
+ this.addTimestampZColumns(record);
806
+ const schemaName = getSchemaName(this.schemaName);
807
+ const columns = Object.keys(record).map((col) => utils.parseSqlIdentifier(col, "column name"));
808
+ const values = this.prepareValuesForInsert(record, tableName);
809
+ const placeholders = values.map((_, i) => `$${i + 1}`).join(", ");
810
+ await t.none(
811
+ `INSERT INTO ${getTableName({ indexName: tableName, schemaName })} (${columns.map((c) => `"${c}"`).join(", ")}) VALUES (${placeholders})`,
812
+ values
813
+ );
814
+ }
815
+ });
816
+ },
817
+ {
818
+ onRetry: (error, attempt, delay) => {
819
+ this.logger?.warn?.(
820
+ `Batch insert retry ${attempt} for table ${tableName} after ${delay}ms: ${error.message}`
821
+ );
822
+ }
823
+ }
824
+ );
825
+ }
826
+ } catch (error$1) {
827
+ throw new error.MastraError(
828
+ {
829
+ id: storage.createStorageErrorId("DSQL", "BATCH_INSERT", "FAILED"),
830
+ domain: error.ErrorDomain.STORAGE,
831
+ category: error.ErrorCategory.THIRD_PARTY,
832
+ details: {
833
+ tableName,
834
+ numberOfRecords: records.length
835
+ }
836
+ },
837
+ error$1
838
+ );
839
+ }
840
+ }
841
+ async dropTable({ tableName }) {
842
+ try {
843
+ const schemaName = getSchemaName(this.schemaName);
844
+ const tableNameWithSchema = getTableName({ indexName: tableName, schemaName });
845
+ await this.client.none(`DROP TABLE IF EXISTS ${tableNameWithSchema}`);
846
+ } catch (error$1) {
847
+ throw new error.MastraError(
848
+ {
849
+ id: storage.createStorageErrorId("DSQL", "DROP_TABLE", "FAILED"),
850
+ domain: error.ErrorDomain.STORAGE,
851
+ category: error.ErrorCategory.THIRD_PARTY,
852
+ details: {
853
+ tableName
854
+ }
855
+ },
856
+ error$1
857
+ );
858
+ }
859
+ }
860
+ /**
861
+ * Wait for an asynchronous DSQL job to complete.
862
+ * Aurora DSQL requires CREATE INDEX ASYNC and sys.wait_for_job() to wait for completion.
863
+ */
864
+ async waitForDSQLJob(jobUuid, timeoutMs = 6e4) {
865
+ const pollIntervalMs = 1e3;
866
+ const startTime = Date.now();
867
+ while (Date.now() - startTime < timeoutMs) {
868
+ const result = await this.client.oneOrNone(`SELECT sys.wait_for_job($1, 1) as status`, [
869
+ jobUuid
870
+ ]);
871
+ if (result?.status === "COMPLETED") {
872
+ return;
873
+ }
874
+ if (result?.status === "FAILED") {
875
+ throw new Error(`DSQL async job ${jobUuid} failed`);
876
+ }
877
+ await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
878
+ }
879
+ throw new Error(`DSQL async job ${jobUuid} timed out after ${timeoutMs}ms`);
880
+ }
881
+ async createIndex(options) {
882
+ try {
883
+ const {
884
+ name,
885
+ table,
886
+ columns,
887
+ unique = false,
888
+ // Note: 'concurrent' option is ignored in DSQL - always uses ASYNC
889
+ where,
890
+ method = "btree",
891
+ opclass,
892
+ storage,
893
+ tablespace
894
+ } = options;
895
+ const schemaName = this.schemaName || "public";
896
+ const fullTableName = getTableName({
897
+ indexName: table,
898
+ schemaName: getSchemaName(this.schemaName)
899
+ });
900
+ const indexExists = await this.client.oneOrNone(
901
+ `SELECT 1 as exists FROM pg_indexes
902
+ WHERE indexname = $1
903
+ AND schemaname = $2`,
904
+ [name, schemaName]
905
+ );
906
+ if (indexExists) {
907
+ return;
908
+ }
909
+ const uniqueStr = unique ? "UNIQUE " : "";
910
+ const methodStr = method !== "btree" ? `USING ${method} ` : "";
911
+ const columnsStr = columns.map((col) => {
912
+ const colName = col.replace(/\s+(DESC|ASC)$/i, "").trim();
913
+ const quotedCol = `"${utils.parseSqlIdentifier(colName, "column name")}"`;
914
+ return opclass ? `${quotedCol} ${opclass}` : quotedCol;
915
+ }).join(", ");
916
+ const whereStr = where ? ` WHERE ${where}` : "";
917
+ const tablespaceStr = tablespace ? ` TABLESPACE ${tablespace}` : "";
918
+ let withStr = "";
919
+ if (storage && Object.keys(storage).length > 0) {
920
+ const storageParams = Object.entries(storage).map(([key, value]) => `${key} = ${value}`).join(", ");
921
+ withStr = ` WITH (${storageParams})`;
922
+ }
923
+ const quotedIndexName = `"${utils.parseSqlIdentifier(name, "index name")}"`;
924
+ const sql = `CREATE ${uniqueStr}INDEX ASYNC ${quotedIndexName} ON ${fullTableName} ${methodStr}(${columnsStr})${withStr}${tablespaceStr}${whereStr}`;
925
+ const result = await this.client.oneOrNone(sql);
926
+ if (result?.job_uuid) {
927
+ await this.waitForDSQLJob(result.job_uuid);
928
+ }
929
+ } catch (error$1) {
930
+ throw new error.MastraError(
931
+ {
932
+ id: storage.createStorageErrorId("DSQL", "INDEX_CREATE", "FAILED"),
933
+ domain: error.ErrorDomain.STORAGE,
934
+ category: error.ErrorCategory.THIRD_PARTY,
935
+ details: {
936
+ indexName: options.name,
937
+ tableName: options.table
938
+ }
939
+ },
940
+ error$1
941
+ );
942
+ }
943
+ }
944
+ async dropIndex(indexName) {
945
+ const schemaName = this.schemaName || "public";
946
+ await withRetry(
947
+ async () => {
948
+ const indexExists = await this.client.oneOrNone(
949
+ `SELECT 1 FROM pg_indexes
950
+ WHERE indexname = $1
951
+ AND schemaname = $2`,
952
+ [indexName, schemaName]
953
+ );
954
+ if (!indexExists) {
955
+ return;
956
+ }
957
+ const quotedIndexName = `"${utils.parseSqlIdentifier(indexName, "index name")}"`;
958
+ const sql = `DROP INDEX IF EXISTS ${getSchemaName(this.schemaName)}.${quotedIndexName}`;
959
+ await this.client.none(sql);
960
+ },
961
+ {
962
+ onRetry: (error, attempt, delay) => {
963
+ this.logger?.warn?.(`dropIndex retry ${attempt} for ${indexName} after ${delay}ms: ${error.message}`);
964
+ }
965
+ }
966
+ ).catch((error$1) => {
967
+ throw new error.MastraError(
968
+ {
969
+ id: storage.createStorageErrorId("DSQL", "INDEX_DROP", "FAILED"),
970
+ domain: error.ErrorDomain.STORAGE,
971
+ category: error.ErrorCategory.THIRD_PARTY,
972
+ details: {
973
+ indexName
974
+ }
975
+ },
976
+ error$1
977
+ );
978
+ });
979
+ }
980
+ async listIndexes(tableName) {
981
+ try {
982
+ const schemaName = this.schemaName || "public";
983
+ let query;
984
+ let params;
985
+ if (tableName) {
986
+ query = `
987
+ SELECT
988
+ i.indexname as name,
989
+ i.tablename as table,
990
+ i.indexdef as definition,
991
+ ix.indisunique as is_unique,
992
+ pg_size_pretty(pg_relation_size(c.oid)) as size,
993
+ array_agg(a.attname ORDER BY array_position(ix.indkey, a.attnum)) as columns
994
+ FROM pg_indexes i
995
+ JOIN pg_class c ON c.relname = i.indexname AND c.relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = i.schemaname)
996
+ JOIN pg_index ix ON ix.indexrelid = c.oid
997
+ JOIN pg_attribute a ON a.attrelid = ix.indrelid AND a.attnum = ANY(ix.indkey)
998
+ WHERE i.schemaname = $1
999
+ AND i.tablename = $2
1000
+ GROUP BY i.indexname, i.tablename, i.indexdef, ix.indisunique, c.oid
1001
+ `;
1002
+ params = [schemaName, tableName];
1003
+ } else {
1004
+ query = `
1005
+ SELECT
1006
+ i.indexname as name,
1007
+ i.tablename as table,
1008
+ i.indexdef as definition,
1009
+ ix.indisunique as is_unique,
1010
+ pg_size_pretty(pg_relation_size(c.oid)) as size,
1011
+ array_agg(a.attname ORDER BY array_position(ix.indkey, a.attnum)) as columns
1012
+ FROM pg_indexes i
1013
+ JOIN pg_class c ON c.relname = i.indexname AND c.relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = i.schemaname)
1014
+ JOIN pg_index ix ON ix.indexrelid = c.oid
1015
+ JOIN pg_attribute a ON a.attrelid = ix.indrelid AND a.attnum = ANY(ix.indkey)
1016
+ WHERE i.schemaname = $1
1017
+ GROUP BY i.indexname, i.tablename, i.indexdef, ix.indisunique, c.oid
1018
+ `;
1019
+ params = [schemaName];
1020
+ }
1021
+ const results = await this.client.manyOrNone(query, params);
1022
+ return results.map((row) => {
1023
+ let columns = [];
1024
+ if (typeof row.columns === "string" && row.columns.startsWith("{") && row.columns.endsWith("}")) {
1025
+ const arrayContent = row.columns.slice(1, -1);
1026
+ columns = arrayContent ? arrayContent.split(",") : [];
1027
+ } else if (Array.isArray(row.columns)) {
1028
+ columns = row.columns;
1029
+ }
1030
+ return {
1031
+ name: row.name,
1032
+ table: row.table,
1033
+ columns,
1034
+ unique: row.is_unique || false,
1035
+ size: row.size || "0",
1036
+ definition: row.definition || ""
1037
+ };
1038
+ });
1039
+ } catch (error$1) {
1040
+ throw new error.MastraError(
1041
+ {
1042
+ id: storage.createStorageErrorId("DSQL", "INDEX_LIST", "FAILED"),
1043
+ domain: error.ErrorDomain.STORAGE,
1044
+ category: error.ErrorCategory.THIRD_PARTY,
1045
+ details: tableName ? {
1046
+ tableName
1047
+ } : {}
1048
+ },
1049
+ error$1
1050
+ );
1051
+ }
1052
+ }
1053
+ async describeIndex(indexName) {
1054
+ try {
1055
+ const schemaName = this.schemaName || "public";
1056
+ const query = `
1057
+ SELECT
1058
+ i.indexname as name,
1059
+ i.tablename as table,
1060
+ i.indexdef as definition,
1061
+ ix.indisunique as is_unique,
1062
+ pg_size_pretty(pg_relation_size(c.oid)) as size,
1063
+ array_agg(a.attname ORDER BY array_position(ix.indkey, a.attnum)) as columns,
1064
+ am.amname as method,
1065
+ s.idx_scan as scans,
1066
+ s.idx_tup_read as tuples_read,
1067
+ s.idx_tup_fetch as tuples_fetched
1068
+ FROM pg_indexes i
1069
+ JOIN pg_class c ON c.relname = i.indexname AND c.relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = i.schemaname)
1070
+ JOIN pg_index ix ON ix.indexrelid = c.oid
1071
+ JOIN pg_attribute a ON a.attrelid = ix.indrelid AND a.attnum = ANY(ix.indkey)
1072
+ JOIN pg_am am ON c.relam = am.oid
1073
+ LEFT JOIN pg_stat_user_indexes s ON s.indexrelname = i.indexname AND s.schemaname = i.schemaname
1074
+ WHERE i.schemaname = $1
1075
+ AND i.indexname = $2
1076
+ GROUP BY i.indexname, i.tablename, i.indexdef, ix.indisunique, c.oid, am.amname, s.idx_scan, s.idx_tup_read, s.idx_tup_fetch
1077
+ `;
1078
+ const result = await this.client.oneOrNone(query, [schemaName, indexName]);
1079
+ if (!result) {
1080
+ throw new Error(`Index "${indexName}" not found in schema "${schemaName}"`);
1081
+ }
1082
+ let columns = [];
1083
+ if (typeof result.columns === "string" && result.columns.startsWith("{") && result.columns.endsWith("}")) {
1084
+ const arrayContent = result.columns.slice(1, -1);
1085
+ columns = arrayContent ? arrayContent.split(",") : [];
1086
+ } else if (Array.isArray(result.columns)) {
1087
+ columns = result.columns;
1088
+ }
1089
+ const normalizedMethod = result.method === "btree_index" ? "btree" : result.method || "btree";
1090
+ return {
1091
+ name: result.name,
1092
+ table: result.table,
1093
+ columns,
1094
+ unique: result.is_unique || false,
1095
+ size: result.size || "0",
1096
+ definition: result.definition || "",
1097
+ method: normalizedMethod,
1098
+ scans: parseInt(result.scans) || 0,
1099
+ tuples_read: parseInt(result.tuples_read) || 0,
1100
+ tuples_fetched: parseInt(result.tuples_fetched) || 0
1101
+ };
1102
+ } catch (error$1) {
1103
+ throw new error.MastraError(
1104
+ {
1105
+ id: storage.createStorageErrorId("DSQL", "INDEX_DESCRIBE", "FAILED"),
1106
+ domain: error.ErrorDomain.STORAGE,
1107
+ category: error.ErrorCategory.THIRD_PARTY,
1108
+ details: {
1109
+ indexName
1110
+ }
1111
+ },
1112
+ error$1
1113
+ );
1114
+ }
1115
+ }
1116
+ async update({
1117
+ tableName,
1118
+ keys,
1119
+ data
1120
+ }) {
1121
+ const setColumns = [];
1122
+ const setValues = [];
1123
+ let paramIndex = 1;
1124
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1125
+ const dataWithTimestamp = {
1126
+ ...data,
1127
+ updatedAt: now,
1128
+ updatedAtZ: now
1129
+ };
1130
+ Object.entries(dataWithTimestamp).forEach(([key, value]) => {
1131
+ const parsedKey = utils.parseSqlIdentifier(key, "column name");
1132
+ setColumns.push(`"${parsedKey}" = $${paramIndex++}`);
1133
+ setValues.push(this.prepareValue(value, key, tableName));
1134
+ });
1135
+ const whereConditions = [];
1136
+ const whereValues = [];
1137
+ Object.entries(keys).forEach(([key, value]) => {
1138
+ const parsedKey = utils.parseSqlIdentifier(key, "column name");
1139
+ whereConditions.push(`"${parsedKey}" = $${paramIndex++}`);
1140
+ whereValues.push(this.prepareValue(value, key, tableName));
1141
+ });
1142
+ const tableName_ = getTableName({
1143
+ indexName: tableName,
1144
+ schemaName: getSchemaName(this.schemaName)
1145
+ });
1146
+ const sql = `UPDATE ${tableName_} SET ${setColumns.join(", ")} WHERE ${whereConditions.join(" AND ")}`;
1147
+ const values = [...setValues, ...whereValues];
1148
+ await withRetry(
1149
+ async () => {
1150
+ await this.client.none(sql, values);
1151
+ },
1152
+ {
1153
+ onRetry: (error, attempt, delay) => {
1154
+ this.logger?.warn?.(`update retry ${attempt} for table ${tableName} after ${delay}ms: ${error.message}`);
1155
+ }
1156
+ }
1157
+ ).catch((error$1) => {
1158
+ throw new error.MastraError(
1159
+ {
1160
+ id: storage.createStorageErrorId("DSQL", "UPDATE", "FAILED"),
1161
+ domain: error.ErrorDomain.STORAGE,
1162
+ category: error.ErrorCategory.THIRD_PARTY,
1163
+ details: {
1164
+ tableName
1165
+ }
1166
+ },
1167
+ error$1
1168
+ );
1169
+ });
1170
+ }
1171
+ async batchUpdate({
1172
+ tableName,
1173
+ updates
1174
+ }) {
1175
+ if (updates.length === 0) {
1176
+ return;
1177
+ }
1178
+ try {
1179
+ const { batches } = splitIntoBatches(updates, { maxRows: DEFAULT_MAX_ROWS_PER_BATCH });
1180
+ for (const batch of batches) {
1181
+ await withRetry(
1182
+ async () => {
1183
+ await this.client.tx(async (t) => {
1184
+ for (const { keys, data } of batch) {
1185
+ const setClauses = [];
1186
+ const whereConditions = [];
1187
+ const values = [];
1188
+ let paramIndex = 1;
1189
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1190
+ const dataWithTimestamp = {
1191
+ ...data,
1192
+ updatedAt: now,
1193
+ updatedAtZ: now
1194
+ };
1195
+ Object.entries(dataWithTimestamp).forEach(([key, value]) => {
1196
+ const parsedKey = utils.parseSqlIdentifier(key, "column name");
1197
+ const preparedValue = this.prepareValue(value, key, tableName);
1198
+ setClauses.push(`"${parsedKey}" = $${paramIndex++}`);
1199
+ values.push(preparedValue);
1200
+ });
1201
+ Object.entries(keys).forEach(([key, value]) => {
1202
+ const parsedKey = utils.parseSqlIdentifier(key, "column name");
1203
+ whereConditions.push(`"${parsedKey}" = $${paramIndex++}`);
1204
+ values.push(value);
1205
+ });
1206
+ const tableName_ = getTableName({
1207
+ indexName: tableName,
1208
+ schemaName: getSchemaName(this.schemaName)
1209
+ });
1210
+ const sql = `UPDATE ${tableName_} SET ${setClauses.join(", ")} WHERE ${whereConditions.join(" AND ")}`;
1211
+ await t.none(sql, values);
1212
+ }
1213
+ });
1214
+ },
1215
+ {
1216
+ onRetry: (error, attempt, delay) => {
1217
+ this.logger?.warn?.(
1218
+ `Batch update retry ${attempt} for table ${tableName} after ${delay}ms: ${error.message}`
1219
+ );
1220
+ }
1221
+ }
1222
+ );
1223
+ }
1224
+ } catch (error$1) {
1225
+ throw new error.MastraError(
1226
+ {
1227
+ id: storage.createStorageErrorId("DSQL", "BATCH_UPDATE", "FAILED"),
1228
+ domain: error.ErrorDomain.STORAGE,
1229
+ category: error.ErrorCategory.THIRD_PARTY,
1230
+ details: {
1231
+ tableName,
1232
+ numberOfRecords: updates.length
1233
+ }
1234
+ },
1235
+ error$1
1236
+ );
1237
+ }
1238
+ }
1239
+ async batchDelete({ tableName, keys }) {
1240
+ if (keys.length === 0) {
1241
+ return;
1242
+ }
1243
+ try {
1244
+ const tableName_ = getTableName({
1245
+ indexName: tableName,
1246
+ schemaName: getSchemaName(this.schemaName)
1247
+ });
1248
+ const { batches } = splitIntoBatches(keys, { maxRows: DEFAULT_MAX_ROWS_PER_BATCH });
1249
+ for (const batch of batches) {
1250
+ await withRetry(
1251
+ async () => {
1252
+ await this.client.tx(async (t) => {
1253
+ for (const keySet of batch) {
1254
+ const conditions = [];
1255
+ const values = [];
1256
+ let paramIndex = 1;
1257
+ Object.entries(keySet).forEach(([key, value]) => {
1258
+ const parsedKey = utils.parseSqlIdentifier(key, "column name");
1259
+ conditions.push(`"${parsedKey}" = $${paramIndex++}`);
1260
+ values.push(value);
1261
+ });
1262
+ const sql = `DELETE FROM ${tableName_} WHERE ${conditions.join(" AND ")}`;
1263
+ await t.none(sql, values);
1264
+ }
1265
+ });
1266
+ },
1267
+ {
1268
+ onRetry: (error, attempt, delay) => {
1269
+ this.logger?.warn?.(
1270
+ `Batch delete retry ${attempt} for table ${tableName} after ${delay}ms: ${error.message}`
1271
+ );
1272
+ }
1273
+ }
1274
+ );
1275
+ }
1276
+ } catch (error$1) {
1277
+ throw new error.MastraError(
1278
+ {
1279
+ id: storage.createStorageErrorId("DSQL", "BATCH_DELETE", "FAILED"),
1280
+ domain: error.ErrorDomain.STORAGE,
1281
+ category: error.ErrorCategory.THIRD_PARTY,
1282
+ details: {
1283
+ tableName,
1284
+ numberOfRecords: keys.length
1285
+ }
1286
+ },
1287
+ error$1
1288
+ );
1289
+ }
1290
+ }
1291
+ /**
1292
+ * Delete all data from a table (alias for clearTable for consistency with other stores)
1293
+ */
1294
+ async deleteData({ tableName }) {
1295
+ return this.clearTable({ tableName });
1296
+ }
1297
+ };
1298
+ function getSchemaName2(schema) {
1299
+ return schema ? `"${utils.parseSqlIdentifier(schema, "schema name")}"` : void 0;
1300
+ }
1301
+ function getTableName2({ indexName, schemaName }) {
1302
+ const parsedIndexName = utils.parseSqlIdentifier(indexName, "index name");
1303
+ const quotedIndexName = `"${parsedIndexName}"`;
1304
+ const quotedSchemaName = schemaName;
1305
+ return quotedSchemaName ? `${quotedSchemaName}.${quotedIndexName}` : quotedIndexName;
1306
+ }
1307
+ function transformFromSqlRow({
1308
+ tableName,
1309
+ sqlRow
1310
+ }) {
1311
+ const schema = storage.TABLE_SCHEMAS[tableName];
1312
+ const result = {};
1313
+ Object.entries(sqlRow).forEach(([key, value]) => {
1314
+ const columnSchema = schema?.[key];
1315
+ if (columnSchema?.type === "jsonb" && typeof value === "string") {
1316
+ try {
1317
+ result[key] = JSON.parse(value);
1318
+ } catch {
1319
+ result[key] = value;
1320
+ }
1321
+ } else if (columnSchema?.type === "timestamp" && value && typeof value === "string") {
1322
+ result[key] = new Date(value);
1323
+ } else if (columnSchema?.type === "timestamp" && value instanceof Date) {
1324
+ result[key] = value;
1325
+ } else if (columnSchema?.type === "boolean") {
1326
+ result[key] = Boolean(value);
1327
+ } else {
1328
+ result[key] = value;
1329
+ }
1330
+ });
1331
+ return result;
1332
+ }
1333
+
1334
+ // src/storage/domains/agents/index.ts
1335
+ var AgentsDSQL = class _AgentsDSQL extends storage.AgentsStorage {
1336
+ #db;
1337
+ #schema;
1338
+ #skipDefaultIndexes;
1339
+ #indexes;
1340
+ /** Tables managed by this domain */
1341
+ static MANAGED_TABLES = [storage.TABLE_AGENTS, storage.TABLE_AGENT_VERSIONS];
1342
+ constructor(config) {
1343
+ super();
1344
+ const { client, schemaName, skipDefaultIndexes, indexes } = resolveDsqlConfig(config);
1345
+ this.#db = new DsqlDB({ client, schemaName, skipDefaultIndexes });
1346
+ this.#schema = schemaName || "public";
1347
+ this.#skipDefaultIndexes = skipDefaultIndexes;
1348
+ this.#indexes = indexes?.filter((idx) => _AgentsDSQL.MANAGED_TABLES.includes(idx.table));
1349
+ }
1350
+ /**
1351
+ * Returns default index definitions for the agents domain tables.
1352
+ * Currently no default indexes are defined for agents.
1353
+ */
1354
+ getDefaultIndexDefinitions() {
1355
+ return [];
1356
+ }
1357
+ /**
1358
+ * Creates default indexes for optimal query performance.
1359
+ * Currently no default indexes are defined for agents.
1360
+ */
1361
+ async createDefaultIndexes() {
1362
+ if (this.#skipDefaultIndexes) {
1363
+ return;
1364
+ }
1365
+ }
1366
+ async init() {
1367
+ await this.#db.createTable({ tableName: storage.TABLE_AGENTS, schema: storage.TABLE_SCHEMAS[storage.TABLE_AGENTS] });
1368
+ await this.#db.createTable({ tableName: storage.TABLE_AGENT_VERSIONS, schema: storage.TABLE_SCHEMAS[storage.TABLE_AGENT_VERSIONS] });
1369
+ await this.createDefaultIndexes();
1370
+ await this.createCustomIndexes();
1371
+ }
1372
+ /**
1373
+ * Creates custom user-defined indexes for this domain's tables.
1374
+ */
1375
+ async createCustomIndexes() {
1376
+ if (!this.#indexes || this.#indexes.length === 0) {
1377
+ return;
1378
+ }
1379
+ for (const indexDef of this.#indexes) {
1380
+ try {
1381
+ await this.#db.createIndex(indexDef);
1382
+ } catch (error) {
1383
+ this.logger?.warn?.(`Failed to create custom index ${indexDef.name}:`, error);
1384
+ }
1385
+ }
1386
+ }
1387
+ async dangerouslyClearAll() {
1388
+ await this.#db.clearTable({ tableName: storage.TABLE_AGENT_VERSIONS });
1389
+ await this.#db.clearTable({ tableName: storage.TABLE_AGENTS });
1390
+ }
1391
+ parseJson(value, fieldName) {
1392
+ if (!value) return void 0;
1393
+ if (typeof value !== "string") return value;
1394
+ try {
1395
+ return JSON.parse(value);
1396
+ } catch (error$1) {
1397
+ const details = {
1398
+ value: value.length > 100 ? value.substring(0, 100) + "..." : value
1399
+ };
1400
+ if (fieldName) {
1401
+ details.field = fieldName;
1402
+ }
1403
+ throw new error.MastraError(
1404
+ {
1405
+ id: storage.createStorageErrorId("DSQL", "PARSE_JSON", "INVALID_JSON"),
1406
+ domain: error.ErrorDomain.STORAGE,
1407
+ category: error.ErrorCategory.SYSTEM,
1408
+ text: `Failed to parse JSON${fieldName ? ` for field "${fieldName}"` : ""}: ${error$1 instanceof Error ? error$1.message : "Unknown error"}`,
1409
+ details
1410
+ },
1411
+ error$1
1412
+ );
1413
+ }
1414
+ }
1415
+ parseRow(row) {
1416
+ return {
1417
+ id: row.id,
1418
+ status: row.status,
1419
+ activeVersionId: row.activeVersionId,
1420
+ authorId: row.authorId,
1421
+ metadata: this.parseJson(row.metadata, "metadata"),
1422
+ createdAt: row.createdAtZ || row.createdAt,
1423
+ updatedAt: row.updatedAtZ || row.updatedAt
1424
+ };
1425
+ }
1426
+ async getById(id) {
1427
+ try {
1428
+ const tableName = getTableName2({ indexName: storage.TABLE_AGENTS, schemaName: getSchemaName2(this.#schema) });
1429
+ const result = await this.#db.client.oneOrNone(`SELECT * FROM ${tableName} WHERE id = $1`, [id]);
1430
+ if (!result) {
1431
+ return null;
1432
+ }
1433
+ return this.parseRow(result);
1434
+ } catch (error$1) {
1435
+ if (error$1 instanceof error.MastraError) throw error$1;
1436
+ throw new error.MastraError(
1437
+ {
1438
+ id: storage.createStorageErrorId("DSQL", "GET_AGENT_BY_ID", "FAILED"),
1439
+ domain: error.ErrorDomain.STORAGE,
1440
+ category: error.ErrorCategory.THIRD_PARTY,
1441
+ details: { agentId: id }
1442
+ },
1443
+ error$1
1444
+ );
1445
+ }
1446
+ }
1447
+ async create(input) {
1448
+ const { agent } = input;
1449
+ const tableName = getTableName2({ indexName: storage.TABLE_AGENTS, schemaName: getSchemaName2(this.#schema) });
1450
+ const now = /* @__PURE__ */ new Date();
1451
+ const nowIso = now.toISOString();
1452
+ try {
1453
+ await withRetry(
1454
+ () => this.#db.client.none(
1455
+ `INSERT INTO ${tableName} (
1456
+ id, status, "authorId", metadata,
1457
+ "activeVersionId",
1458
+ "createdAt", "createdAtZ", "updatedAt", "updatedAtZ"
1459
+ ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`,
1460
+ [
1461
+ agent.id,
1462
+ "draft",
1463
+ agent.authorId ?? null,
1464
+ agent.metadata ? JSON.stringify(agent.metadata) : null,
1465
+ null,
1466
+ // activeVersionId starts as null
1467
+ nowIso,
1468
+ nowIso,
1469
+ nowIso,
1470
+ nowIso
1471
+ ]
1472
+ )
1473
+ );
1474
+ const { id: _id, authorId: _authorId, metadata: _metadata, ...snapshotConfig } = agent;
1475
+ const versionId = crypto.randomUUID();
1476
+ await this.createVersion({
1477
+ id: versionId,
1478
+ agentId: agent.id,
1479
+ versionNumber: 1,
1480
+ ...snapshotConfig,
1481
+ changedFields: Object.keys(snapshotConfig),
1482
+ changeMessage: "Initial version"
1483
+ });
1484
+ return {
1485
+ id: agent.id,
1486
+ status: "draft",
1487
+ activeVersionId: void 0,
1488
+ authorId: agent.authorId,
1489
+ metadata: agent.metadata,
1490
+ createdAt: now,
1491
+ updatedAt: now
1492
+ };
1493
+ } catch (error$1) {
1494
+ if (error$1 instanceof error.MastraError) throw error$1;
1495
+ try {
1496
+ await this.#db.client.none(
1497
+ `DELETE FROM ${tableName} WHERE id = $1 AND status = 'draft' AND "activeVersionId" IS NULL`,
1498
+ [agent.id]
1499
+ );
1500
+ } catch {
1501
+ }
1502
+ throw new error.MastraError(
1503
+ {
1504
+ id: storage.createStorageErrorId("DSQL", "CREATE_AGENT", "FAILED"),
1505
+ domain: error.ErrorDomain.STORAGE,
1506
+ category: error.ErrorCategory.THIRD_PARTY,
1507
+ details: { agentId: agent.id }
1508
+ },
1509
+ error$1
1510
+ );
1511
+ }
1512
+ }
1513
+ async update(input) {
1514
+ const { id, ...updates } = input;
1515
+ try {
1516
+ const tableName = getTableName2({ indexName: storage.TABLE_AGENTS, schemaName: getSchemaName2(this.#schema) });
1517
+ const existingAgent = await this.getById(id);
1518
+ if (!existingAgent) {
1519
+ throw new error.MastraError({
1520
+ id: storage.createStorageErrorId("DSQL", "UPDATE_AGENT", "NOT_FOUND"),
1521
+ domain: error.ErrorDomain.STORAGE,
1522
+ category: error.ErrorCategory.USER,
1523
+ text: `Agent ${id} not found`,
1524
+ details: { agentId: id }
1525
+ });
1526
+ }
1527
+ const { authorId, activeVersionId, metadata, status } = updates;
1528
+ const setClauses = [];
1529
+ const values = [];
1530
+ let paramIndex = 1;
1531
+ if (authorId !== void 0) {
1532
+ setClauses.push(`"authorId" = $${paramIndex++}`);
1533
+ values.push(authorId);
1534
+ }
1535
+ if (activeVersionId !== void 0) {
1536
+ setClauses.push(`"activeVersionId" = $${paramIndex++}`);
1537
+ values.push(activeVersionId);
1538
+ }
1539
+ if (status !== void 0) {
1540
+ setClauses.push(`status = $${paramIndex++}`);
1541
+ values.push(status);
1542
+ }
1543
+ if (metadata !== void 0) {
1544
+ setClauses.push(`metadata = $${paramIndex++}`);
1545
+ values.push(JSON.stringify(metadata));
1546
+ }
1547
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1548
+ setClauses.push(`"updatedAt" = $${paramIndex++}`);
1549
+ values.push(now);
1550
+ setClauses.push(`"updatedAtZ" = $${paramIndex++}`);
1551
+ values.push(now);
1552
+ values.push(id);
1553
+ await withRetry(
1554
+ () => this.#db.client.none(`UPDATE ${tableName} SET ${setClauses.join(", ")} WHERE id = ${paramIndex}`, values)
1555
+ );
1556
+ const updatedAgent = await this.getById(id);
1557
+ if (!updatedAgent) {
1558
+ throw new error.MastraError({
1559
+ id: storage.createStorageErrorId("DSQL", "UPDATE_AGENT", "NOT_FOUND_AFTER_UPDATE"),
1560
+ domain: error.ErrorDomain.STORAGE,
1561
+ category: error.ErrorCategory.SYSTEM,
1562
+ text: `Agent ${id} not found after update`,
1563
+ details: { agentId: id }
1564
+ });
1565
+ }
1566
+ return updatedAgent;
1567
+ } catch (error$1) {
1568
+ if (error$1 instanceof error.MastraError) throw error$1;
1569
+ throw new error.MastraError(
1570
+ {
1571
+ id: storage.createStorageErrorId("DSQL", "UPDATE_AGENT", "FAILED"),
1572
+ domain: error.ErrorDomain.STORAGE,
1573
+ category: error.ErrorCategory.THIRD_PARTY,
1574
+ details: { agentId: id }
1575
+ },
1576
+ error$1
1577
+ );
1578
+ }
1579
+ }
1580
+ async delete(id) {
1581
+ try {
1582
+ const tableName = getTableName2({ indexName: storage.TABLE_AGENTS, schemaName: getSchemaName2(this.#schema) });
1583
+ await this.deleteVersionsByParentId(id);
1584
+ await this.#db.client.none(`DELETE FROM ${tableName} WHERE id = $1`, [id]);
1585
+ } catch (error$1) {
1586
+ if (error$1 instanceof error.MastraError) throw error$1;
1587
+ throw new error.MastraError(
1588
+ {
1589
+ id: storage.createStorageErrorId("DSQL", "DELETE_AGENT", "FAILED"),
1590
+ domain: error.ErrorDomain.STORAGE,
1591
+ category: error.ErrorCategory.THIRD_PARTY,
1592
+ details: { agentId: id }
1593
+ },
1594
+ error$1
1595
+ );
1596
+ }
1597
+ }
1598
+ async list(args) {
1599
+ const { page = 0, perPage: perPageInput, orderBy, authorId, metadata, status } = args || {};
1600
+ const { field, direction } = this.parseOrderBy(orderBy);
1601
+ if (page < 0) {
1602
+ throw new error.MastraError(
1603
+ {
1604
+ id: storage.createStorageErrorId("DSQL", "LIST_AGENTS", "INVALID_PAGE"),
1605
+ domain: error.ErrorDomain.STORAGE,
1606
+ category: error.ErrorCategory.USER,
1607
+ details: { page }
1608
+ },
1609
+ new Error("page must be >= 0")
1610
+ );
1611
+ }
1612
+ const perPage = storage.normalizePerPage(perPageInput, 100);
1613
+ const { offset, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
1614
+ try {
1615
+ const tableName = getTableName2({ indexName: storage.TABLE_AGENTS, schemaName: getSchemaName2(this.#schema) });
1616
+ const conditions = [];
1617
+ const queryParams = [];
1618
+ let paramIdx = 1;
1619
+ if (status) {
1620
+ conditions.push(`status = $${paramIdx++}`);
1621
+ queryParams.push(status);
1622
+ }
1623
+ if (authorId !== void 0) {
1624
+ conditions.push(`"authorId" = $${paramIdx++}`);
1625
+ queryParams.push(authorId);
1626
+ }
1627
+ if (metadata && Object.keys(metadata).length > 0) {
1628
+ conditions.push(`metadata::text = $${paramIdx++}`);
1629
+ queryParams.push(JSON.stringify(metadata));
1630
+ }
1631
+ const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
1632
+ const countResult = await this.#db.client.one(
1633
+ `SELECT COUNT(*) as count FROM ${tableName} ${whereClause}`,
1634
+ queryParams
1635
+ );
1636
+ const total = parseInt(countResult.count, 10);
1637
+ if (total === 0) {
1638
+ return {
1639
+ agents: [],
1640
+ total: 0,
1641
+ page,
1642
+ perPage: perPageForResponse,
1643
+ hasMore: false
1644
+ };
1645
+ }
1646
+ const limitValue = perPageInput === false ? total : perPage;
1647
+ const dataResult = await this.#db.client.manyOrNone(
1648
+ `SELECT * FROM ${tableName} ${whereClause} ORDER BY "${field}" ${direction} LIMIT $${paramIdx++} OFFSET $${paramIdx++}`,
1649
+ [...queryParams, limitValue, offset]
1650
+ );
1651
+ const agents = (dataResult || []).map((row) => this.parseRow(row));
1652
+ return {
1653
+ agents,
1654
+ total,
1655
+ page,
1656
+ perPage: perPageForResponse,
1657
+ hasMore: perPageInput === false ? false : offset + perPage < total
1658
+ };
1659
+ } catch (error$1) {
1660
+ if (error$1 instanceof error.MastraError) throw error$1;
1661
+ throw new error.MastraError(
1662
+ {
1663
+ id: storage.createStorageErrorId("DSQL", "LIST_AGENTS", "FAILED"),
1664
+ domain: error.ErrorDomain.STORAGE,
1665
+ category: error.ErrorCategory.THIRD_PARTY
1666
+ },
1667
+ error$1
1668
+ );
1669
+ }
1670
+ }
1671
+ // ==========================================================================
1672
+ // Agent Version Methods
1673
+ // ==========================================================================
1674
+ async createVersion(input) {
1675
+ try {
1676
+ const tableName = getTableName2({ indexName: storage.TABLE_AGENT_VERSIONS, schemaName: getSchemaName2(this.#schema) });
1677
+ const now = /* @__PURE__ */ new Date();
1678
+ const nowIso = now.toISOString();
1679
+ await withRetry(
1680
+ () => this.#db.client.none(
1681
+ `INSERT INTO ${tableName} (
1682
+ id, "agentId", "versionNumber",
1683
+ name, description, instructions, model, tools,
1684
+ "defaultOptions", workflows, agents, "integrationTools",
1685
+ "inputProcessors", "outputProcessors", memory, scorers,
1686
+ "mcpClients", "requestContextSchema", workspace, skills, "skillsFormat",
1687
+ "changedFields", "changeMessage",
1688
+ "createdAt", "createdAtZ"
1689
+ ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25)`,
1690
+ [
1691
+ input.id,
1692
+ input.agentId,
1693
+ input.versionNumber,
1694
+ input.name,
1695
+ input.description ?? null,
1696
+ this.serializeInstructions(input.instructions),
1697
+ JSON.stringify(input.model),
1698
+ input.tools ? JSON.stringify(input.tools) : null,
1699
+ input.defaultOptions ? JSON.stringify(input.defaultOptions) : null,
1700
+ input.workflows ? JSON.stringify(input.workflows) : null,
1701
+ input.agents ? JSON.stringify(input.agents) : null,
1702
+ input.integrationTools ? JSON.stringify(input.integrationTools) : null,
1703
+ input.inputProcessors ? JSON.stringify(input.inputProcessors) : null,
1704
+ input.outputProcessors ? JSON.stringify(input.outputProcessors) : null,
1705
+ input.memory ? JSON.stringify(input.memory) : null,
1706
+ input.scorers ? JSON.stringify(input.scorers) : null,
1707
+ input.mcpClients ? JSON.stringify(input.mcpClients) : null,
1708
+ input.requestContextSchema ? JSON.stringify(input.requestContextSchema) : null,
1709
+ input.workspace ? JSON.stringify(input.workspace) : null,
1710
+ input.skills ? JSON.stringify(input.skills) : null,
1711
+ input.skillsFormat ?? null,
1712
+ input.changedFields ? JSON.stringify(input.changedFields) : null,
1713
+ input.changeMessage ?? null,
1714
+ nowIso,
1715
+ nowIso
1716
+ ]
1717
+ )
1718
+ );
1719
+ return {
1720
+ ...input,
1721
+ createdAt: now
1722
+ };
1723
+ } catch (error$1) {
1724
+ if (error$1 instanceof error.MastraError) throw error$1;
1725
+ throw new error.MastraError(
1726
+ {
1727
+ id: storage.createStorageErrorId("DSQL", "CREATE_VERSION", "FAILED"),
1728
+ domain: error.ErrorDomain.STORAGE,
1729
+ category: error.ErrorCategory.THIRD_PARTY,
1730
+ details: { versionId: input.id, agentId: input.agentId }
1731
+ },
1732
+ error$1
1733
+ );
1734
+ }
1735
+ }
1736
+ async getVersion(id) {
1737
+ try {
1738
+ const tableName = getTableName2({ indexName: storage.TABLE_AGENT_VERSIONS, schemaName: getSchemaName2(this.#schema) });
1739
+ const result = await this.#db.client.oneOrNone(`SELECT * FROM ${tableName} WHERE id = $1`, [id]);
1740
+ if (!result) {
1741
+ return null;
1742
+ }
1743
+ return this.parseVersionRow(result);
1744
+ } catch (error$1) {
1745
+ if (error$1 instanceof error.MastraError) throw error$1;
1746
+ throw new error.MastraError(
1747
+ {
1748
+ id: storage.createStorageErrorId("DSQL", "GET_VERSION", "FAILED"),
1749
+ domain: error.ErrorDomain.STORAGE,
1750
+ category: error.ErrorCategory.THIRD_PARTY,
1751
+ details: { versionId: id }
1752
+ },
1753
+ error$1
1754
+ );
1755
+ }
1756
+ }
1757
+ async getVersionByNumber(agentId, versionNumber) {
1758
+ try {
1759
+ const tableName = getTableName2({ indexName: storage.TABLE_AGENT_VERSIONS, schemaName: getSchemaName2(this.#schema) });
1760
+ const result = await this.#db.client.oneOrNone(
1761
+ `SELECT * FROM ${tableName} WHERE "agentId" = $1 AND "versionNumber" = $2`,
1762
+ [agentId, versionNumber]
1763
+ );
1764
+ if (!result) {
1765
+ return null;
1766
+ }
1767
+ return this.parseVersionRow(result);
1768
+ } catch (error$1) {
1769
+ if (error$1 instanceof error.MastraError) throw error$1;
1770
+ throw new error.MastraError(
1771
+ {
1772
+ id: storage.createStorageErrorId("DSQL", "GET_VERSION_BY_NUMBER", "FAILED"),
1773
+ domain: error.ErrorDomain.STORAGE,
1774
+ category: error.ErrorCategory.THIRD_PARTY,
1775
+ details: { agentId, versionNumber }
1776
+ },
1777
+ error$1
1778
+ );
1779
+ }
1780
+ }
1781
+ async getLatestVersion(agentId) {
1782
+ try {
1783
+ const tableName = getTableName2({ indexName: storage.TABLE_AGENT_VERSIONS, schemaName: getSchemaName2(this.#schema) });
1784
+ const result = await this.#db.client.oneOrNone(
1785
+ `SELECT * FROM ${tableName} WHERE "agentId" = $1 ORDER BY "versionNumber" DESC LIMIT 1`,
1786
+ [agentId]
1787
+ );
1788
+ if (!result) {
1789
+ return null;
1790
+ }
1791
+ return this.parseVersionRow(result);
1792
+ } catch (error$1) {
1793
+ if (error$1 instanceof error.MastraError) throw error$1;
1794
+ throw new error.MastraError(
1795
+ {
1796
+ id: storage.createStorageErrorId("DSQL", "GET_LATEST_VERSION", "FAILED"),
1797
+ domain: error.ErrorDomain.STORAGE,
1798
+ category: error.ErrorCategory.THIRD_PARTY,
1799
+ details: { agentId }
1800
+ },
1801
+ error$1
1802
+ );
1803
+ }
1804
+ }
1805
+ async listVersions(input) {
1806
+ const { agentId, page = 0, perPage: perPageInput, orderBy } = input;
1807
+ const perPage = storage.normalizePerPage(perPageInput, 100);
1808
+ const { offset, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
1809
+ const sortField = orderBy?.field || "versionNumber";
1810
+ const sortDirection = orderBy?.direction || "DESC";
1811
+ try {
1812
+ const tableName = getTableName2({ indexName: storage.TABLE_AGENT_VERSIONS, schemaName: getSchemaName2(this.#schema) });
1813
+ const countResult = await this.#db.client.one(`SELECT COUNT(*) as count FROM ${tableName} WHERE "agentId" = $1`, [
1814
+ agentId
1815
+ ]);
1816
+ const total = parseInt(countResult.count, 10);
1817
+ if (total === 0) {
1818
+ return {
1819
+ versions: [],
1820
+ total: 0,
1821
+ page,
1822
+ perPage: perPageForResponse,
1823
+ hasMore: false
1824
+ };
1825
+ }
1826
+ const limitValue = perPageInput === false ? total : perPage;
1827
+ const rows = await this.#db.client.manyOrNone(
1828
+ `SELECT * FROM ${tableName} WHERE "agentId" = $1 ORDER BY "${sortField}" ${sortDirection} LIMIT $2 OFFSET $3`,
1829
+ [agentId, limitValue, offset]
1830
+ );
1831
+ const versions = (rows || []).map((row) => this.parseVersionRow(row));
1832
+ return {
1833
+ versions,
1834
+ total,
1835
+ page,
1836
+ perPage: perPageForResponse,
1837
+ hasMore: perPageInput === false ? false : offset + perPage < total
1838
+ };
1839
+ } catch (error$1) {
1840
+ if (error$1 instanceof error.MastraError) throw error$1;
1841
+ throw new error.MastraError(
1842
+ {
1843
+ id: storage.createStorageErrorId("DSQL", "LIST_VERSIONS", "FAILED"),
1844
+ domain: error.ErrorDomain.STORAGE,
1845
+ category: error.ErrorCategory.THIRD_PARTY,
1846
+ details: { agentId }
1847
+ },
1848
+ error$1
1849
+ );
1850
+ }
1851
+ }
1852
+ async deleteVersion(id) {
1853
+ try {
1854
+ const tableName = getTableName2({ indexName: storage.TABLE_AGENT_VERSIONS, schemaName: getSchemaName2(this.#schema) });
1855
+ await this.#db.client.none(`DELETE FROM ${tableName} WHERE id = $1`, [id]);
1856
+ } catch (error$1) {
1857
+ if (error$1 instanceof error.MastraError) throw error$1;
1858
+ throw new error.MastraError(
1859
+ {
1860
+ id: storage.createStorageErrorId("DSQL", "DELETE_VERSION", "FAILED"),
1861
+ domain: error.ErrorDomain.STORAGE,
1862
+ category: error.ErrorCategory.THIRD_PARTY,
1863
+ details: { versionId: id }
1864
+ },
1865
+ error$1
1866
+ );
1867
+ }
1868
+ }
1869
+ async deleteVersionsByParentId(agentId) {
1870
+ try {
1871
+ const tableName = getTableName2({ indexName: storage.TABLE_AGENT_VERSIONS, schemaName: getSchemaName2(this.#schema) });
1872
+ await this.#db.client.none(`DELETE FROM ${tableName} WHERE "agentId" = $1`, [agentId]);
1873
+ } catch (error$1) {
1874
+ if (error$1 instanceof error.MastraError) throw error$1;
1875
+ throw new error.MastraError(
1876
+ {
1877
+ id: storage.createStorageErrorId("DSQL", "DELETE_VERSIONS_BY_PARENT", "FAILED"),
1878
+ domain: error.ErrorDomain.STORAGE,
1879
+ category: error.ErrorCategory.THIRD_PARTY,
1880
+ details: { agentId }
1881
+ },
1882
+ error$1
1883
+ );
1884
+ }
1885
+ }
1886
+ async countVersions(agentId) {
1887
+ try {
1888
+ const tableName = getTableName2({ indexName: storage.TABLE_AGENT_VERSIONS, schemaName: getSchemaName2(this.#schema) });
1889
+ const result = await this.#db.client.one(`SELECT COUNT(*) as count FROM ${tableName} WHERE "agentId" = $1`, [
1890
+ agentId
1891
+ ]);
1892
+ return parseInt(result.count, 10);
1893
+ } catch (error$1) {
1894
+ if (error$1 instanceof error.MastraError) throw error$1;
1895
+ throw new error.MastraError(
1896
+ {
1897
+ id: storage.createStorageErrorId("DSQL", "COUNT_VERSIONS", "FAILED"),
1898
+ domain: error.ErrorDomain.STORAGE,
1899
+ category: error.ErrorCategory.THIRD_PARTY,
1900
+ details: { agentId }
1901
+ },
1902
+ error$1
1903
+ );
1904
+ }
1905
+ }
1906
+ // ==========================================================================
1907
+ // Private Helpers
1908
+ // ==========================================================================
1909
+ serializeInstructions(instructions) {
1910
+ if (instructions == null) return void 0;
1911
+ return Array.isArray(instructions) ? JSON.stringify(instructions) : instructions;
1912
+ }
1913
+ deserializeInstructions(raw) {
1914
+ if (!raw) return "";
1915
+ try {
1916
+ const parsed = JSON.parse(raw);
1917
+ if (Array.isArray(parsed)) return parsed;
1918
+ } catch {
1919
+ }
1920
+ return raw;
1921
+ }
1922
+ parseVersionRow(row) {
1923
+ return {
1924
+ id: row.id,
1925
+ agentId: row.agentId,
1926
+ versionNumber: row.versionNumber,
1927
+ name: row.name,
1928
+ description: row.description,
1929
+ instructions: this.deserializeInstructions(row.instructions),
1930
+ model: this.parseJson(row.model, "model"),
1931
+ tools: this.parseJson(row.tools, "tools"),
1932
+ defaultOptions: this.parseJson(row.defaultOptions, "defaultOptions"),
1933
+ workflows: this.parseJson(row.workflows, "workflows"),
1934
+ agents: this.parseJson(row.agents, "agents"),
1935
+ integrationTools: this.parseJson(row.integrationTools, "integrationTools"),
1936
+ inputProcessors: this.parseJson(row.inputProcessors, "inputProcessors"),
1937
+ outputProcessors: this.parseJson(row.outputProcessors, "outputProcessors"),
1938
+ memory: this.parseJson(row.memory, "memory"),
1939
+ scorers: this.parseJson(row.scorers, "scorers"),
1940
+ mcpClients: this.parseJson(row.mcpClients, "mcpClients"),
1941
+ requestContextSchema: this.parseJson(row.requestContextSchema, "requestContextSchema"),
1942
+ workspace: this.parseJson(row.workspace, "workspace"),
1943
+ skills: this.parseJson(row.skills, "skills"),
1944
+ skillsFormat: row.skillsFormat,
1945
+ changedFields: this.parseJson(row.changedFields, "changedFields"),
1946
+ changeMessage: row.changeMessage,
1947
+ createdAt: row.createdAtZ || row.createdAt
1948
+ };
1949
+ }
1950
+ };
1951
+ function inPlaceholders(count, startIndex = 1) {
1952
+ return Array.from({ length: count }, (_, i) => `$${i + startIndex}`).join(", ");
1953
+ }
1954
+ var MemoryDSQL = class _MemoryDSQL extends storage.MemoryStorage {
1955
+ #db;
1956
+ #schema;
1957
+ #skipDefaultIndexes;
1958
+ #indexes;
1959
+ /** Tables managed by this domain */
1960
+ static MANAGED_TABLES = [storage.TABLE_THREADS, storage.TABLE_MESSAGES, storage.TABLE_RESOURCES];
1961
+ constructor(config) {
1962
+ super();
1963
+ const { client, schemaName, skipDefaultIndexes, indexes } = resolveDsqlConfig(config);
1964
+ this.#db = new DsqlDB({ client, schemaName, skipDefaultIndexes });
1965
+ this.#schema = schemaName || "public";
1966
+ this.#skipDefaultIndexes = skipDefaultIndexes;
1967
+ this.#indexes = indexes?.filter((idx) => _MemoryDSQL.MANAGED_TABLES.includes(idx.table));
1968
+ }
1969
+ async init() {
1970
+ await this.#db.createTable({ tableName: storage.TABLE_THREADS, schema: storage.TABLE_SCHEMAS[storage.TABLE_THREADS] });
1971
+ await this.#db.createTable({ tableName: storage.TABLE_MESSAGES, schema: storage.TABLE_SCHEMAS[storage.TABLE_MESSAGES] });
1972
+ await this.#db.createTable({ tableName: storage.TABLE_RESOURCES, schema: storage.TABLE_SCHEMAS[storage.TABLE_RESOURCES] });
1973
+ await this.#db.alterTable({
1974
+ tableName: storage.TABLE_MESSAGES,
1975
+ schema: storage.TABLE_SCHEMAS[storage.TABLE_MESSAGES],
1976
+ ifNotExists: ["resourceId"]
1977
+ });
1978
+ await this.createDefaultIndexes();
1979
+ await this.createCustomIndexes();
1980
+ }
1981
+ /**
1982
+ * Returns default index definitions for the memory domain tables.
1983
+ * Note: Aurora DSQL does not support ASC/DESC in index columns.
1984
+ */
1985
+ getDefaultIndexDefinitions() {
1986
+ const schemaPrefix = this.#schema !== "public" ? `${this.#schema}_` : "";
1987
+ return [
1988
+ {
1989
+ name: `${schemaPrefix}mastra_threads_resourceid_createdat_idx`,
1990
+ table: storage.TABLE_THREADS,
1991
+ columns: ["resourceId", "createdAt"]
1992
+ },
1993
+ {
1994
+ name: `${schemaPrefix}mastra_messages_thread_id_createdat_idx`,
1995
+ table: storage.TABLE_MESSAGES,
1996
+ columns: ["thread_id", "createdAt"]
1997
+ }
1998
+ ];
1999
+ }
2000
+ /**
2001
+ * Creates default indexes for optimal query performance.
2002
+ */
2003
+ async createDefaultIndexes() {
2004
+ if (this.#skipDefaultIndexes) {
2005
+ return;
2006
+ }
2007
+ for (const indexDef of this.getDefaultIndexDefinitions()) {
2008
+ try {
2009
+ await this.#db.createIndex(indexDef);
2010
+ } catch (error) {
2011
+ this.logger?.warn?.(`Failed to create index ${indexDef.name}:`, error);
2012
+ }
2013
+ }
2014
+ }
2015
+ /**
2016
+ * Creates custom user-defined indexes for this domain's tables.
2017
+ */
2018
+ async createCustomIndexes() {
2019
+ if (!this.#indexes || this.#indexes.length === 0) {
2020
+ return;
2021
+ }
2022
+ for (const indexDef of this.#indexes) {
2023
+ try {
2024
+ await this.#db.createIndex(indexDef);
2025
+ } catch (error) {
2026
+ this.logger?.warn?.(`Failed to create custom index ${indexDef.name}:`, error);
2027
+ }
2028
+ }
2029
+ }
2030
+ async dangerouslyClearAll() {
2031
+ await this.#db.clearTable({ tableName: storage.TABLE_MESSAGES });
2032
+ await this.#db.clearTable({ tableName: storage.TABLE_THREADS });
2033
+ await this.#db.clearTable({ tableName: storage.TABLE_RESOURCES });
2034
+ }
2035
+ /**
2036
+ * Normalizes message row from database by applying createdAtZ fallback
2037
+ */
2038
+ normalizeMessageRow(row) {
2039
+ return {
2040
+ id: row.id,
2041
+ content: row.content,
2042
+ role: row.role,
2043
+ type: row.type,
2044
+ createdAt: row.createdAtZ || row.createdAt,
2045
+ threadId: row.threadId,
2046
+ resourceId: row.resourceId
2047
+ };
2048
+ }
2049
+ async getThreadById({ threadId }) {
2050
+ try {
2051
+ const tableName = getTableName2({ indexName: storage.TABLE_THREADS, schemaName: getSchemaName2(this.#schema) });
2052
+ const thread = await this.#db.client.oneOrNone(
2053
+ `SELECT * FROM ${tableName} WHERE id = $1`,
2054
+ [threadId]
2055
+ );
2056
+ if (!thread) {
2057
+ return null;
2058
+ }
2059
+ return {
2060
+ id: thread.id,
2061
+ resourceId: thread.resourceId,
2062
+ title: thread.title,
2063
+ metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
2064
+ createdAt: thread.createdAtZ || thread.createdAt,
2065
+ updatedAt: thread.updatedAtZ || thread.updatedAt
2066
+ };
2067
+ } catch (error$1) {
2068
+ throw new error.MastraError(
2069
+ {
2070
+ id: storage.createStorageErrorId("DSQL", "GET_THREAD_BY_ID", "FAILED"),
2071
+ domain: error.ErrorDomain.STORAGE,
2072
+ category: error.ErrorCategory.THIRD_PARTY,
2073
+ details: {
2074
+ threadId
2075
+ }
2076
+ },
2077
+ error$1
2078
+ );
2079
+ }
2080
+ }
2081
+ async listThreads(args) {
2082
+ const { page = 0, perPage: perPageInput, orderBy, filter } = args;
2083
+ try {
2084
+ this.validatePaginationInput(page, perPageInput ?? 100);
2085
+ } catch (error$1) {
2086
+ throw new error.MastraError({
2087
+ id: storage.createStorageErrorId("DSQL", "LIST_THREADS", "INVALID_PAGE"),
2088
+ domain: error.ErrorDomain.STORAGE,
2089
+ category: error.ErrorCategory.USER,
2090
+ text: error$1 instanceof Error ? error$1.message : "Invalid pagination parameters",
2091
+ details: { page, ...perPageInput !== void 0 && { perPage: perPageInput } }
2092
+ });
2093
+ }
2094
+ const perPage = storage.normalizePerPage(perPageInput, 100);
2095
+ try {
2096
+ this.validateMetadataKeys(filter?.metadata);
2097
+ } catch (error$1) {
2098
+ throw new error.MastraError({
2099
+ id: storage.createStorageErrorId("DSQL", "LIST_THREADS", "INVALID_METADATA_KEY"),
2100
+ domain: error.ErrorDomain.STORAGE,
2101
+ category: error.ErrorCategory.USER,
2102
+ text: error$1 instanceof Error ? error$1.message : "Invalid metadata key",
2103
+ details: { metadataKeys: filter?.metadata ? Object.keys(filter.metadata).join(", ") : "" }
2104
+ });
2105
+ }
2106
+ const { field, direction } = this.parseOrderBy(orderBy);
2107
+ const { offset, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
2108
+ try {
2109
+ const tableName = getTableName2({ indexName: storage.TABLE_THREADS, schemaName: getSchemaName2(this.#schema) });
2110
+ const whereClauses = [];
2111
+ const queryParams = [];
2112
+ let paramIndex = 1;
2113
+ if (filter?.resourceId) {
2114
+ whereClauses.push(`"resourceId" = ${paramIndex}`);
2115
+ queryParams.push(filter.resourceId);
2116
+ paramIndex++;
2117
+ }
2118
+ if (filter?.metadata && Object.keys(filter.metadata).length > 0) {
2119
+ for (const [key, value] of Object.entries(filter.metadata)) {
2120
+ whereClauses.push(`metadata::jsonb @> ${paramIndex}::jsonb`);
2121
+ queryParams.push(JSON.stringify({ [key]: value }));
2122
+ paramIndex++;
2123
+ }
2124
+ }
2125
+ const whereClause = whereClauses.length > 0 ? `WHERE ${whereClauses.join(" AND ")}` : "";
2126
+ const baseQuery = `FROM ${tableName} ${whereClause}`;
2127
+ const countQuery = `SELECT COUNT(*) ${baseQuery}`;
2128
+ const countResult = await this.#db.client.one(countQuery, queryParams);
2129
+ const total = parseInt(countResult.count, 10);
2130
+ if (total === 0) {
2131
+ return {
2132
+ threads: [],
2133
+ total: 0,
2134
+ page,
2135
+ perPage: perPageForResponse,
2136
+ hasMore: false
2137
+ };
2138
+ }
2139
+ const limitValue = perPageInput === false ? total : perPage;
2140
+ const dataQuery = `SELECT id, "resourceId", title, metadata, "createdAt", "createdAtZ", "updatedAt", "updatedAtZ" ${baseQuery} ORDER BY "${field}" ${direction} LIMIT ${paramIndex} OFFSET ${paramIndex + 1}`;
2141
+ const rows = await this.#db.client.manyOrNone(
2142
+ dataQuery,
2143
+ [...queryParams, limitValue, offset]
2144
+ );
2145
+ const threads = (rows || []).map((thread) => ({
2146
+ id: thread.id,
2147
+ resourceId: thread.resourceId,
2148
+ title: thread.title,
2149
+ metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
2150
+ createdAt: thread.createdAtZ || thread.createdAt,
2151
+ updatedAt: thread.updatedAtZ || thread.updatedAt
2152
+ }));
2153
+ return {
2154
+ threads,
2155
+ total,
2156
+ page,
2157
+ perPage: perPageForResponse,
2158
+ hasMore: perPageInput === false ? false : offset + perPage < total
2159
+ };
2160
+ } catch (error$1) {
2161
+ const mastraError = new error.MastraError(
2162
+ {
2163
+ id: storage.createStorageErrorId("DSQL", "LIST_THREADS", "FAILED"),
2164
+ domain: error.ErrorDomain.STORAGE,
2165
+ category: error.ErrorCategory.THIRD_PARTY,
2166
+ details: {
2167
+ ...filter?.resourceId && { resourceId: filter.resourceId },
2168
+ hasMetadataFilter: !!filter?.metadata,
2169
+ page
2170
+ }
2171
+ },
2172
+ error$1
2173
+ );
2174
+ this.logger?.error?.(mastraError.toString());
2175
+ this.logger?.trackException(mastraError);
2176
+ return {
2177
+ threads: [],
2178
+ total: 0,
2179
+ page,
2180
+ perPage: perPageForResponse,
2181
+ hasMore: false
2182
+ };
2183
+ }
2184
+ }
2185
+ async saveThread({ thread }) {
2186
+ const tableName = getTableName2({ indexName: storage.TABLE_THREADS, schemaName: getSchemaName2(this.#schema) });
2187
+ await withRetry(
2188
+ async () => {
2189
+ await this.#db.client.none(
2190
+ `INSERT INTO ${tableName} (
2191
+ id,
2192
+ "resourceId",
2193
+ title,
2194
+ metadata,
2195
+ "createdAt",
2196
+ "createdAtZ",
2197
+ "updatedAt",
2198
+ "updatedAtZ"
2199
+ ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
2200
+ ON CONFLICT (id) DO UPDATE SET
2201
+ "resourceId" = EXCLUDED."resourceId",
2202
+ title = EXCLUDED.title,
2203
+ metadata = EXCLUDED.metadata,
2204
+ "createdAt" = EXCLUDED."createdAt",
2205
+ "createdAtZ" = EXCLUDED."createdAtZ",
2206
+ "updatedAt" = EXCLUDED."updatedAt",
2207
+ "updatedAtZ" = EXCLUDED."updatedAtZ"`,
2208
+ [
2209
+ thread.id,
2210
+ thread.resourceId,
2211
+ thread.title,
2212
+ thread.metadata ? JSON.stringify(thread.metadata) : null,
2213
+ thread.createdAt,
2214
+ thread.createdAt,
2215
+ thread.updatedAt,
2216
+ thread.updatedAt
2217
+ ]
2218
+ );
2219
+ },
2220
+ {
2221
+ onRetry: (error, attempt, delay) => {
2222
+ this.logger?.warn?.(`saveThread retry ${attempt} for ${thread.id} after ${delay}ms: ${error.message}`);
2223
+ }
2224
+ }
2225
+ ).catch((error$1) => {
2226
+ throw new error.MastraError(
2227
+ {
2228
+ id: storage.createStorageErrorId("DSQL", "SAVE_THREAD", "FAILED"),
2229
+ domain: error.ErrorDomain.STORAGE,
2230
+ category: error.ErrorCategory.THIRD_PARTY,
2231
+ details: {
2232
+ threadId: thread.id
2233
+ }
2234
+ },
2235
+ error$1
2236
+ );
2237
+ });
2238
+ return thread;
2239
+ }
2240
+ async updateThread({
2241
+ id,
2242
+ title,
2243
+ metadata
2244
+ }) {
2245
+ const threadTableName = getTableName2({ indexName: storage.TABLE_THREADS, schemaName: getSchemaName2(this.#schema) });
2246
+ const { result } = await withRetry(
2247
+ async () => {
2248
+ const existingThread = await this.getThreadById({ threadId: id });
2249
+ if (!existingThread) {
2250
+ throw new error.MastraError({
2251
+ id: storage.createStorageErrorId("DSQL", "UPDATE_THREAD", "NOT_FOUND"),
2252
+ domain: error.ErrorDomain.STORAGE,
2253
+ category: error.ErrorCategory.USER,
2254
+ text: `Thread ${id} not found`,
2255
+ details: {
2256
+ threadId: id,
2257
+ title
2258
+ }
2259
+ });
2260
+ }
2261
+ const mergedMetadata = {
2262
+ ...existingThread.metadata,
2263
+ ...metadata
2264
+ };
2265
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2266
+ const thread = await this.#db.client.one(
2267
+ `UPDATE ${threadTableName}
2268
+ SET
2269
+ title = $1,
2270
+ metadata = $2,
2271
+ "updatedAt" = $3::timestamp,
2272
+ "updatedAtZ" = $4::timestamptz
2273
+ WHERE id = $5
2274
+ RETURNING *
2275
+ `,
2276
+ [title, JSON.stringify(mergedMetadata), now, now, id]
2277
+ );
2278
+ return {
2279
+ id: thread.id,
2280
+ resourceId: thread.resourceId,
2281
+ title: thread.title,
2282
+ metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
2283
+ createdAt: thread.createdAtZ || thread.createdAt,
2284
+ updatedAt: thread.updatedAtZ || thread.updatedAt
2285
+ };
2286
+ },
2287
+ {
2288
+ onRetry: (error, attempt, delay) => {
2289
+ this.logger?.warn?.(`updateThread retry ${attempt} for ${id} after ${delay}ms: ${error.message}`);
2290
+ }
2291
+ }
2292
+ ).catch((error$1) => {
2293
+ if (error$1 instanceof error.MastraError) {
2294
+ throw error$1;
2295
+ }
2296
+ throw new error.MastraError(
2297
+ {
2298
+ id: storage.createStorageErrorId("DSQL", "UPDATE_THREAD", "FAILED"),
2299
+ domain: error.ErrorDomain.STORAGE,
2300
+ category: error.ErrorCategory.THIRD_PARTY,
2301
+ details: {
2302
+ threadId: id,
2303
+ title
2304
+ }
2305
+ },
2306
+ error$1
2307
+ );
2308
+ });
2309
+ return result;
2310
+ }
2311
+ async deleteThread({ threadId }) {
2312
+ const tableName = getTableName2({ indexName: storage.TABLE_MESSAGES, schemaName: getSchemaName2(this.#schema) });
2313
+ const threadTableName = getTableName2({ indexName: storage.TABLE_THREADS, schemaName: getSchemaName2(this.#schema) });
2314
+ await withRetry(
2315
+ async () => {
2316
+ await this.#db.client.tx(async (t) => {
2317
+ await t.none(`DELETE FROM ${tableName} WHERE thread_id = $1`, [threadId]);
2318
+ await t.none(`DELETE FROM ${threadTableName} WHERE id = $1`, [threadId]);
2319
+ });
2320
+ },
2321
+ {
2322
+ onRetry: (error, attempt, delay) => {
2323
+ this.logger?.warn?.(`deleteThread retry ${attempt} for ${threadId} after ${delay}ms: ${error.message}`);
2324
+ }
2325
+ }
2326
+ ).catch((error$1) => {
2327
+ throw new error.MastraError(
2328
+ {
2329
+ id: storage.createStorageErrorId("DSQL", "DELETE_THREAD", "FAILED"),
2330
+ domain: error.ErrorDomain.STORAGE,
2331
+ category: error.ErrorCategory.THIRD_PARTY,
2332
+ details: {
2333
+ threadId
2334
+ }
2335
+ },
2336
+ error$1
2337
+ );
2338
+ });
2339
+ }
2340
+ async _getIncludedMessages({ include }) {
2341
+ if (!include || include.length === 0) return null;
2342
+ const unionQueries = [];
2343
+ const params = [];
2344
+ let paramIdx = 1;
2345
+ const tableName = getTableName2({ indexName: storage.TABLE_MESSAGES, schemaName: getSchemaName2(this.#schema) });
2346
+ for (const inc of include) {
2347
+ const { id, withPreviousMessages = 0, withNextMessages = 0 } = inc;
2348
+ unionQueries.push(
2349
+ `
2350
+ SELECT * FROM (
2351
+ WITH target_thread AS (
2352
+ SELECT thread_id FROM ${tableName} WHERE id = $${paramIdx}
2353
+ ),
2354
+ ordered_messages AS (
2355
+ SELECT
2356
+ *,
2357
+ ROW_NUMBER() OVER (ORDER BY "createdAt" ASC) as row_num
2358
+ FROM ${tableName}
2359
+ WHERE thread_id = (SELECT thread_id FROM target_thread)
2360
+ )
2361
+ SELECT
2362
+ m.id,
2363
+ m.content,
2364
+ m.role,
2365
+ m.type,
2366
+ m."createdAt",
2367
+ m."createdAtZ",
2368
+ m.thread_id AS "threadId",
2369
+ m."resourceId"
2370
+ FROM ordered_messages m
2371
+ WHERE m.id = $${paramIdx}
2372
+ OR EXISTS (
2373
+ SELECT 1 FROM ordered_messages target
2374
+ WHERE target.id = $${paramIdx}
2375
+ AND (
2376
+ (m.row_num < target.row_num AND m.row_num >= target.row_num - $${paramIdx + 1})
2377
+ OR
2378
+ (m.row_num > target.row_num AND m.row_num <= target.row_num + $${paramIdx + 2})
2379
+ )
2380
+ )
2381
+ ) AS query_${paramIdx}
2382
+ `
2383
+ );
2384
+ params.push(id, withPreviousMessages, withNextMessages);
2385
+ paramIdx += 3;
2386
+ }
2387
+ const finalQuery = unionQueries.join(" UNION ALL ") + ' ORDER BY "createdAt" ASC';
2388
+ const includedRows = await this.#db.client.manyOrNone(finalQuery, params);
2389
+ const seen = /* @__PURE__ */ new Set();
2390
+ const dedupedRows = includedRows.filter((row) => {
2391
+ if (seen.has(row.id)) return false;
2392
+ seen.add(row.id);
2393
+ return true;
2394
+ });
2395
+ return dedupedRows;
2396
+ }
2397
+ parseRow(row) {
2398
+ const normalized = this.normalizeMessageRow(row);
2399
+ let content = normalized.content;
2400
+ try {
2401
+ content = JSON.parse(normalized.content);
2402
+ } catch {
2403
+ }
2404
+ return {
2405
+ id: normalized.id,
2406
+ content,
2407
+ role: normalized.role,
2408
+ createdAt: new Date(normalized.createdAt),
2409
+ threadId: normalized.threadId,
2410
+ resourceId: normalized.resourceId,
2411
+ ...normalized.type && normalized.type !== "v2" ? { type: normalized.type } : {}
2412
+ };
2413
+ }
2414
+ async listMessagesById({ messageIds }) {
2415
+ if (messageIds.length === 0) return { messages: [] };
2416
+ const selectStatement = `SELECT id, content, role, type, "createdAt", "createdAtZ", thread_id AS "threadId", "resourceId"`;
2417
+ try {
2418
+ const tableName = getTableName2({ indexName: storage.TABLE_MESSAGES, schemaName: getSchemaName2(this.#schema) });
2419
+ const query = `
2420
+ ${selectStatement} FROM ${tableName}
2421
+ WHERE id IN (${inPlaceholders(messageIds.length)})
2422
+ ORDER BY "createdAt" DESC
2423
+ `;
2424
+ const resultRows = await this.#db.client.manyOrNone(query, messageIds);
2425
+ const list = new agent.MessageList().add(
2426
+ resultRows.map((row) => this.parseRow(row)),
2427
+ "memory"
2428
+ );
2429
+ return { messages: list.get.all.db() };
2430
+ } catch (error$1) {
2431
+ const mastraError = new error.MastraError(
2432
+ {
2433
+ id: storage.createStorageErrorId("DSQL", "LIST_MESSAGES_BY_ID", "FAILED"),
2434
+ domain: error.ErrorDomain.STORAGE,
2435
+ category: error.ErrorCategory.THIRD_PARTY,
2436
+ details: {
2437
+ messageIds: JSON.stringify(messageIds)
2438
+ }
2439
+ },
2440
+ error$1
2441
+ );
2442
+ this.logger?.error?.(mastraError.toString());
2443
+ this.logger?.trackException(mastraError);
2444
+ return { messages: [] };
2445
+ }
2446
+ }
2447
+ async listMessages(args) {
2448
+ const { threadId, resourceId, include, filter, perPage: perPageInput, page = 0, orderBy } = args;
2449
+ const threadIds = (Array.isArray(threadId) ? threadId : [threadId]).filter(
2450
+ (id) => typeof id === "string"
2451
+ );
2452
+ if (threadIds.length === 0 || threadIds.some((id) => !id.trim())) {
2453
+ throw new error.MastraError(
2454
+ {
2455
+ id: storage.createStorageErrorId("DSQL", "LIST_MESSAGES", "INVALID_THREAD_ID"),
2456
+ domain: error.ErrorDomain.STORAGE,
2457
+ category: error.ErrorCategory.USER,
2458
+ details: { threadId: Array.isArray(threadId) ? String(threadId) : String(threadId) }
2459
+ },
2460
+ new Error("threadId must be a non-empty string or array of non-empty strings")
2461
+ );
2462
+ }
2463
+ if (page < 0) {
2464
+ throw new error.MastraError({
2465
+ id: storage.createStorageErrorId("DSQL", "LIST_MESSAGES", "INVALID_PAGE"),
2466
+ domain: error.ErrorDomain.STORAGE,
2467
+ category: error.ErrorCategory.USER,
2468
+ text: "Page number must be non-negative",
2469
+ details: {
2470
+ threadId: Array.isArray(threadId) ? threadId.join(",") : threadId,
2471
+ page
2472
+ }
2473
+ });
2474
+ }
2475
+ const perPage = storage.normalizePerPage(perPageInput, 40);
2476
+ const { offset, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
2477
+ try {
2478
+ const { field, direction } = this.parseOrderBy(orderBy, "ASC");
2479
+ const orderByStatement = `ORDER BY "${field}" ${direction}`;
2480
+ const selectStatement = `SELECT id, content, role, type, "createdAt", "createdAtZ", thread_id AS "threadId", "resourceId"`;
2481
+ const tableName = getTableName2({ indexName: storage.TABLE_MESSAGES, schemaName: getSchemaName2(this.#schema) });
2482
+ const conditions = [`thread_id IN (${inPlaceholders(threadIds.length)})`];
2483
+ const queryParams = [...threadIds];
2484
+ let paramIndex = threadIds.length + 1;
2485
+ if (resourceId) {
2486
+ conditions.push(`"resourceId" = $${paramIndex++}`);
2487
+ queryParams.push(resourceId);
2488
+ }
2489
+ if (filter?.dateRange?.start) {
2490
+ conditions.push(`"createdAtZ" >= $${paramIndex++}::timestamptz`);
2491
+ queryParams.push(filter.dateRange.start);
2492
+ }
2493
+ if (filter?.dateRange?.end) {
2494
+ conditions.push(`"createdAtZ" <= $${paramIndex++}::timestamptz`);
2495
+ queryParams.push(filter.dateRange.end);
2496
+ }
2497
+ const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
2498
+ const countQuery = `SELECT COUNT(*) FROM ${tableName} ${whereClause}`;
2499
+ const countResult = await this.#db.client.one(countQuery, queryParams);
2500
+ const total = parseInt(countResult.count, 10);
2501
+ const limitValue = perPageInput === false ? total : perPage;
2502
+ const dataQuery = `${selectStatement} FROM ${tableName} ${whereClause} ${orderByStatement} LIMIT $${paramIndex++} OFFSET $${paramIndex++}`;
2503
+ const rows = await this.#db.client.manyOrNone(dataQuery, [...queryParams, limitValue, offset]);
2504
+ const messages = [...rows || []];
2505
+ if (total === 0 && messages.length === 0 && (!include || include.length === 0)) {
2506
+ return {
2507
+ messages: [],
2508
+ total: 0,
2509
+ page,
2510
+ perPage: perPageForResponse,
2511
+ hasMore: false
2512
+ };
2513
+ }
2514
+ const messageIds = new Set(messages.map((m) => m.id));
2515
+ if (include && include.length > 0) {
2516
+ const includeMessages = await this._getIncludedMessages({ include });
2517
+ if (includeMessages) {
2518
+ for (const includeMsg of includeMessages) {
2519
+ if (!messageIds.has(includeMsg.id)) {
2520
+ messages.push(includeMsg);
2521
+ messageIds.add(includeMsg.id);
2522
+ }
2523
+ }
2524
+ }
2525
+ }
2526
+ const messagesWithParsedContent = messages.map((row) => this.parseRow(row));
2527
+ const list = new agent.MessageList().add(messagesWithParsedContent, "memory");
2528
+ let finalMessages = list.get.all.db();
2529
+ finalMessages = finalMessages.sort((a, b) => {
2530
+ const aValue = field === "createdAt" ? new Date(a.createdAt).getTime() : a[field];
2531
+ const bValue = field === "createdAt" ? new Date(b.createdAt).getTime() : b[field];
2532
+ if (aValue == null && bValue == null) return a.id.localeCompare(b.id);
2533
+ if (aValue == null) return 1;
2534
+ if (bValue == null) return -1;
2535
+ if (aValue === bValue) {
2536
+ return a.id.localeCompare(b.id);
2537
+ }
2538
+ if (typeof aValue === "number" && typeof bValue === "number") {
2539
+ return direction === "ASC" ? aValue - bValue : bValue - aValue;
2540
+ }
2541
+ return direction === "ASC" ? String(aValue).localeCompare(String(bValue)) : String(bValue).localeCompare(String(aValue));
2542
+ });
2543
+ const threadIdSet = new Set(threadIds);
2544
+ const returnedThreadMessageIds = new Set(
2545
+ finalMessages.filter((m) => m.threadId && threadIdSet.has(m.threadId)).map((m) => m.id)
2546
+ );
2547
+ const allThreadMessagesReturned = returnedThreadMessageIds.size >= total;
2548
+ const hasMore = perPageInput !== false && !allThreadMessagesReturned && offset + perPage < total;
2549
+ return {
2550
+ messages: finalMessages,
2551
+ total,
2552
+ page,
2553
+ perPage: perPageForResponse,
2554
+ hasMore
2555
+ };
2556
+ } catch (error$1) {
2557
+ const mastraError = new error.MastraError(
2558
+ {
2559
+ id: storage.createStorageErrorId("DSQL", "LIST_MESSAGES", "FAILED"),
2560
+ domain: error.ErrorDomain.STORAGE,
2561
+ category: error.ErrorCategory.THIRD_PARTY,
2562
+ details: {
2563
+ threadId: Array.isArray(threadId) ? threadId.join(",") : threadId,
2564
+ resourceId: resourceId ?? ""
2565
+ }
2566
+ },
2567
+ error$1
2568
+ );
2569
+ this.logger?.error?.(mastraError.toString());
2570
+ this.logger?.trackException(mastraError);
2571
+ return {
2572
+ messages: [],
2573
+ total: 0,
2574
+ page,
2575
+ perPage: perPageForResponse,
2576
+ hasMore: false
2577
+ };
2578
+ }
2579
+ }
2580
+ async saveMessages({ messages }) {
2581
+ if (messages.length === 0) return { messages: [] };
2582
+ const threadId = messages[0]?.threadId;
2583
+ if (!threadId) {
2584
+ throw new error.MastraError({
2585
+ id: storage.createStorageErrorId("DSQL", "SAVE_MESSAGES", "FAILED"),
2586
+ domain: error.ErrorDomain.STORAGE,
2587
+ category: error.ErrorCategory.USER,
2588
+ text: `Thread ID is required`
2589
+ });
2590
+ }
2591
+ const thread = await this.getThreadById({ threadId });
2592
+ if (!thread) {
2593
+ throw new error.MastraError({
2594
+ id: storage.createStorageErrorId("DSQL", "SAVE_MESSAGES", "FAILED"),
2595
+ domain: error.ErrorDomain.STORAGE,
2596
+ category: error.ErrorCategory.USER,
2597
+ text: `Thread ${threadId} not found`,
2598
+ details: {
2599
+ threadId
2600
+ }
2601
+ });
2602
+ }
2603
+ const tableName = getTableName2({ indexName: storage.TABLE_MESSAGES, schemaName: getSchemaName2(this.#schema) });
2604
+ const threadTableName = getTableName2({ indexName: storage.TABLE_THREADS, schemaName: getSchemaName2(this.#schema) });
2605
+ await withRetry(
2606
+ async () => {
2607
+ await this.#db.client.tx(async (t) => {
2608
+ const messageInserts = messages.map((message) => {
2609
+ if (!message.threadId) {
2610
+ throw new Error(
2611
+ `Expected to find a threadId for message, but couldn't find one. An unexpected error has occurred.`
2612
+ );
2613
+ }
2614
+ if (!message.resourceId) {
2615
+ throw new Error(
2616
+ `Expected to find a resourceId for message, but couldn't find one. An unexpected error has occurred.`
2617
+ );
2618
+ }
2619
+ const createdAtIso = message.createdAt ? new Date(message.createdAt).toISOString() : (/* @__PURE__ */ new Date()).toISOString();
2620
+ return t.none(
2621
+ `INSERT INTO ${tableName} (id, thread_id, content, "createdAt", "createdAtZ", role, type, "resourceId")
2622
+ VALUES ($1, $2, $3, $4::timestamp, $5::timestamptz, $6, $7, $8)
2623
+ ON CONFLICT (id) DO UPDATE SET
2624
+ thread_id = EXCLUDED.thread_id,
2625
+ content = EXCLUDED.content,
2626
+ role = EXCLUDED.role,
2627
+ type = EXCLUDED.type,
2628
+ "resourceId" = EXCLUDED."resourceId"`,
2629
+ [
2630
+ message.id,
2631
+ message.threadId,
2632
+ typeof message.content === "string" ? message.content : JSON.stringify(message.content),
2633
+ createdAtIso,
2634
+ createdAtIso,
2635
+ message.role,
2636
+ message.type || "v2",
2637
+ message.resourceId
2638
+ ]
2639
+ );
2640
+ });
2641
+ const nowIso = (/* @__PURE__ */ new Date()).toISOString();
2642
+ const threadUpdate = t.none(
2643
+ `UPDATE ${threadTableName}
2644
+ SET
2645
+ "updatedAt" = $1::timestamp,
2646
+ "updatedAtZ" = $2::timestamptz
2647
+ WHERE id = $3
2648
+ `,
2649
+ [nowIso, nowIso, threadId]
2650
+ );
2651
+ await Promise.all([...messageInserts, threadUpdate]);
2652
+ });
2653
+ },
2654
+ {
2655
+ onRetry: (error, attempt, delay) => {
2656
+ this.logger?.warn?.(
2657
+ `saveMessages retry ${attempt} for thread ${threadId} after ${delay}ms: ${error.message}`
2658
+ );
2659
+ }
2660
+ }
2661
+ ).catch((error$1) => {
2662
+ throw new error.MastraError(
2663
+ {
2664
+ id: storage.createStorageErrorId("DSQL", "SAVE_MESSAGES", "FAILED"),
2665
+ domain: error.ErrorDomain.STORAGE,
2666
+ category: error.ErrorCategory.THIRD_PARTY,
2667
+ details: {
2668
+ threadId
2669
+ }
2670
+ },
2671
+ error$1
2672
+ );
2673
+ });
2674
+ const messagesWithParsedContent = messages.map((message) => {
2675
+ if (typeof message.content === "string") {
2676
+ try {
2677
+ return { ...message, content: JSON.parse(message.content) };
2678
+ } catch {
2679
+ return message;
2680
+ }
2681
+ }
2682
+ return message;
2683
+ });
2684
+ const list = new agent.MessageList().add(messagesWithParsedContent, "memory");
2685
+ return { messages: list.get.all.db() };
2686
+ }
2687
+ async updateMessages({
2688
+ messages
2689
+ }) {
2690
+ if (messages.length === 0) {
2691
+ return [];
2692
+ }
2693
+ const messageIds = messages.map((m) => m.id);
2694
+ const selectQuery = `SELECT id, content, role, type, "createdAt", "createdAtZ", thread_id AS "threadId", "resourceId" FROM ${getTableName2({ indexName: storage.TABLE_MESSAGES, schemaName: getSchemaName2(this.#schema) })} WHERE id IN (${inPlaceholders(messageIds.length)})`;
2695
+ const existingMessagesDb = await this.#db.client.manyOrNone(selectQuery, messageIds);
2696
+ if (existingMessagesDb.length === 0) {
2697
+ return [];
2698
+ }
2699
+ const existingMessages = existingMessagesDb.map((msg) => {
2700
+ if (typeof msg.content === "string") {
2701
+ try {
2702
+ msg.content = JSON.parse(msg.content);
2703
+ } catch {
2704
+ }
2705
+ }
2706
+ return msg;
2707
+ });
2708
+ const threadIdsToUpdate = /* @__PURE__ */ new Set();
2709
+ await withRetry(
2710
+ async () => {
2711
+ await this.#db.client.tx(async (t) => {
2712
+ const queries = [];
2713
+ const columnMapping = {
2714
+ threadId: "thread_id"
2715
+ };
2716
+ for (const existingMessage of existingMessages) {
2717
+ const updatePayload = messages.find((m) => m.id === existingMessage.id);
2718
+ if (!updatePayload) continue;
2719
+ const { id, ...fieldsToUpdate } = updatePayload;
2720
+ if (Object.keys(fieldsToUpdate).length === 0) continue;
2721
+ threadIdsToUpdate.add(existingMessage.threadId);
2722
+ if (updatePayload.threadId && updatePayload.threadId !== existingMessage.threadId) {
2723
+ threadIdsToUpdate.add(updatePayload.threadId);
2724
+ }
2725
+ const setClauses = [];
2726
+ const values = [];
2727
+ let paramIndex = 1;
2728
+ const updatableFields = { ...fieldsToUpdate };
2729
+ if (updatableFields.content) {
2730
+ const newContent = {
2731
+ ...existingMessage.content,
2732
+ ...updatableFields.content,
2733
+ ...existingMessage.content?.metadata && updatableFields.content.metadata ? {
2734
+ metadata: {
2735
+ ...existingMessage.content.metadata,
2736
+ ...updatableFields.content.metadata
2737
+ }
2738
+ } : {}
2739
+ };
2740
+ setClauses.push(`content = $${paramIndex++}`);
2741
+ values.push(newContent);
2742
+ delete updatableFields.content;
2743
+ }
2744
+ for (const key in updatableFields) {
2745
+ if (Object.prototype.hasOwnProperty.call(updatableFields, key)) {
2746
+ const dbColumn = columnMapping[key] || key;
2747
+ setClauses.push(`"${dbColumn}" = $${paramIndex++}`);
2748
+ values.push(updatableFields[key]);
2749
+ }
2750
+ }
2751
+ if (setClauses.length > 0) {
2752
+ values.push(id);
2753
+ const sql = `UPDATE ${getTableName2({ indexName: storage.TABLE_MESSAGES, schemaName: getSchemaName2(this.#schema) })} SET ${setClauses.join(", ")} WHERE id = $${paramIndex}`;
2754
+ queries.push(t.none(sql, values));
2755
+ }
2756
+ }
2757
+ if (threadIdsToUpdate.size > 0) {
2758
+ const threadIds = Array.from(threadIdsToUpdate);
2759
+ queries.push(
2760
+ t.none(
2761
+ `UPDATE ${getTableName2({ indexName: storage.TABLE_THREADS, schemaName: getSchemaName2(this.#schema) })} SET "updatedAt" = NOW(), "updatedAtZ" = NOW() WHERE id IN (${inPlaceholders(threadIds.length)})`,
2762
+ threadIds
2763
+ )
2764
+ );
2765
+ }
2766
+ if (queries.length > 0) {
2767
+ await t.batch(queries);
2768
+ }
2769
+ });
2770
+ },
2771
+ {
2772
+ onRetry: (error, attempt, delay) => {
2773
+ this.logger?.warn?.(
2774
+ `updateMessages retry ${attempt} for ${messageIds.length} messages after ${delay}ms: ${error.message}`
2775
+ );
2776
+ }
2777
+ }
2778
+ ).catch((error$1) => {
2779
+ throw new error.MastraError(
2780
+ {
2781
+ id: storage.createStorageErrorId("DSQL", "UPDATE_MESSAGES", "FAILED"),
2782
+ domain: error.ErrorDomain.STORAGE,
2783
+ category: error.ErrorCategory.THIRD_PARTY,
2784
+ details: {
2785
+ messageIdsLength: messageIds.length
2786
+ }
2787
+ },
2788
+ error$1
2789
+ );
2790
+ });
2791
+ const updatedMessages = await this.#db.client.manyOrNone(selectQuery, messageIds);
2792
+ return (updatedMessages || []).map((row) => {
2793
+ const message = this.normalizeMessageRow(row);
2794
+ if (typeof message.content === "string") {
2795
+ try {
2796
+ return { ...message, content: JSON.parse(message.content) };
2797
+ } catch {
2798
+ }
2799
+ }
2800
+ return message;
2801
+ });
2802
+ }
2803
+ async deleteMessages(messageIds) {
2804
+ if (!messageIds || messageIds.length === 0) {
2805
+ return;
2806
+ }
2807
+ const messageTableName = getTableName2({ indexName: storage.TABLE_MESSAGES, schemaName: getSchemaName2(this.#schema) });
2808
+ const threadTableName = getTableName2({ indexName: storage.TABLE_THREADS, schemaName: getSchemaName2(this.#schema) });
2809
+ await withRetry(
2810
+ async () => {
2811
+ await this.#db.client.tx(async (t) => {
2812
+ const placeholders = messageIds.map((_, idx) => `$${idx + 1}`).join(",");
2813
+ const messages = await t.manyOrNone(
2814
+ `SELECT DISTINCT thread_id FROM ${messageTableName} WHERE id IN (${placeholders})`,
2815
+ messageIds
2816
+ );
2817
+ const threadIds = messages?.map((msg) => msg.thread_id).filter(Boolean) || [];
2818
+ await t.none(`DELETE FROM ${messageTableName} WHERE id IN (${placeholders})`, messageIds);
2819
+ if (threadIds.length > 0) {
2820
+ const updatePromises = threadIds.map(
2821
+ (threadId) => t.none(`UPDATE ${threadTableName} SET "updatedAt" = NOW(), "updatedAtZ" = NOW() WHERE id = $1`, [
2822
+ threadId
2823
+ ])
2824
+ );
2825
+ await Promise.all(updatePromises);
2826
+ }
2827
+ });
2828
+ },
2829
+ {
2830
+ onRetry: (error, attempt, delay) => {
2831
+ this.logger?.warn?.(
2832
+ `deleteMessages retry ${attempt} for ${messageIds.length} messages after ${delay}ms: ${error.message}`
2833
+ );
2834
+ }
2835
+ }
2836
+ ).catch((error$1) => {
2837
+ throw new error.MastraError(
2838
+ {
2839
+ id: storage.createStorageErrorId("DSQL", "DELETE_MESSAGES", "FAILED"),
2840
+ domain: error.ErrorDomain.STORAGE,
2841
+ category: error.ErrorCategory.THIRD_PARTY,
2842
+ details: { messageIds: messageIds.join(", ") }
2843
+ },
2844
+ error$1
2845
+ );
2846
+ });
2847
+ }
2848
+ async getResourceById({ resourceId }) {
2849
+ try {
2850
+ const tableName = getTableName2({ indexName: storage.TABLE_RESOURCES, schemaName: getSchemaName2(this.#schema) });
2851
+ const result = await this.#db.client.oneOrNone(
2852
+ `SELECT * FROM ${tableName} WHERE id = $1`,
2853
+ [resourceId]
2854
+ );
2855
+ if (!result) {
2856
+ return null;
2857
+ }
2858
+ return {
2859
+ id: result.id,
2860
+ createdAt: result.createdAtZ || result.createdAt,
2861
+ updatedAt: result.updatedAtZ || result.updatedAt,
2862
+ workingMemory: result.workingMemory,
2863
+ metadata: typeof result.metadata === "string" ? JSON.parse(result.metadata) : result.metadata
2864
+ };
2865
+ } catch (error$1) {
2866
+ throw new error.MastraError(
2867
+ {
2868
+ id: storage.createStorageErrorId("DSQL", "GET_RESOURCE_BY_ID", "FAILED"),
2869
+ domain: error.ErrorDomain.STORAGE,
2870
+ category: error.ErrorCategory.THIRD_PARTY,
2871
+ details: { resourceId }
2872
+ },
2873
+ error$1
2874
+ );
2875
+ }
2876
+ }
2877
+ async saveResource({ resource }) {
2878
+ await this.#db.insert({
2879
+ tableName: storage.TABLE_RESOURCES,
2880
+ record: {
2881
+ ...resource,
2882
+ metadata: JSON.stringify(resource.metadata)
2883
+ }
2884
+ });
2885
+ return resource;
2886
+ }
2887
+ async updateResource({
2888
+ resourceId,
2889
+ workingMemory,
2890
+ metadata
2891
+ }) {
2892
+ const tableName = getTableName2({ indexName: storage.TABLE_RESOURCES, schemaName: getSchemaName2(this.#schema) });
2893
+ const { result } = await withRetry(
2894
+ async () => {
2895
+ const existingResource = await this.getResourceById({ resourceId });
2896
+ if (!existingResource) {
2897
+ const newResource = {
2898
+ id: resourceId,
2899
+ workingMemory,
2900
+ metadata: metadata || {},
2901
+ createdAt: /* @__PURE__ */ new Date(),
2902
+ updatedAt: /* @__PURE__ */ new Date()
2903
+ };
2904
+ return this.saveResource({ resource: newResource });
2905
+ }
2906
+ const updatedResource = {
2907
+ ...existingResource,
2908
+ workingMemory: workingMemory !== void 0 ? workingMemory : existingResource.workingMemory,
2909
+ metadata: {
2910
+ ...existingResource.metadata,
2911
+ ...metadata
2912
+ },
2913
+ updatedAt: /* @__PURE__ */ new Date()
2914
+ };
2915
+ const updates = [];
2916
+ const values = [];
2917
+ let paramIndex = 1;
2918
+ if (workingMemory !== void 0) {
2919
+ updates.push(`"workingMemory" = $${paramIndex}`);
2920
+ values.push(workingMemory);
2921
+ paramIndex++;
2922
+ }
2923
+ if (metadata) {
2924
+ updates.push(`metadata = $${paramIndex}`);
2925
+ values.push(JSON.stringify(updatedResource.metadata));
2926
+ paramIndex++;
2927
+ }
2928
+ updates.push(`"updatedAt" = $${paramIndex}`);
2929
+ values.push(updatedResource.updatedAt.toISOString());
2930
+ paramIndex++;
2931
+ updates.push(`"updatedAtZ" = $${paramIndex}`);
2932
+ values.push(updatedResource.updatedAt.toISOString());
2933
+ paramIndex++;
2934
+ values.push(resourceId);
2935
+ await this.#db.client.none(`UPDATE ${tableName} SET ${updates.join(", ")} WHERE id = $${paramIndex}`, values);
2936
+ return updatedResource;
2937
+ },
2938
+ {
2939
+ onRetry: (error, attempt, delay) => {
2940
+ this.logger?.warn?.(`updateResource retry ${attempt} for ${resourceId} after ${delay}ms: ${error.message}`);
2941
+ }
2942
+ }
2943
+ ).catch((error$1) => {
2944
+ if (error$1 instanceof error.MastraError) {
2945
+ throw error$1;
2946
+ }
2947
+ throw new error.MastraError(
2948
+ {
2949
+ id: storage.createStorageErrorId("DSQL", "UPDATE_RESOURCE", "FAILED"),
2950
+ domain: error.ErrorDomain.STORAGE,
2951
+ category: error.ErrorCategory.THIRD_PARTY,
2952
+ details: {
2953
+ resourceId
2954
+ }
2955
+ },
2956
+ error$1
2957
+ );
2958
+ });
2959
+ return result;
2960
+ }
2961
+ };
2962
+ var ObservabilityDSQL = class _ObservabilityDSQL extends storage.ObservabilityStorage {
2963
+ #db;
2964
+ #schema;
2965
+ #skipDefaultIndexes;
2966
+ #indexes;
2967
+ /** Tables managed by this domain */
2968
+ static MANAGED_TABLES = [storage.TABLE_SPANS];
2969
+ constructor(config) {
2970
+ super();
2971
+ const { client, schemaName, skipDefaultIndexes, indexes } = resolveDsqlConfig(config);
2972
+ this.#db = new DsqlDB({ client, schemaName, skipDefaultIndexes });
2973
+ this.#schema = schemaName || "public";
2974
+ this.#skipDefaultIndexes = skipDefaultIndexes;
2975
+ this.#indexes = indexes?.filter((idx) => _ObservabilityDSQL.MANAGED_TABLES.includes(idx.table));
2976
+ }
2977
+ async init() {
2978
+ await this.#db.createTable({ tableName: storage.TABLE_SPANS, schema: storage.TABLE_SCHEMAS[storage.TABLE_SPANS] });
2979
+ await this.createDefaultIndexes();
2980
+ await this.createCustomIndexes();
2981
+ }
2982
+ /**
2983
+ * Returns default index definitions for the observability domain tables.
2984
+ */
2985
+ getDefaultIndexDefinitions() {
2986
+ const schemaPrefix = this.#schema !== "public" ? `${this.#schema}_` : "";
2987
+ return [
2988
+ {
2989
+ name: `${schemaPrefix}mastra_ai_spans_traceid_startedat_idx`,
2990
+ table: storage.TABLE_SPANS,
2991
+ columns: ["traceId", "startedAt"]
2992
+ },
2993
+ {
2994
+ name: `${schemaPrefix}mastra_ai_spans_parentspanid_startedat_idx`,
2995
+ table: storage.TABLE_SPANS,
2996
+ columns: ["parentSpanId", "startedAt"]
2997
+ },
2998
+ {
2999
+ name: `${schemaPrefix}mastra_ai_spans_name_idx`,
3000
+ table: storage.TABLE_SPANS,
3001
+ columns: ["name"]
3002
+ },
3003
+ {
3004
+ name: `${schemaPrefix}mastra_ai_spans_spantype_startedat_idx`,
3005
+ table: storage.TABLE_SPANS,
3006
+ columns: ["spanType", "startedAt"]
3007
+ },
3008
+ // Entity identification indexes - common filtering patterns
3009
+ {
3010
+ name: `${schemaPrefix}mastra_ai_spans_entitytype_entityid_idx`,
3011
+ table: storage.TABLE_SPANS,
3012
+ columns: ["entityType", "entityId"]
3013
+ },
3014
+ {
3015
+ name: `${schemaPrefix}mastra_ai_spans_entitytype_entityname_idx`,
3016
+ table: storage.TABLE_SPANS,
3017
+ columns: ["entityType", "entityName"]
3018
+ },
3019
+ // Multi-tenant filtering - organizationId + userId
3020
+ {
3021
+ name: `${schemaPrefix}mastra_ai_spans_orgid_userid_idx`,
3022
+ table: storage.TABLE_SPANS,
3023
+ columns: ["organizationId", "userId"]
3024
+ }
3025
+ ];
3026
+ }
3027
+ /**
3028
+ * Creates default indexes for optimal query performance.
3029
+ */
3030
+ async createDefaultIndexes() {
3031
+ if (this.#skipDefaultIndexes) {
3032
+ return;
3033
+ }
3034
+ for (const indexDef of this.getDefaultIndexDefinitions()) {
3035
+ try {
3036
+ await this.#db.createIndex(indexDef);
3037
+ } catch (error) {
3038
+ this.logger?.warn?.(`Failed to create index ${indexDef.name}:`, error);
3039
+ }
3040
+ }
3041
+ }
3042
+ /**
3043
+ * Creates custom user-defined indexes for this domain's tables.
3044
+ */
3045
+ async createCustomIndexes() {
3046
+ if (!this.#indexes || this.#indexes.length === 0) {
3047
+ return;
3048
+ }
3049
+ for (const indexDef of this.#indexes) {
3050
+ try {
3051
+ await this.#db.createIndex(indexDef);
3052
+ } catch (error) {
3053
+ this.logger?.warn?.(`Failed to create custom index ${indexDef.name}:`, error);
3054
+ }
3055
+ }
3056
+ }
3057
+ async dangerouslyClearAll() {
3058
+ await this.#db.clearTable({ tableName: storage.TABLE_SPANS });
3059
+ }
3060
+ get tracingStrategy() {
3061
+ return {
3062
+ preferred: "batch-with-updates",
3063
+ supported: ["batch-with-updates", "insert-only"]
3064
+ };
3065
+ }
3066
+ async createSpan(args) {
3067
+ const { span } = args;
3068
+ try {
3069
+ const startedAt = span.startedAt instanceof Date ? span.startedAt.toISOString() : span.startedAt;
3070
+ const endedAt = span.endedAt instanceof Date ? span.endedAt.toISOString() : span.endedAt;
3071
+ const now = (/* @__PURE__ */ new Date()).toISOString();
3072
+ const record = {
3073
+ ...span,
3074
+ startedAt,
3075
+ endedAt,
3076
+ startedAtZ: startedAt,
3077
+ endedAtZ: endedAt,
3078
+ // Aurora DSQL doesn't support triggers, so we set timestamps explicitly
3079
+ createdAt: now,
3080
+ updatedAt: now
3081
+ };
3082
+ await this.#db.insert({ tableName: storage.TABLE_SPANS, record });
3083
+ } catch (error$1) {
3084
+ throw new error.MastraError(
3085
+ {
3086
+ id: storage.createStorageErrorId("DSQL", "CREATE_SPAN", "FAILED"),
3087
+ domain: error.ErrorDomain.STORAGE,
3088
+ category: error.ErrorCategory.USER,
3089
+ details: {
3090
+ spanId: span.spanId,
3091
+ traceId: span.traceId,
3092
+ spanType: span.spanType,
3093
+ spanName: span.name
3094
+ }
3095
+ },
3096
+ error$1
3097
+ );
3098
+ }
3099
+ }
3100
+ async getSpan(args) {
3101
+ const { traceId, spanId } = args;
3102
+ try {
3103
+ const tableName = getTableName2({
3104
+ indexName: storage.TABLE_SPANS,
3105
+ schemaName: getSchemaName2(this.#schema)
3106
+ });
3107
+ const row = await this.#db.client.oneOrNone(
3108
+ `SELECT
3109
+ "traceId", "spanId", "parentSpanId", "name",
3110
+ "entityType", "entityId", "entityName",
3111
+ "userId", "organizationId", "resourceId",
3112
+ "runId", "sessionId", "threadId", "requestId",
3113
+ "environment", "source", "serviceName", "scope",
3114
+ "spanType", "attributes", "metadata", "tags", "links",
3115
+ "input", "output", "error", "isEvent",
3116
+ "startedAtZ" as "startedAt", "endedAtZ" as "endedAt",
3117
+ "createdAtZ" as "createdAt", "updatedAtZ" as "updatedAt"
3118
+ FROM ${tableName}
3119
+ WHERE "traceId" = $1 AND "spanId" = $2`,
3120
+ [traceId, spanId]
3121
+ );
3122
+ if (!row) {
3123
+ return null;
3124
+ }
3125
+ return {
3126
+ span: transformFromSqlRow({
3127
+ tableName: storage.TABLE_SPANS,
3128
+ sqlRow: row
3129
+ })
3130
+ };
3131
+ } catch (error$1) {
3132
+ throw new error.MastraError(
3133
+ {
3134
+ id: storage.createStorageErrorId("DSQL", "GET_SPAN", "FAILED"),
3135
+ domain: error.ErrorDomain.STORAGE,
3136
+ category: error.ErrorCategory.USER,
3137
+ details: { traceId, spanId }
3138
+ },
3139
+ error$1
3140
+ );
3141
+ }
3142
+ }
3143
+ async getRootSpan(args) {
3144
+ const { traceId } = args;
3145
+ try {
3146
+ const tableName = getTableName2({
3147
+ indexName: storage.TABLE_SPANS,
3148
+ schemaName: getSchemaName2(this.#schema)
3149
+ });
3150
+ const row = await this.#db.client.oneOrNone(
3151
+ `SELECT
3152
+ "traceId", "spanId", "parentSpanId", "name",
3153
+ "entityType", "entityId", "entityName",
3154
+ "userId", "organizationId", "resourceId",
3155
+ "runId", "sessionId", "threadId", "requestId",
3156
+ "environment", "source", "serviceName", "scope",
3157
+ "spanType", "attributes", "metadata", "tags", "links",
3158
+ "input", "output", "error", "isEvent",
3159
+ "startedAtZ" as "startedAt", "endedAtZ" as "endedAt",
3160
+ "createdAtZ" as "createdAt", "updatedAtZ" as "updatedAt"
3161
+ FROM ${tableName}
3162
+ WHERE "traceId" = $1 AND "parentSpanId" IS NULL`,
3163
+ [traceId]
3164
+ );
3165
+ if (!row) {
3166
+ return null;
3167
+ }
3168
+ return {
3169
+ span: transformFromSqlRow({
3170
+ tableName: storage.TABLE_SPANS,
3171
+ sqlRow: row
3172
+ })
3173
+ };
3174
+ } catch (error$1) {
3175
+ throw new error.MastraError(
3176
+ {
3177
+ id: storage.createStorageErrorId("DSQL", "GET_ROOT_SPAN", "FAILED"),
3178
+ domain: error.ErrorDomain.STORAGE,
3179
+ category: error.ErrorCategory.USER,
3180
+ details: { traceId }
3181
+ },
3182
+ error$1
3183
+ );
3184
+ }
3185
+ }
3186
+ async getTrace(args) {
3187
+ const { traceId } = args;
3188
+ try {
3189
+ const tableName = getTableName2({
3190
+ indexName: storage.TABLE_SPANS,
3191
+ schemaName: getSchemaName2(this.#schema)
3192
+ });
3193
+ const spans = await this.#db.client.manyOrNone(
3194
+ `SELECT
3195
+ "traceId", "spanId", "parentSpanId", "name",
3196
+ "entityType", "entityId", "entityName",
3197
+ "userId", "organizationId", "resourceId",
3198
+ "runId", "sessionId", "threadId", "requestId",
3199
+ "environment", "source", "serviceName", "scope",
3200
+ "spanType", "attributes", "metadata", "tags", "links",
3201
+ "input", "output", "error", "isEvent",
3202
+ "startedAtZ" as "startedAt", "endedAtZ" as "endedAt",
3203
+ "createdAtZ" as "createdAt", "updatedAtZ" as "updatedAt"
3204
+ FROM ${tableName}
3205
+ WHERE "traceId" = $1
3206
+ ORDER BY "startedAtZ" ASC`,
3207
+ [traceId]
3208
+ );
3209
+ if (!spans || spans.length === 0) {
3210
+ return null;
3211
+ }
3212
+ return {
3213
+ traceId,
3214
+ spans: spans.map(
3215
+ (span) => transformFromSqlRow({
3216
+ tableName: storage.TABLE_SPANS,
3217
+ sqlRow: span
3218
+ })
3219
+ )
3220
+ };
3221
+ } catch (error$1) {
3222
+ throw new error.MastraError(
3223
+ {
3224
+ id: storage.createStorageErrorId("DSQL", "GET_TRACE", "FAILED"),
3225
+ domain: error.ErrorDomain.STORAGE,
3226
+ category: error.ErrorCategory.USER,
3227
+ details: {
3228
+ traceId
3229
+ }
3230
+ },
3231
+ error$1
3232
+ );
3233
+ }
3234
+ }
3235
+ async updateSpan(args) {
3236
+ const { traceId, spanId, updates } = args;
3237
+ try {
3238
+ const data = { ...updates };
3239
+ if (data.endedAt instanceof Date) {
3240
+ const endedAt = data.endedAt.toISOString();
3241
+ data.endedAt = endedAt;
3242
+ data.endedAtZ = endedAt;
3243
+ }
3244
+ if (data.startedAt instanceof Date) {
3245
+ const startedAt = data.startedAt.toISOString();
3246
+ data.startedAt = startedAt;
3247
+ data.startedAtZ = startedAt;
3248
+ }
3249
+ await this.#db.update({
3250
+ tableName: storage.TABLE_SPANS,
3251
+ keys: { spanId, traceId },
3252
+ data
3253
+ });
3254
+ } catch (error$1) {
3255
+ throw new error.MastraError(
3256
+ {
3257
+ id: storage.createStorageErrorId("DSQL", "UPDATE_SPAN", "FAILED"),
3258
+ domain: error.ErrorDomain.STORAGE,
3259
+ category: error.ErrorCategory.USER,
3260
+ details: {
3261
+ spanId,
3262
+ traceId
3263
+ }
3264
+ },
3265
+ error$1
3266
+ );
3267
+ }
3268
+ }
3269
+ async listTraces(args) {
3270
+ const { filters, pagination, orderBy } = storage.listTracesArgsSchema.parse(args);
3271
+ const page = pagination?.page ?? 0;
3272
+ const perPage = pagination?.perPage ?? 100;
3273
+ const tableName = getTableName2({
3274
+ indexName: storage.TABLE_SPANS,
3275
+ schemaName: getSchemaName2(this.#schema)
3276
+ });
3277
+ try {
3278
+ const conditions = ['r."parentSpanId" IS NULL'];
3279
+ const params = [];
3280
+ let paramIndex = 1;
3281
+ if (filters) {
3282
+ if (filters.startedAt?.start) {
3283
+ conditions.push(`r."startedAtZ" >= $${paramIndex++}`);
3284
+ params.push(filters.startedAt.start.toISOString());
3285
+ }
3286
+ if (filters.startedAt?.end) {
3287
+ conditions.push(`r."startedAtZ" <= $${paramIndex++}`);
3288
+ params.push(filters.startedAt.end.toISOString());
3289
+ }
3290
+ if (filters.endedAt?.start) {
3291
+ conditions.push(`r."endedAtZ" >= $${paramIndex++}`);
3292
+ params.push(filters.endedAt.start.toISOString());
3293
+ }
3294
+ if (filters.endedAt?.end) {
3295
+ conditions.push(`r."endedAtZ" <= $${paramIndex++}`);
3296
+ params.push(filters.endedAt.end.toISOString());
3297
+ }
3298
+ if (filters.spanType !== void 0) {
3299
+ conditions.push(`r."spanType" = $${paramIndex++}`);
3300
+ params.push(filters.spanType);
3301
+ }
3302
+ if (filters.entityType !== void 0) {
3303
+ conditions.push(`r."entityType" = $${paramIndex++}`);
3304
+ params.push(filters.entityType);
3305
+ }
3306
+ if (filters.entityId !== void 0) {
3307
+ conditions.push(`r."entityId" = $${paramIndex++}`);
3308
+ params.push(filters.entityId);
3309
+ }
3310
+ if (filters.entityName !== void 0) {
3311
+ conditions.push(`r."entityName" = $${paramIndex++}`);
3312
+ params.push(filters.entityName);
3313
+ }
3314
+ if (filters.userId !== void 0) {
3315
+ conditions.push(`r."userId" = $${paramIndex++}`);
3316
+ params.push(filters.userId);
3317
+ }
3318
+ if (filters.organizationId !== void 0) {
3319
+ conditions.push(`r."organizationId" = $${paramIndex++}`);
3320
+ params.push(filters.organizationId);
3321
+ }
3322
+ if (filters.resourceId !== void 0) {
3323
+ conditions.push(`r."resourceId" = $${paramIndex++}`);
3324
+ params.push(filters.resourceId);
3325
+ }
3326
+ if (filters.runId !== void 0) {
3327
+ conditions.push(`r."runId" = $${paramIndex++}`);
3328
+ params.push(filters.runId);
3329
+ }
3330
+ if (filters.sessionId !== void 0) {
3331
+ conditions.push(`r."sessionId" = $${paramIndex++}`);
3332
+ params.push(filters.sessionId);
3333
+ }
3334
+ if (filters.threadId !== void 0) {
3335
+ conditions.push(`r."threadId" = $${paramIndex++}`);
3336
+ params.push(filters.threadId);
3337
+ }
3338
+ if (filters.requestId !== void 0) {
3339
+ conditions.push(`r."requestId" = $${paramIndex++}`);
3340
+ params.push(filters.requestId);
3341
+ }
3342
+ if (filters.environment !== void 0) {
3343
+ conditions.push(`r."environment" = $${paramIndex++}`);
3344
+ params.push(filters.environment);
3345
+ }
3346
+ if (filters.source !== void 0) {
3347
+ conditions.push(`r."source" = $${paramIndex++}`);
3348
+ params.push(filters.source);
3349
+ }
3350
+ if (filters.serviceName !== void 0) {
3351
+ conditions.push(`r."serviceName" = $${paramIndex++}`);
3352
+ params.push(filters.serviceName);
3353
+ }
3354
+ if (filters.scope != null) {
3355
+ conditions.push(`r."scope"::text = $${paramIndex++}`);
3356
+ params.push(JSON.stringify(filters.scope));
3357
+ }
3358
+ if (filters.metadata != null) {
3359
+ conditions.push(`r."metadata"::text = $${paramIndex++}`);
3360
+ params.push(JSON.stringify(filters.metadata));
3361
+ }
3362
+ if (filters.tags != null && filters.tags.length > 0) {
3363
+ conditions.push(`r."tags"::text = $${paramIndex++}`);
3364
+ params.push(JSON.stringify(filters.tags));
3365
+ }
3366
+ if (filters.status !== void 0) {
3367
+ switch (filters.status) {
3368
+ case storage.TraceStatus.ERROR:
3369
+ conditions.push(`r."error" IS NOT NULL`);
3370
+ break;
3371
+ case storage.TraceStatus.RUNNING:
3372
+ conditions.push(`r."endedAtZ" IS NULL AND r."error" IS NULL`);
3373
+ break;
3374
+ case storage.TraceStatus.SUCCESS:
3375
+ conditions.push(`r."endedAtZ" IS NOT NULL AND r."error" IS NULL`);
3376
+ break;
3377
+ }
3378
+ }
3379
+ if (filters.hasChildError !== void 0) {
3380
+ if (filters.hasChildError) {
3381
+ conditions.push(`EXISTS (
3382
+ SELECT 1 FROM ${tableName} c
3383
+ WHERE c."traceId" = r."traceId" AND c."error" IS NOT NULL
3384
+ )`);
3385
+ } else {
3386
+ conditions.push(`NOT EXISTS (
3387
+ SELECT 1 FROM ${tableName} c
3388
+ WHERE c."traceId" = r."traceId" AND c."error" IS NOT NULL
3389
+ )`);
3390
+ }
3391
+ }
3392
+ }
3393
+ const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
3394
+ const sortField = `${orderBy?.field ?? "startedAt"}Z`;
3395
+ const sortDirection = orderBy?.direction ?? "DESC";
3396
+ const orderClause = `ORDER BY r."${sortField}" ${sortDirection}`;
3397
+ const countResult = await this.#db.client.oneOrNone(
3398
+ `SELECT COUNT(*) FROM ${tableName} r ${whereClause}`,
3399
+ params
3400
+ );
3401
+ const count = Number(countResult?.count ?? 0);
3402
+ if (count === 0) {
3403
+ return {
3404
+ pagination: {
3405
+ total: 0,
3406
+ page,
3407
+ perPage,
3408
+ hasMore: false
3409
+ },
3410
+ spans: []
3411
+ };
3412
+ }
3413
+ const spans = await this.#db.client.manyOrNone(
3414
+ `SELECT
3415
+ r."traceId", r."spanId", r."parentSpanId", r."name",
3416
+ r."entityType", r."entityId", r."entityName",
3417
+ r."userId", r."organizationId", r."resourceId",
3418
+ r."runId", r."sessionId", r."threadId", r."requestId",
3419
+ r."environment", r."source", r."serviceName", r."scope",
3420
+ r."spanType", r."attributes", r."metadata", r."tags", r."links",
3421
+ r."input", r."output", r."error", r."isEvent",
3422
+ r."startedAtZ" as "startedAt", r."endedAtZ" as "endedAt",
3423
+ r."createdAtZ" as "createdAt", r."updatedAtZ" as "updatedAt"
3424
+ FROM ${tableName} r
3425
+ ${whereClause}
3426
+ ${orderClause}
3427
+ LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`,
3428
+ [...params, perPage, page * perPage]
3429
+ );
3430
+ return {
3431
+ pagination: {
3432
+ total: count,
3433
+ page,
3434
+ perPage,
3435
+ hasMore: (page + 1) * perPage < count
3436
+ },
3437
+ spans: storage.toTraceSpans(
3438
+ spans.map(
3439
+ (span) => transformFromSqlRow({
3440
+ tableName: storage.TABLE_SPANS,
3441
+ sqlRow: span
3442
+ })
3443
+ )
3444
+ )
3445
+ };
3446
+ } catch (error$1) {
3447
+ throw new error.MastraError(
3448
+ {
3449
+ id: storage.createStorageErrorId("DSQL", "LIST_TRACES", "FAILED"),
3450
+ domain: error.ErrorDomain.STORAGE,
3451
+ category: error.ErrorCategory.USER
3452
+ },
3453
+ error$1
3454
+ );
3455
+ }
3456
+ }
3457
+ async batchCreateSpans(args) {
3458
+ try {
3459
+ const now = (/* @__PURE__ */ new Date()).toISOString();
3460
+ const records = args.records.map((record) => {
3461
+ const startedAt = record.startedAt instanceof Date ? record.startedAt.toISOString() : record.startedAt;
3462
+ const endedAt = record.endedAt instanceof Date ? record.endedAt.toISOString() : record.endedAt;
3463
+ return {
3464
+ ...record,
3465
+ startedAt,
3466
+ endedAt,
3467
+ startedAtZ: startedAt,
3468
+ endedAtZ: endedAt,
3469
+ // Aurora DSQL doesn't support triggers, so we set timestamps explicitly
3470
+ createdAt: now,
3471
+ updatedAt: now
3472
+ };
3473
+ });
3474
+ await this.#db.batchInsert({
3475
+ tableName: storage.TABLE_SPANS,
3476
+ records
3477
+ });
3478
+ } catch (error$1) {
3479
+ throw new error.MastraError(
3480
+ {
3481
+ id: storage.createStorageErrorId("DSQL", "BATCH_CREATE_SPANS", "FAILED"),
3482
+ domain: error.ErrorDomain.STORAGE,
3483
+ category: error.ErrorCategory.USER
3484
+ },
3485
+ error$1
3486
+ );
3487
+ }
3488
+ }
3489
+ async batchUpdateSpans(args) {
3490
+ try {
3491
+ await this.#db.batchUpdate({
3492
+ tableName: storage.TABLE_SPANS,
3493
+ updates: args.records.map((record) => {
3494
+ const data = { ...record.updates };
3495
+ if (data.endedAt instanceof Date) {
3496
+ const endedAt = data.endedAt.toISOString();
3497
+ data.endedAt = endedAt;
3498
+ data.endedAtZ = endedAt;
3499
+ }
3500
+ if (data.startedAt instanceof Date) {
3501
+ const startedAt = data.startedAt.toISOString();
3502
+ data.startedAt = startedAt;
3503
+ data.startedAtZ = startedAt;
3504
+ }
3505
+ return {
3506
+ keys: { spanId: record.spanId, traceId: record.traceId },
3507
+ data
3508
+ };
3509
+ })
3510
+ });
3511
+ } catch (error$1) {
3512
+ throw new error.MastraError(
3513
+ {
3514
+ id: storage.createStorageErrorId("DSQL", "BATCH_UPDATE_SPANS", "FAILED"),
3515
+ domain: error.ErrorDomain.STORAGE,
3516
+ category: error.ErrorCategory.USER
3517
+ },
3518
+ error$1
3519
+ );
3520
+ }
3521
+ }
3522
+ async batchDeleteTraces(args) {
3523
+ const { batches } = splitIntoBatches(args.traceIds, { maxRows: DEFAULT_MAX_ROWS_PER_BATCH });
3524
+ const tableName = getTableName2({
3525
+ indexName: storage.TABLE_SPANS,
3526
+ schemaName: getSchemaName2(this.#schema)
3527
+ });
3528
+ for (const batchTraceIds of batches) {
3529
+ const placeholders = batchTraceIds.map((_, i) => `$${i + 1}`).join(", ");
3530
+ await withRetry(
3531
+ async () => {
3532
+ await this.#db.client.none(`DELETE FROM ${tableName} WHERE "traceId" IN (${placeholders})`, batchTraceIds);
3533
+ },
3534
+ {
3535
+ onRetry: (error, attempt, delay) => {
3536
+ this.logger?.warn?.(
3537
+ `batchDeleteTraces retry ${attempt} for ${batchTraceIds.length} traces after ${delay}ms: ${error.message}`
3538
+ );
3539
+ }
3540
+ }
3541
+ ).catch((error$1) => {
3542
+ throw new error.MastraError(
3543
+ {
3544
+ id: storage.createStorageErrorId("DSQL", "BATCH_DELETE_TRACES", "FAILED"),
3545
+ domain: error.ErrorDomain.STORAGE,
3546
+ category: error.ErrorCategory.USER
3547
+ },
3548
+ error$1
3549
+ );
3550
+ });
3551
+ }
3552
+ }
3553
+ };
3554
+ function transformScoreRow(row) {
3555
+ return storage.transformScoreRow(row, {
3556
+ preferredTimestampFields: {
3557
+ createdAt: "createdAtZ",
3558
+ updatedAt: "updatedAtZ"
3559
+ }
3560
+ });
3561
+ }
3562
+ var ScoresDSQL = class _ScoresDSQL extends storage.ScoresStorage {
3563
+ #db;
3564
+ #schema;
3565
+ #skipDefaultIndexes;
3566
+ #indexes;
3567
+ /** Tables managed by this domain */
3568
+ static MANAGED_TABLES = [storage.TABLE_SCORERS];
3569
+ constructor(config) {
3570
+ super();
3571
+ const { client, schemaName, skipDefaultIndexes, indexes } = resolveDsqlConfig(config);
3572
+ this.#db = new DsqlDB({ client, schemaName, skipDefaultIndexes });
3573
+ this.#schema = schemaName || "public";
3574
+ this.#skipDefaultIndexes = skipDefaultIndexes;
3575
+ this.#indexes = indexes?.filter((idx) => _ScoresDSQL.MANAGED_TABLES.includes(idx.table));
3576
+ }
3577
+ async init() {
3578
+ await this.#db.createTable({ tableName: storage.TABLE_SCORERS, schema: storage.TABLE_SCHEMAS[storage.TABLE_SCORERS] });
3579
+ await this.createDefaultIndexes();
3580
+ await this.createCustomIndexes();
3581
+ }
3582
+ /**
3583
+ * Returns default index definitions for the scores domain tables.
3584
+ * Note: Aurora DSQL does not support ASC/DESC in index columns.
3585
+ */
3586
+ getDefaultIndexDefinitions() {
3587
+ const schemaPrefix = this.#schema !== "public" ? `${this.#schema}_` : "";
3588
+ return [
3589
+ {
3590
+ name: `${schemaPrefix}mastra_scores_trace_id_span_id_created_at_idx`,
3591
+ table: storage.TABLE_SCORERS,
3592
+ columns: ["traceId", "spanId", "createdAt"]
3593
+ }
3594
+ ];
3595
+ }
3596
+ /**
3597
+ * Creates default indexes for optimal query performance.
3598
+ */
3599
+ async createDefaultIndexes() {
3600
+ if (this.#skipDefaultIndexes) {
3601
+ return;
3602
+ }
3603
+ for (const indexDef of this.getDefaultIndexDefinitions()) {
3604
+ try {
3605
+ await this.#db.createIndex(indexDef);
3606
+ } catch (error) {
3607
+ this.logger?.warn?.(`Failed to create index ${indexDef.name}:`, error);
3608
+ }
3609
+ }
3610
+ }
3611
+ /**
3612
+ * Creates custom user-defined indexes for this domain's tables.
3613
+ */
3614
+ async createCustomIndexes() {
3615
+ if (!this.#indexes || this.#indexes.length === 0) {
3616
+ return;
3617
+ }
3618
+ for (const indexDef of this.#indexes) {
3619
+ try {
3620
+ await this.#db.createIndex(indexDef);
3621
+ } catch (error) {
3622
+ this.logger?.warn?.(`Failed to create custom index ${indexDef.name}:`, error);
3623
+ }
3624
+ }
3625
+ }
3626
+ async dangerouslyClearAll() {
3627
+ await this.#db.clearTable({ tableName: storage.TABLE_SCORERS });
3628
+ }
3629
+ async getScoreById({ id }) {
3630
+ try {
3631
+ const result = await this.#db.client.oneOrNone(
3632
+ `SELECT * FROM ${getTableName2({ indexName: storage.TABLE_SCORERS, schemaName: getSchemaName2(this.#schema) })} WHERE id = $1`,
3633
+ [id]
3634
+ );
3635
+ return result ? transformScoreRow(result) : null;
3636
+ } catch (error$1) {
3637
+ throw new error.MastraError(
3638
+ {
3639
+ id: storage.createStorageErrorId("DSQL", "GET_SCORE_BY_ID", "FAILED"),
3640
+ domain: error.ErrorDomain.STORAGE,
3641
+ category: error.ErrorCategory.THIRD_PARTY
3642
+ },
3643
+ error$1
3644
+ );
3645
+ }
3646
+ }
3647
+ async listScoresByScorerId({
3648
+ scorerId,
3649
+ pagination,
3650
+ entityId,
3651
+ entityType,
3652
+ source
3653
+ }) {
3654
+ try {
3655
+ const conditions = [`"scorerId" = $1`];
3656
+ const queryParams = [scorerId];
3657
+ let paramIndex = 2;
3658
+ if (entityId) {
3659
+ conditions.push(`"entityId" = $${paramIndex++}`);
3660
+ queryParams.push(entityId);
3661
+ }
3662
+ if (entityType) {
3663
+ conditions.push(`"entityType" = $${paramIndex++}`);
3664
+ queryParams.push(entityType);
3665
+ }
3666
+ if (source) {
3667
+ conditions.push(`"source" = $${paramIndex++}`);
3668
+ queryParams.push(source);
3669
+ }
3670
+ const whereClause = conditions.join(" AND ");
3671
+ const total = await this.#db.client.oneOrNone(
3672
+ `SELECT COUNT(*) FROM ${getTableName2({ indexName: storage.TABLE_SCORERS, schemaName: getSchemaName2(this.#schema) })} WHERE ${whereClause}`,
3673
+ queryParams
3674
+ );
3675
+ const { page, perPage: perPageInput } = pagination;
3676
+ const perPage = storage.normalizePerPage(perPageInput, 100);
3677
+ const { offset: start, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
3678
+ if (total?.count === "0" || !total?.count) {
3679
+ return {
3680
+ pagination: {
3681
+ total: 0,
3682
+ page,
3683
+ perPage: perPageForResponse,
3684
+ hasMore: false
3685
+ },
3686
+ scores: []
3687
+ };
3688
+ }
3689
+ const limitValue = perPageInput === false ? Number(total?.count) : perPage;
3690
+ const end = perPageInput === false ? Number(total?.count) : start + perPage;
3691
+ const result = await this.#db.client.manyOrNone(
3692
+ `SELECT * FROM ${getTableName2({ indexName: storage.TABLE_SCORERS, schemaName: getSchemaName2(this.#schema) })} WHERE ${whereClause} ORDER BY "createdAt" DESC LIMIT $${paramIndex++} OFFSET $${paramIndex++}`,
3693
+ [...queryParams, limitValue, start]
3694
+ );
3695
+ return {
3696
+ pagination: {
3697
+ total: Number(total?.count) || 0,
3698
+ page,
3699
+ perPage: perPageForResponse,
3700
+ hasMore: end < Number(total?.count)
3701
+ },
3702
+ scores: result.map(transformScoreRow)
3703
+ };
3704
+ } catch (error$1) {
3705
+ throw new error.MastraError(
3706
+ {
3707
+ id: storage.createStorageErrorId("DSQL", "LIST_SCORES_BY_SCORER_ID", "FAILED"),
3708
+ domain: error.ErrorDomain.STORAGE,
3709
+ category: error.ErrorCategory.THIRD_PARTY
3710
+ },
3711
+ error$1
3712
+ );
3713
+ }
3714
+ }
3715
+ async saveScore(score) {
3716
+ let parsedScore;
3717
+ try {
3718
+ parsedScore = evals.saveScorePayloadSchema.parse(score);
3719
+ } catch (error$1) {
3720
+ throw new error.MastraError(
3721
+ {
3722
+ id: storage.createStorageErrorId("DSQL", "SAVE_SCORE", "VALIDATION_FAILED"),
3723
+ domain: error.ErrorDomain.STORAGE,
3724
+ category: error.ErrorCategory.USER,
3725
+ details: {
3726
+ scorer: typeof score.scorer?.id === "string" ? score.scorer.id : String(score.scorer?.id ?? "unknown"),
3727
+ entityId: score.entityId ?? "unknown",
3728
+ entityType: score.entityType ?? "unknown",
3729
+ traceId: score.traceId ?? "",
3730
+ spanId: score.spanId ?? ""
3731
+ }
3732
+ },
3733
+ error$1
3734
+ );
3735
+ }
3736
+ try {
3737
+ const id = crypto.randomUUID();
3738
+ const now = /* @__PURE__ */ new Date();
3739
+ const {
3740
+ scorer,
3741
+ preprocessStepResult,
3742
+ analyzeStepResult,
3743
+ metadata,
3744
+ input,
3745
+ output,
3746
+ additionalContext,
3747
+ requestContext,
3748
+ entity,
3749
+ ...rest
3750
+ } = parsedScore;
3751
+ await this.#db.insert({
3752
+ tableName: storage.TABLE_SCORERS,
3753
+ record: {
3754
+ id,
3755
+ ...rest,
3756
+ input: JSON.stringify(input) || "",
3757
+ output: JSON.stringify(output) || "",
3758
+ scorer: scorer ? JSON.stringify(scorer) : null,
3759
+ preprocessStepResult: preprocessStepResult ? JSON.stringify(preprocessStepResult) : null,
3760
+ analyzeStepResult: analyzeStepResult ? JSON.stringify(analyzeStepResult) : null,
3761
+ metadata: metadata ? JSON.stringify(metadata) : null,
3762
+ additionalContext: additionalContext ? JSON.stringify(additionalContext) : null,
3763
+ requestContext: requestContext ? JSON.stringify(requestContext) : null,
3764
+ entity: entity ? JSON.stringify(entity) : null,
3765
+ createdAt: now.toISOString(),
3766
+ updatedAt: now.toISOString()
3767
+ }
3768
+ });
3769
+ return { score: { ...parsedScore, id, createdAt: now, updatedAt: now } };
3770
+ } catch (error$1) {
3771
+ throw new error.MastraError(
3772
+ {
3773
+ id: storage.createStorageErrorId("DSQL", "SAVE_SCORE", "FAILED"),
3774
+ domain: error.ErrorDomain.STORAGE,
3775
+ category: error.ErrorCategory.THIRD_PARTY
3776
+ },
3777
+ error$1
3778
+ );
3779
+ }
3780
+ }
3781
+ async listScoresByRunId({
3782
+ runId,
3783
+ pagination
3784
+ }) {
3785
+ try {
3786
+ const total = await this.#db.client.oneOrNone(
3787
+ `SELECT COUNT(*) FROM ${getTableName2({ indexName: storage.TABLE_SCORERS, schemaName: getSchemaName2(this.#schema) })} WHERE "runId" = $1`,
3788
+ [runId]
3789
+ );
3790
+ const { page, perPage: perPageInput } = pagination;
3791
+ const perPage = storage.normalizePerPage(perPageInput, 100);
3792
+ const { offset: start, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
3793
+ if (total?.count === "0" || !total?.count) {
3794
+ return {
3795
+ pagination: {
3796
+ total: 0,
3797
+ page,
3798
+ perPage: perPageForResponse,
3799
+ hasMore: false
3800
+ },
3801
+ scores: []
3802
+ };
3803
+ }
3804
+ const limitValue = perPageInput === false ? Number(total?.count) : perPage;
3805
+ const end = perPageInput === false ? Number(total?.count) : start + perPage;
3806
+ const result = await this.#db.client.manyOrNone(
3807
+ `SELECT * FROM ${getTableName2({ indexName: storage.TABLE_SCORERS, schemaName: getSchemaName2(this.#schema) })} WHERE "runId" = $1 ORDER BY "createdAt" DESC LIMIT $2 OFFSET $3`,
3808
+ [runId, limitValue, start]
3809
+ );
3810
+ return {
3811
+ pagination: {
3812
+ total: Number(total?.count) || 0,
3813
+ page,
3814
+ perPage: perPageForResponse,
3815
+ hasMore: end < Number(total?.count)
3816
+ },
3817
+ scores: result.map(transformScoreRow)
3818
+ };
3819
+ } catch (error$1) {
3820
+ throw new error.MastraError(
3821
+ {
3822
+ id: storage.createStorageErrorId("DSQL", "LIST_SCORES_BY_RUN_ID", "FAILED"),
3823
+ domain: error.ErrorDomain.STORAGE,
3824
+ category: error.ErrorCategory.THIRD_PARTY
3825
+ },
3826
+ error$1
3827
+ );
3828
+ }
3829
+ }
3830
+ async listScoresByEntityId({
3831
+ entityId,
3832
+ entityType,
3833
+ pagination
3834
+ }) {
3835
+ try {
3836
+ const total = await this.#db.client.oneOrNone(
3837
+ `SELECT COUNT(*) FROM ${getTableName2({ indexName: storage.TABLE_SCORERS, schemaName: getSchemaName2(this.#schema) })} WHERE "entityId" = $1 AND "entityType" = $2`,
3838
+ [entityId, entityType]
3839
+ );
3840
+ const { page, perPage: perPageInput } = pagination;
3841
+ const perPage = storage.normalizePerPage(perPageInput, 100);
3842
+ const { offset: start, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
3843
+ if (total?.count === "0" || !total?.count) {
3844
+ return {
3845
+ pagination: {
3846
+ total: 0,
3847
+ page,
3848
+ perPage: perPageForResponse,
3849
+ hasMore: false
3850
+ },
3851
+ scores: []
3852
+ };
3853
+ }
3854
+ const limitValue = perPageInput === false ? Number(total?.count) : perPage;
3855
+ const end = perPageInput === false ? Number(total?.count) : start + perPage;
3856
+ const result = await this.#db.client.manyOrNone(
3857
+ `SELECT * FROM ${getTableName2({ indexName: storage.TABLE_SCORERS, schemaName: getSchemaName2(this.#schema) })} WHERE "entityId" = $1 AND "entityType" = $2 ORDER BY "createdAt" DESC LIMIT $3 OFFSET $4`,
3858
+ [entityId, entityType, limitValue, start]
3859
+ );
3860
+ return {
3861
+ pagination: {
3862
+ total: Number(total?.count) || 0,
3863
+ page,
3864
+ perPage: perPageForResponse,
3865
+ hasMore: end < Number(total?.count)
3866
+ },
3867
+ scores: result.map(transformScoreRow)
3868
+ };
3869
+ } catch (error$1) {
3870
+ throw new error.MastraError(
3871
+ {
3872
+ id: storage.createStorageErrorId("DSQL", "LIST_SCORES_BY_ENTITY_ID", "FAILED"),
3873
+ domain: error.ErrorDomain.STORAGE,
3874
+ category: error.ErrorCategory.THIRD_PARTY
3875
+ },
3876
+ error$1
3877
+ );
3878
+ }
3879
+ }
3880
+ async listScoresBySpan({
3881
+ traceId,
3882
+ spanId,
3883
+ pagination
3884
+ }) {
3885
+ try {
3886
+ const tableName = getTableName2({ indexName: storage.TABLE_SCORERS, schemaName: getSchemaName2(this.#schema) });
3887
+ const countSQLResult = await this.#db.client.oneOrNone(
3888
+ `SELECT COUNT(*) as count FROM ${tableName} WHERE "traceId" = $1 AND "spanId" = $2`,
3889
+ [traceId, spanId]
3890
+ );
3891
+ const total = Number(countSQLResult?.count ?? 0);
3892
+ const { page, perPage: perPageInput } = pagination;
3893
+ const perPage = storage.normalizePerPage(perPageInput, 100);
3894
+ const { offset: start, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
3895
+ const limitValue = perPageInput === false ? total : perPage;
3896
+ const end = perPageInput === false ? total : start + perPage;
3897
+ const result = await this.#db.client.manyOrNone(
3898
+ `SELECT * FROM ${tableName} WHERE "traceId" = $1 AND "spanId" = $2 ORDER BY "createdAt" DESC LIMIT $3 OFFSET $4`,
3899
+ [traceId, spanId, limitValue, start]
3900
+ );
3901
+ const hasMore = end < total;
3902
+ const scores = result.map((row) => transformScoreRow(row)) ?? [];
3903
+ return {
3904
+ scores,
3905
+ pagination: {
3906
+ total,
3907
+ page,
3908
+ perPage: perPageForResponse,
3909
+ hasMore
3910
+ }
3911
+ };
3912
+ } catch (error$1) {
3913
+ throw new error.MastraError(
3914
+ {
3915
+ id: storage.createStorageErrorId("DSQL", "LIST_SCORES_BY_SPAN", "FAILED"),
3916
+ domain: error.ErrorDomain.STORAGE,
3917
+ category: error.ErrorCategory.THIRD_PARTY
3918
+ },
3919
+ error$1
3920
+ );
3921
+ }
3922
+ }
3923
+ };
3924
+ function parseWorkflowRun(row) {
3925
+ let parsedSnapshot = row.snapshot;
3926
+ if (typeof parsedSnapshot === "string") {
3927
+ try {
3928
+ parsedSnapshot = JSON.parse(row.snapshot);
3929
+ } catch (e) {
3930
+ console.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
3931
+ }
3932
+ }
3933
+ return {
3934
+ workflowName: row.workflow_name,
3935
+ runId: row.run_id,
3936
+ snapshot: parsedSnapshot,
3937
+ resourceId: row.resourceId,
3938
+ createdAt: new Date(row.createdAtZ || row.createdAt),
3939
+ updatedAt: new Date(row.updatedAtZ || row.updatedAt)
3940
+ };
3941
+ }
3942
+ var WorkflowsDSQL = class _WorkflowsDSQL extends storage.WorkflowsStorage {
3943
+ #db;
3944
+ #schema;
3945
+ #skipDefaultIndexes;
3946
+ #indexes;
3947
+ /** Tables managed by this domain */
3948
+ static MANAGED_TABLES = [storage.TABLE_WORKFLOW_SNAPSHOT];
3949
+ constructor(config) {
3950
+ super();
3951
+ const { client, schemaName, skipDefaultIndexes, indexes } = resolveDsqlConfig(config);
3952
+ this.#db = new DsqlDB({ client, schemaName });
3953
+ this.#schema = schemaName || "public";
3954
+ this.#skipDefaultIndexes = skipDefaultIndexes;
3955
+ this.#indexes = indexes?.filter((idx) => _WorkflowsDSQL.MANAGED_TABLES.includes(idx.table));
3956
+ }
3957
+ supportsConcurrentUpdates() {
3958
+ return true;
3959
+ }
3960
+ /**
3961
+ * Returns default index definitions for the workflows domain tables.
3962
+ * Currently no default indexes are defined for workflows.
3963
+ */
3964
+ getDefaultIndexDefinitions() {
3965
+ return [];
3966
+ }
3967
+ /**
3968
+ * Creates default indexes for optimal query performance.
3969
+ * Currently no default indexes are defined for workflows.
3970
+ */
3971
+ async createDefaultIndexes() {
3972
+ if (this.#skipDefaultIndexes) {
3973
+ return;
3974
+ }
3975
+ }
3976
+ async init() {
3977
+ await this.#db.createTable({ tableName: storage.TABLE_WORKFLOW_SNAPSHOT, schema: storage.TABLE_SCHEMAS[storage.TABLE_WORKFLOW_SNAPSHOT] });
3978
+ await this.#db.alterTable({
3979
+ tableName: storage.TABLE_WORKFLOW_SNAPSHOT,
3980
+ schema: storage.TABLE_SCHEMAS[storage.TABLE_WORKFLOW_SNAPSHOT],
3981
+ ifNotExists: ["resourceId"]
3982
+ });
3983
+ await this.createDefaultIndexes();
3984
+ await this.createCustomIndexes();
3985
+ }
3986
+ /**
3987
+ * Creates custom user-defined indexes for this domain's tables.
3988
+ */
3989
+ async createCustomIndexes() {
3990
+ if (!this.#indexes || this.#indexes.length === 0) {
3991
+ return;
3992
+ }
3993
+ for (const indexDef of this.#indexes) {
3994
+ try {
3995
+ await this.#db.createIndex(indexDef);
3996
+ } catch (error) {
3997
+ this.logger?.warn?.(`Failed to create custom index ${indexDef.name}:`, error);
3998
+ }
3999
+ }
4000
+ }
4001
+ async dangerouslyClearAll() {
4002
+ await this.#db.clearTable({ tableName: storage.TABLE_WORKFLOW_SNAPSHOT });
4003
+ }
4004
+ async updateWorkflowResults({
4005
+ workflowName,
4006
+ runId,
4007
+ stepId,
4008
+ result,
4009
+ requestContext
4010
+ }) {
4011
+ try {
4012
+ const { result: context } = await withRetry(
4013
+ async () => {
4014
+ return this.#db.client.tx(async (t) => {
4015
+ const tableName = getTableName2({
4016
+ indexName: storage.TABLE_WORKFLOW_SNAPSHOT,
4017
+ schemaName: getSchemaName2(this.#schema)
4018
+ });
4019
+ const existingSnapshotResult = await t.oneOrNone(
4020
+ `SELECT snapshot FROM ${tableName} WHERE workflow_name = $1 AND run_id = $2`,
4021
+ [workflowName, runId]
4022
+ );
4023
+ let snapshot;
4024
+ if (!existingSnapshotResult) {
4025
+ snapshot = {
4026
+ context: {},
4027
+ activePaths: [],
4028
+ timestamp: Date.now(),
4029
+ suspendedPaths: {},
4030
+ activeStepsPath: {},
4031
+ resumeLabels: {},
4032
+ serializedStepGraph: [],
4033
+ status: "pending",
4034
+ value: {},
4035
+ waitingPaths: {},
4036
+ runId,
4037
+ requestContext: {}
4038
+ };
4039
+ } else {
4040
+ const existingSnapshot = existingSnapshotResult.snapshot;
4041
+ snapshot = typeof existingSnapshot === "string" ? JSON.parse(existingSnapshot) : existingSnapshot;
4042
+ }
4043
+ snapshot.context[stepId] = result;
4044
+ snapshot.requestContext = { ...snapshot.requestContext, ...requestContext };
4045
+ const now = /* @__PURE__ */ new Date();
4046
+ await t.none(
4047
+ `INSERT INTO ${tableName} (workflow_name, run_id, snapshot, "createdAt", "updatedAt")
4048
+ VALUES ($1, $2, $3, $4, $5)
4049
+ ON CONFLICT (workflow_name, run_id) DO UPDATE
4050
+ SET snapshot = $3, "updatedAt" = $5`,
4051
+ [workflowName, runId, JSON.stringify(snapshot), now, now]
4052
+ );
4053
+ return snapshot.context;
4054
+ });
4055
+ },
4056
+ {
4057
+ onRetry: (error, attempt, delay) => {
4058
+ this.logger?.warn?.(
4059
+ `updateWorkflowResults retry ${attempt} for workflow ${workflowName}/${runId} after ${delay}ms: ${error.message}`
4060
+ );
4061
+ }
4062
+ }
4063
+ );
4064
+ return context;
4065
+ } catch (error$1) {
4066
+ throw new error.MastraError(
4067
+ {
4068
+ id: storage.createStorageErrorId("DSQL", "UPDATE_WORKFLOW_RESULTS", "FAILED"),
4069
+ domain: error.ErrorDomain.STORAGE,
4070
+ category: error.ErrorCategory.THIRD_PARTY,
4071
+ details: {
4072
+ workflowName,
4073
+ runId,
4074
+ stepId
4075
+ }
4076
+ },
4077
+ error$1
4078
+ );
4079
+ }
4080
+ }
4081
+ async updateWorkflowState({
4082
+ workflowName,
4083
+ runId,
4084
+ opts
4085
+ }) {
4086
+ try {
4087
+ const { result } = await withRetry(
4088
+ async () => {
4089
+ return this.#db.client.tx(async (t) => {
4090
+ const tableName = getTableName2({
4091
+ indexName: storage.TABLE_WORKFLOW_SNAPSHOT,
4092
+ schemaName: getSchemaName2(this.#schema)
4093
+ });
4094
+ const existingSnapshotResult = await t.oneOrNone(
4095
+ `SELECT snapshot FROM ${tableName} WHERE workflow_name = $1 AND run_id = $2`,
4096
+ [workflowName, runId]
4097
+ );
4098
+ if (!existingSnapshotResult) {
4099
+ return void 0;
4100
+ }
4101
+ const existingSnapshot = existingSnapshotResult.snapshot;
4102
+ const snapshot = typeof existingSnapshot === "string" ? JSON.parse(existingSnapshot) : existingSnapshot;
4103
+ if (!snapshot || !snapshot?.context) {
4104
+ throw new Error(`Snapshot not found for runId ${runId}`);
4105
+ }
4106
+ const updatedSnapshot = { ...snapshot, ...opts };
4107
+ await t.none(
4108
+ `UPDATE ${tableName} SET snapshot = $1, "updatedAt" = $2 WHERE workflow_name = $3 AND run_id = $4`,
4109
+ [JSON.stringify(updatedSnapshot), /* @__PURE__ */ new Date(), workflowName, runId]
4110
+ );
4111
+ return updatedSnapshot;
4112
+ });
4113
+ },
4114
+ {
4115
+ onRetry: (error, attempt, delay) => {
4116
+ this.logger?.warn?.(
4117
+ `updateWorkflowState retry ${attempt} for workflow ${workflowName}/${runId} after ${delay}ms: ${error.message}`
4118
+ );
4119
+ }
4120
+ }
4121
+ );
4122
+ return result;
4123
+ } catch (error$1) {
4124
+ throw new error.MastraError(
4125
+ {
4126
+ id: storage.createStorageErrorId("DSQL", "UPDATE_WORKFLOW_STATE", "FAILED"),
4127
+ domain: error.ErrorDomain.STORAGE,
4128
+ category: error.ErrorCategory.THIRD_PARTY,
4129
+ details: {
4130
+ workflowName,
4131
+ runId
4132
+ }
4133
+ },
4134
+ error$1
4135
+ );
4136
+ }
4137
+ }
4138
+ async persistWorkflowSnapshot({
4139
+ workflowName,
4140
+ runId,
4141
+ resourceId,
4142
+ snapshot,
4143
+ createdAt,
4144
+ updatedAt
4145
+ }) {
4146
+ try {
4147
+ const now = /* @__PURE__ */ new Date();
4148
+ const createdAtValue = createdAt ? createdAt : now;
4149
+ const updatedAtValue = updatedAt ? updatedAt : now;
4150
+ await this.#db.client.none(
4151
+ `INSERT INTO ${getTableName2({ indexName: storage.TABLE_WORKFLOW_SNAPSHOT, schemaName: getSchemaName2(this.#schema) })} (workflow_name, run_id, "resourceId", snapshot, "createdAt", "updatedAt")
4152
+ VALUES ($1, $2, $3, $4, $5, $6)
4153
+ ON CONFLICT (workflow_name, run_id) DO UPDATE
4154
+ SET "resourceId" = $3, snapshot = $4, "updatedAt" = $6`,
4155
+ [
4156
+ workflowName,
4157
+ runId,
4158
+ resourceId,
4159
+ JSON.stringify(snapshot),
4160
+ createdAtValue.toISOString(),
4161
+ updatedAtValue.toISOString()
4162
+ ]
4163
+ );
4164
+ } catch (error$1) {
4165
+ throw new error.MastraError(
4166
+ {
4167
+ id: storage.createStorageErrorId("DSQL", "PERSIST_WORKFLOW_SNAPSHOT", "FAILED"),
4168
+ domain: error.ErrorDomain.STORAGE,
4169
+ category: error.ErrorCategory.THIRD_PARTY
4170
+ },
4171
+ error$1
4172
+ );
4173
+ }
4174
+ }
4175
+ async loadWorkflowSnapshot({
4176
+ workflowName,
4177
+ runId
4178
+ }) {
4179
+ try {
4180
+ const result = await this.#db.load({
4181
+ tableName: storage.TABLE_WORKFLOW_SNAPSHOT,
4182
+ keys: { workflow_name: workflowName, run_id: runId }
4183
+ });
4184
+ return result ? result.snapshot : null;
4185
+ } catch (error$1) {
4186
+ throw new error.MastraError(
4187
+ {
4188
+ id: storage.createStorageErrorId("DSQL", "LOAD_WORKFLOW_SNAPSHOT", "FAILED"),
4189
+ domain: error.ErrorDomain.STORAGE,
4190
+ category: error.ErrorCategory.THIRD_PARTY
4191
+ },
4192
+ error$1
4193
+ );
4194
+ }
4195
+ }
4196
+ async getWorkflowRunById({
4197
+ runId,
4198
+ workflowName
4199
+ }) {
4200
+ try {
4201
+ const conditions = [];
4202
+ const values = [];
4203
+ let paramIndex = 1;
4204
+ if (runId) {
4205
+ conditions.push(`run_id = $${paramIndex}`);
4206
+ values.push(runId);
4207
+ paramIndex++;
4208
+ }
4209
+ if (workflowName) {
4210
+ conditions.push(`workflow_name = $${paramIndex}`);
4211
+ values.push(workflowName);
4212
+ paramIndex++;
4213
+ }
4214
+ const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
4215
+ const query = `
4216
+ SELECT * FROM ${getTableName2({ indexName: storage.TABLE_WORKFLOW_SNAPSHOT, schemaName: getSchemaName2(this.#schema) })}
4217
+ ${whereClause}
4218
+ ORDER BY "createdAt" DESC LIMIT 1
4219
+ `;
4220
+ const queryValues = values;
4221
+ const result = await this.#db.client.oneOrNone(query, queryValues);
4222
+ if (!result) {
4223
+ return null;
4224
+ }
4225
+ return parseWorkflowRun(result);
4226
+ } catch (error$1) {
4227
+ throw new error.MastraError(
4228
+ {
4229
+ id: storage.createStorageErrorId("DSQL", "GET_WORKFLOW_RUN_BY_ID", "FAILED"),
4230
+ domain: error.ErrorDomain.STORAGE,
4231
+ category: error.ErrorCategory.THIRD_PARTY,
4232
+ details: {
4233
+ runId,
4234
+ workflowName: workflowName || ""
4235
+ }
4236
+ },
4237
+ error$1
4238
+ );
4239
+ }
4240
+ }
4241
+ async deleteWorkflowRunById({ runId, workflowName }) {
4242
+ try {
4243
+ await this.#db.client.none(
4244
+ `DELETE FROM ${getTableName2({ indexName: storage.TABLE_WORKFLOW_SNAPSHOT, schemaName: getSchemaName2(this.#schema) })} WHERE run_id = $1 AND workflow_name = $2`,
4245
+ [runId, workflowName]
4246
+ );
4247
+ } catch (error$1) {
4248
+ throw new error.MastraError(
4249
+ {
4250
+ id: storage.createStorageErrorId("DSQL", "DELETE_WORKFLOW_RUN_BY_ID", "FAILED"),
4251
+ domain: error.ErrorDomain.STORAGE,
4252
+ category: error.ErrorCategory.THIRD_PARTY,
4253
+ details: {
4254
+ runId,
4255
+ workflowName
4256
+ }
4257
+ },
4258
+ error$1
4259
+ );
4260
+ }
4261
+ }
4262
+ async listWorkflowRuns({
4263
+ workflowName,
4264
+ fromDate,
4265
+ toDate,
4266
+ perPage,
4267
+ page,
4268
+ resourceId,
4269
+ status
4270
+ } = {}) {
4271
+ try {
4272
+ const conditions = [];
4273
+ const values = [];
4274
+ let paramIndex = 1;
4275
+ if (workflowName) {
4276
+ conditions.push(`workflow_name = $${paramIndex}`);
4277
+ values.push(workflowName);
4278
+ paramIndex++;
4279
+ }
4280
+ if (status) {
4281
+ conditions.push(`snapshot::jsonb ->> 'status' = $${paramIndex}`);
4282
+ values.push(status);
4283
+ paramIndex++;
4284
+ }
4285
+ if (resourceId) {
4286
+ const hasResourceId = await this.#db.hasColumn(storage.TABLE_WORKFLOW_SNAPSHOT, "resourceId");
4287
+ if (hasResourceId) {
4288
+ conditions.push(`"resourceId" = $${paramIndex}`);
4289
+ values.push(resourceId);
4290
+ paramIndex++;
4291
+ } else {
4292
+ console.warn(`[${storage.TABLE_WORKFLOW_SNAPSHOT}] resourceId column not found. Skipping resourceId filter.`);
4293
+ }
4294
+ }
4295
+ if (fromDate) {
4296
+ conditions.push(`"createdAt" >= $${paramIndex}`);
4297
+ values.push(fromDate instanceof Date ? fromDate.toISOString() : fromDate);
4298
+ paramIndex++;
4299
+ }
4300
+ if (toDate) {
4301
+ conditions.push(`"createdAt" <= $${paramIndex}`);
4302
+ values.push(toDate instanceof Date ? toDate.toISOString() : toDate);
4303
+ paramIndex++;
4304
+ }
4305
+ const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
4306
+ let total = 0;
4307
+ const usePagination = typeof perPage === "number" && typeof page === "number";
4308
+ if (usePagination) {
4309
+ const countResult = await this.#db.client.one(
4310
+ `SELECT COUNT(*) as count FROM ${getTableName2({ indexName: storage.TABLE_WORKFLOW_SNAPSHOT, schemaName: getSchemaName2(this.#schema) })} ${whereClause}`,
4311
+ values
4312
+ );
4313
+ total = Number(countResult.count);
4314
+ }
4315
+ const normalizedPerPage = usePagination ? storage.normalizePerPage(perPage, Number.MAX_SAFE_INTEGER) : 0;
4316
+ const offset = usePagination ? page * normalizedPerPage : void 0;
4317
+ const query = `
4318
+ SELECT * FROM ${getTableName2({ indexName: storage.TABLE_WORKFLOW_SNAPSHOT, schemaName: getSchemaName2(this.#schema) })}
4319
+ ${whereClause}
4320
+ ORDER BY "createdAt" DESC
4321
+ ${usePagination ? ` LIMIT $${paramIndex} OFFSET $${paramIndex + 1}` : ""}
4322
+ `;
4323
+ const queryValues = usePagination ? [...values, normalizedPerPage, offset] : values;
4324
+ const result = await this.#db.client.manyOrNone(query, queryValues);
4325
+ const runs = (result || []).map((row) => {
4326
+ return parseWorkflowRun(row);
4327
+ });
4328
+ return { runs, total: total || runs.length };
4329
+ } catch (error$1) {
4330
+ throw new error.MastraError(
4331
+ {
4332
+ id: storage.createStorageErrorId("DSQL", "LIST_WORKFLOW_RUNS", "FAILED"),
4333
+ domain: error.ErrorDomain.STORAGE,
4334
+ category: error.ErrorCategory.THIRD_PARTY,
4335
+ details: {
4336
+ workflowName: workflowName || "all"
4337
+ }
4338
+ },
4339
+ error$1
4340
+ );
4341
+ }
4342
+ }
4343
+ };
4344
+
4345
+ // src/storage/index.ts
4346
+ var DSQLStore = class extends storage.MastraStorage {
4347
+ #pool;
4348
+ #db;
4349
+ #ownsPool;
4350
+ schema;
4351
+ isInitialized = false;
4352
+ stores;
4353
+ constructor(config) {
4354
+ try {
4355
+ validateConfig(config);
4356
+ super({ id: config.id, name: "DSQLStore", disableInit: config.disableInit });
4357
+ this.schema = config.schemaName || "public";
4358
+ if (isPoolConfig(config)) {
4359
+ this.#pool = config.pool;
4360
+ this.#ownsPool = false;
4361
+ } else {
4362
+ this.#pool = this.createPool(config);
4363
+ this.#ownsPool = true;
4364
+ }
4365
+ this.#db = new PoolAdapter(this.#pool);
4366
+ const domainConfig = {
4367
+ client: this.#db,
4368
+ schemaName: this.schema,
4369
+ skipDefaultIndexes: config.skipDefaultIndexes,
4370
+ indexes: config.indexes
4371
+ };
4372
+ this.stores = {
4373
+ scores: new ScoresDSQL(domainConfig),
4374
+ workflows: new WorkflowsDSQL(domainConfig),
4375
+ memory: new MemoryDSQL(domainConfig),
4376
+ observability: new ObservabilityDSQL(domainConfig),
4377
+ agents: new AgentsDSQL(domainConfig)
4378
+ };
4379
+ } catch (e) {
4380
+ throw new error.MastraError(
4381
+ {
4382
+ id: storage.createStorageErrorId("DSQL", "INITIALIZATION", "FAILED"),
4383
+ domain: error.ErrorDomain.STORAGE,
4384
+ category: error.ErrorCategory.USER
4385
+ },
4386
+ e
4387
+ );
4388
+ }
4389
+ }
4390
+ /**
4391
+ * Creates a connection pool with AuroraDSQLClient for IAM authentication.
4392
+ */
4393
+ createPool(config) {
4394
+ if (!isHostConfig(config)) {
4395
+ throw new Error("DSQLStore: Invalid configuration for creating pool.");
4396
+ }
4397
+ const region = getEffectiveRegion(config);
4398
+ const poolConfig = {
4399
+ host: config.host,
4400
+ user: config.user ?? "admin",
4401
+ database: config.database ?? "postgres",
4402
+ // Use AuroraDSQLClient for automatic IAM token generation
4403
+ Client: auroraDsqlNodePostgresConnector.AuroraDSQLClient,
4404
+ // Pass region for IAM token generation
4405
+ region,
4406
+ // Custom credentials provider (optional)
4407
+ customCredentialsProvider: config.customCredentialsProvider,
4408
+ // Pool settings optimized for Aurora DSQL
4409
+ max: config.max ?? DSQL_POOL_DEFAULTS.max,
4410
+ min: config.min ?? DSQL_POOL_DEFAULTS.min,
4411
+ idleTimeoutMillis: config.idleTimeoutMillis ?? DSQL_POOL_DEFAULTS.idleTimeoutMillis,
4412
+ maxLifetimeSeconds: config.maxLifetimeSeconds ?? DSQL_POOL_DEFAULTS.maxLifetimeSeconds,
4413
+ connectionTimeoutMillis: config.connectionTimeoutMillis ?? DSQL_POOL_DEFAULTS.connectionTimeoutMillis,
4414
+ allowExitOnIdle: config.allowExitOnIdle ?? DSQL_POOL_DEFAULTS.allowExitOnIdle
4415
+ };
4416
+ return new pg.Pool(poolConfig);
4417
+ }
4418
+ async init() {
4419
+ if (this.isInitialized) {
4420
+ return;
4421
+ }
4422
+ try {
4423
+ this.isInitialized = true;
4424
+ await super.init();
4425
+ } catch (error$1) {
4426
+ this.isInitialized = false;
4427
+ throw new error.MastraError(
4428
+ {
4429
+ id: storage.createStorageErrorId("DSQL", "INIT", "FAILED"),
4430
+ domain: error.ErrorDomain.STORAGE,
4431
+ category: error.ErrorCategory.THIRD_PARTY
4432
+ },
4433
+ error$1
4434
+ );
4435
+ }
4436
+ }
4437
+ /**
4438
+ * Database client for executing queries.
4439
+ *
4440
+ * @example
4441
+ * ```typescript
4442
+ * const rows = await store.db.any('SELECT * FROM users WHERE active = $1', [true]);
4443
+ * const user = await store.db.one('SELECT * FROM users WHERE id = $1', [userId]);
4444
+ * ```
4445
+ */
4446
+ get db() {
4447
+ return this.#db;
4448
+ }
4449
+ /**
4450
+ * The underlying pg.Pool for direct database access or ORM integration.
4451
+ */
4452
+ get pool() {
4453
+ return this.#pool;
4454
+ }
4455
+ /**
4456
+ * Closes the connection pool if it was created by this store.
4457
+ * If a pool was passed in via config, it will not be closed.
4458
+ */
4459
+ async close() {
4460
+ if (this.#ownsPool) {
4461
+ await this.#pool.end();
4462
+ }
4463
+ this.isInitialized = false;
4464
+ }
4465
+ };
4466
+
4467
+ exports.AgentsDSQL = AgentsDSQL;
4468
+ exports.DSQLStore = DSQLStore;
4469
+ exports.DSQL_POOL_DEFAULTS = DSQL_POOL_DEFAULTS;
4470
+ exports.MemoryDSQL = MemoryDSQL;
4471
+ exports.ObservabilityDSQL = ObservabilityDSQL;
4472
+ exports.PoolAdapter = PoolAdapter;
4473
+ exports.ScoresDSQL = ScoresDSQL;
4474
+ exports.WorkflowsDSQL = WorkflowsDSQL;
4475
+ //# sourceMappingURL=index.cjs.map
4476
+ //# sourceMappingURL=index.cjs.map