@mastra/mssql 0.0.0-remove-unused-import-20250909212718 → 0.0.0-remove-unused-model-providers-api-20251030210744

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,8 +1,10 @@
1
1
  import { MastraError, ErrorCategory, ErrorDomain } from '@mastra/core/error';
2
- import { MastraStorage, LegacyEvalsStorage, StoreOperations, TABLE_WORKFLOW_SNAPSHOT, ScoresStorage, TABLE_SCORERS, TracesStorage, TABLE_TRACES, WorkflowsStorage, MemoryStorage, resolveMessageLimit, TABLE_RESOURCES, TABLE_EVALS, TABLE_THREADS, TABLE_MESSAGES } from '@mastra/core/storage';
2
+ import { MastraStorage, StoreOperations, TABLE_WORKFLOW_SNAPSHOT, TABLE_SCHEMAS, TABLE_THREADS, TABLE_MESSAGES, TABLE_TRACES, TABLE_SCORERS, TABLE_AI_SPANS, ScoresStorage, WorkflowsStorage, MemoryStorage, resolveMessageLimit, TABLE_RESOURCES, ObservabilityStorage, safelyParseJSON } from '@mastra/core/storage';
3
3
  import sql2 from 'mssql';
4
- import { parseSqlIdentifier, parseFieldKey } from '@mastra/core/utils';
5
4
  import { MessageList } from '@mastra/core/agent';
5
+ import { parseSqlIdentifier } from '@mastra/core/utils';
6
+ import { randomUUID } from 'crypto';
7
+ import { saveScorePayloadSchema } from '@mastra/core/scores';
6
8
 
7
9
  // src/storage/index.ts
8
10
  function getSchemaName(schema) {
@@ -14,154 +16,71 @@ function getTableName({ indexName, schemaName }) {
14
16
  const quotedSchemaName = schemaName;
15
17
  return quotedSchemaName ? `${quotedSchemaName}.${quotedIndexName}` : quotedIndexName;
16
18
  }
17
-
18
- // src/storage/domains/legacy-evals/index.ts
19
- function transformEvalRow(row) {
20
- let testInfoValue = null, resultValue = null;
21
- if (row.test_info) {
22
- try {
23
- testInfoValue = typeof row.test_info === "string" ? JSON.parse(row.test_info) : row.test_info;
24
- } catch {
25
- }
19
+ function buildDateRangeFilter(dateRange, fieldName) {
20
+ const filters = {};
21
+ if (dateRange?.start) {
22
+ filters[`${fieldName}_gte`] = dateRange.start;
26
23
  }
27
- if (row.test_info) {
28
- try {
29
- resultValue = typeof row.result === "string" ? JSON.parse(row.result) : row.result;
30
- } catch {
31
- }
24
+ if (dateRange?.end) {
25
+ filters[`${fieldName}_lte`] = dateRange.end;
32
26
  }
27
+ return filters;
28
+ }
29
+ function prepareWhereClause(filters, _schema) {
30
+ const conditions = [];
31
+ const params = {};
32
+ let paramIndex = 1;
33
+ Object.entries(filters).forEach(([key, value]) => {
34
+ if (value === void 0) return;
35
+ const paramName = `p${paramIndex++}`;
36
+ if (key.endsWith("_gte")) {
37
+ const fieldName = key.slice(0, -4);
38
+ conditions.push(`[${parseSqlIdentifier(fieldName, "field name")}] >= @${paramName}`);
39
+ params[paramName] = value instanceof Date ? value.toISOString() : value;
40
+ } else if (key.endsWith("_lte")) {
41
+ const fieldName = key.slice(0, -4);
42
+ conditions.push(`[${parseSqlIdentifier(fieldName, "field name")}] <= @${paramName}`);
43
+ params[paramName] = value instanceof Date ? value.toISOString() : value;
44
+ } else if (value === null) {
45
+ conditions.push(`[${parseSqlIdentifier(key, "field name")}] IS NULL`);
46
+ } else {
47
+ conditions.push(`[${parseSqlIdentifier(key, "field name")}] = @${paramName}`);
48
+ params[paramName] = value instanceof Date ? value.toISOString() : value;
49
+ }
50
+ });
33
51
  return {
34
- agentName: row.agent_name,
35
- input: row.input,
36
- output: row.output,
37
- result: resultValue,
38
- metricName: row.metric_name,
39
- instructions: row.instructions,
40
- testInfo: testInfoValue,
41
- globalRunId: row.global_run_id,
42
- runId: row.run_id,
43
- createdAt: row.created_at
52
+ sql: conditions.length > 0 ? ` WHERE ${conditions.join(" AND ")}` : "",
53
+ params
44
54
  };
45
55
  }
46
- var LegacyEvalsMSSQL = class extends LegacyEvalsStorage {
47
- pool;
48
- schema;
49
- constructor({ pool, schema }) {
50
- super();
51
- this.pool = pool;
52
- this.schema = schema;
53
- }
54
- /** @deprecated use getEvals instead */
55
- async getEvalsByAgentName(agentName, type) {
56
- try {
57
- let query = `SELECT * FROM ${getTableName({ indexName: TABLE_EVALS, schemaName: getSchemaName(this.schema) })} WHERE agent_name = @p1`;
58
- if (type === "test") {
59
- query += " AND test_info IS NOT NULL AND JSON_VALUE(test_info, '$.testPath') IS NOT NULL";
60
- } else if (type === "live") {
61
- query += " AND (test_info IS NULL OR JSON_VALUE(test_info, '$.testPath') IS NULL)";
62
- }
63
- query += " ORDER BY created_at DESC";
64
- const request = this.pool.request();
65
- request.input("p1", agentName);
66
- const result = await request.query(query);
67
- const rows = result.recordset;
68
- return typeof transformEvalRow === "function" ? rows?.map((row) => transformEvalRow(row)) ?? [] : rows ?? [];
69
- } catch (error) {
70
- if (error && error.number === 208 && error.message && error.message.includes("Invalid object name")) {
71
- return [];
72
- }
73
- console.error("Failed to get evals for the specified agent: " + error?.message);
74
- throw error;
75
- }
76
- }
77
- async getEvals(options = {}) {
78
- const { agentName, type, page = 0, perPage = 100, dateRange } = options;
79
- const fromDate = dateRange?.start;
80
- const toDate = dateRange?.end;
81
- const where = [];
82
- const params = {};
83
- if (agentName) {
84
- where.push("agent_name = @agentName");
85
- params["agentName"] = agentName;
86
- }
87
- if (type === "test") {
88
- where.push("test_info IS NOT NULL AND JSON_VALUE(test_info, '$.testPath') IS NOT NULL");
89
- } else if (type === "live") {
90
- where.push("(test_info IS NULL OR JSON_VALUE(test_info, '$.testPath') IS NULL)");
91
- }
92
- if (fromDate instanceof Date && !isNaN(fromDate.getTime())) {
93
- where.push(`[created_at] >= @fromDate`);
94
- params[`fromDate`] = fromDate.toISOString();
95
- }
96
- if (toDate instanceof Date && !isNaN(toDate.getTime())) {
97
- where.push(`[created_at] <= @toDate`);
98
- params[`toDate`] = toDate.toISOString();
99
- }
100
- const whereClause = where.length > 0 ? `WHERE ${where.join(" AND ")}` : "";
101
- const tableName = getTableName({ indexName: TABLE_EVALS, schemaName: getSchemaName(this.schema) });
102
- const offset = page * perPage;
103
- const countQuery = `SELECT COUNT(*) as total FROM ${tableName} ${whereClause}`;
104
- const dataQuery = `SELECT * FROM ${tableName} ${whereClause} ORDER BY seq_id DESC OFFSET @offset ROWS FETCH NEXT @perPage ROWS ONLY`;
105
- try {
106
- const countReq = this.pool.request();
107
- Object.entries(params).forEach(([key, value]) => {
108
- if (value instanceof Date) {
109
- countReq.input(key, sql2.DateTime, value);
110
- } else {
111
- countReq.input(key, value);
112
- }
113
- });
114
- const countResult = await countReq.query(countQuery);
115
- const total = countResult.recordset[0]?.total || 0;
116
- if (total === 0) {
117
- return {
118
- evals: [],
119
- total: 0,
120
- page,
121
- perPage,
122
- hasMore: false
123
- };
56
+ function transformFromSqlRow({
57
+ tableName,
58
+ sqlRow
59
+ }) {
60
+ const schema = TABLE_SCHEMAS[tableName];
61
+ const result = {};
62
+ Object.entries(sqlRow).forEach(([key, value]) => {
63
+ const columnSchema = schema?.[key];
64
+ if (columnSchema?.type === "jsonb" && typeof value === "string") {
65
+ try {
66
+ result[key] = JSON.parse(value);
67
+ } catch {
68
+ result[key] = value;
124
69
  }
125
- const req = this.pool.request();
126
- Object.entries(params).forEach(([key, value]) => {
127
- if (value instanceof Date) {
128
- req.input(key, sql2.DateTime, value);
129
- } else {
130
- req.input(key, value);
131
- }
132
- });
133
- req.input("offset", offset);
134
- req.input("perPage", perPage);
135
- const result = await req.query(dataQuery);
136
- const rows = result.recordset;
137
- return {
138
- evals: rows?.map((row) => transformEvalRow(row)) ?? [],
139
- total,
140
- page,
141
- perPage,
142
- hasMore: offset + (rows?.length ?? 0) < total
143
- };
144
- } catch (error) {
145
- const mastraError = new MastraError(
146
- {
147
- id: "MASTRA_STORAGE_MSSQL_STORE_GET_EVALS_FAILED",
148
- domain: ErrorDomain.STORAGE,
149
- category: ErrorCategory.THIRD_PARTY,
150
- details: {
151
- agentName: agentName || "all",
152
- type: type || "all",
153
- page,
154
- perPage
155
- }
156
- },
157
- error
158
- );
159
- this.logger?.error?.(mastraError.toString());
160
- this.logger?.trackException(mastraError);
161
- throw mastraError;
70
+ } else if (columnSchema?.type === "timestamp" && value && typeof value === "string") {
71
+ result[key] = new Date(value);
72
+ } else if (columnSchema?.type === "timestamp" && value instanceof Date) {
73
+ result[key] = value;
74
+ } else if (columnSchema?.type === "boolean") {
75
+ result[key] = Boolean(value);
76
+ } else {
77
+ result[key] = value;
162
78
  }
163
- }
164
- };
79
+ });
80
+ return result;
81
+ }
82
+
83
+ // src/storage/domains/memory/index.ts
165
84
  var MemoryMSSQL = class extends MemoryStorage {
166
85
  pool;
167
86
  schema;
@@ -193,7 +112,7 @@ var MemoryMSSQL = class extends MemoryStorage {
193
112
  }
194
113
  async getThreadById({ threadId }) {
195
114
  try {
196
- const sql7 = `SELECT
115
+ const sql5 = `SELECT
197
116
  id,
198
117
  [resourceId],
199
118
  title,
@@ -204,7 +123,7 @@ var MemoryMSSQL = class extends MemoryStorage {
204
123
  WHERE id = @threadId`;
205
124
  const request = this.pool.request();
206
125
  request.input("threadId", threadId);
207
- const resultSet = await request.query(sql7);
126
+ const resultSet = await request.query(sql5);
208
127
  const thread = resultSet.recordset[0] || null;
209
128
  if (!thread) {
210
129
  return null;
@@ -250,7 +169,8 @@ var MemoryMSSQL = class extends MemoryStorage {
250
169
  };
251
170
  }
252
171
  const orderByField = orderBy === "createdAt" ? "[createdAt]" : "[updatedAt]";
253
- const dataQuery = `SELECT id, [resourceId], title, metadata, [createdAt], [updatedAt] ${baseQuery} ORDER BY ${orderByField} ${sortDirection} OFFSET @offset ROWS FETCH NEXT @perPage ROWS ONLY`;
172
+ const dir = (sortDirection || "DESC").toUpperCase() === "ASC" ? "ASC" : "DESC";
173
+ const dataQuery = `SELECT id, [resourceId], title, metadata, [createdAt], [updatedAt] ${baseQuery} ORDER BY ${orderByField} ${dir} OFFSET @offset ROWS FETCH NEXT @perPage ROWS ONLY`;
254
174
  const dataRequest = this.pool.request();
255
175
  dataRequest.input("resourceId", resourceId);
256
176
  dataRequest.input("perPage", perPage);
@@ -307,7 +227,12 @@ var MemoryMSSQL = class extends MemoryStorage {
307
227
  req.input("id", thread.id);
308
228
  req.input("resourceId", thread.resourceId);
309
229
  req.input("title", thread.title);
310
- req.input("metadata", thread.metadata ? JSON.stringify(thread.metadata) : null);
230
+ const metadata = thread.metadata ? JSON.stringify(thread.metadata) : null;
231
+ if (metadata === null) {
232
+ req.input("metadata", sql2.NVarChar, null);
233
+ } else {
234
+ req.input("metadata", metadata);
235
+ }
311
236
  req.input("createdAt", sql2.DateTime2, thread.createdAt);
312
237
  req.input("updatedAt", sql2.DateTime2, thread.updatedAt);
313
238
  await req.query(mergeSql);
@@ -334,7 +259,8 @@ var MemoryMSSQL = class extends MemoryStorage {
334
259
  try {
335
260
  const baseQuery = `FROM ${getTableName({ indexName: TABLE_THREADS, schemaName: getSchemaName(this.schema) })} WHERE [resourceId] = @resourceId`;
336
261
  const orderByField = orderBy === "createdAt" ? "[createdAt]" : "[updatedAt]";
337
- const dataQuery = `SELECT id, [resourceId], title, metadata, [createdAt], [updatedAt] ${baseQuery} ORDER BY ${orderByField} ${sortDirection}`;
262
+ const dir = (sortDirection || "DESC").toUpperCase() === "ASC" ? "ASC" : "DESC";
263
+ const dataQuery = `SELECT id, [resourceId], title, metadata, [createdAt], [updatedAt] ${baseQuery} ORDER BY ${orderByField} ${dir}`;
338
264
  const request = this.pool.request();
339
265
  request.input("resourceId", resourceId);
340
266
  const resultSet = await request.query(dataQuery);
@@ -377,7 +303,7 @@ var MemoryMSSQL = class extends MemoryStorage {
377
303
  };
378
304
  try {
379
305
  const table = getTableName({ indexName: TABLE_THREADS, schemaName: getSchemaName(this.schema) });
380
- const sql7 = `UPDATE ${table}
306
+ const sql5 = `UPDATE ${table}
381
307
  SET title = @title,
382
308
  metadata = @metadata,
383
309
  [updatedAt] = @updatedAt
@@ -388,7 +314,7 @@ var MemoryMSSQL = class extends MemoryStorage {
388
314
  req.input("title", title);
389
315
  req.input("metadata", JSON.stringify(mergedMetadata));
390
316
  req.input("updatedAt", /* @__PURE__ */ new Date());
391
- const result = await req.query(sql7);
317
+ const result = await req.query(sql5);
392
318
  let thread = result.recordset && result.recordset[0];
393
319
  if (thread && "seq_id" in thread) {
394
320
  const { seq_id, ...rest } = thread;
@@ -458,8 +384,7 @@ var MemoryMSSQL = class extends MemoryStorage {
458
384
  }
459
385
  async _getIncludedMessages({
460
386
  threadId,
461
- selectBy,
462
- orderByStatement
387
+ selectBy
463
388
  }) {
464
389
  if (!threadId.trim()) throw new Error("threadId must be a non-empty string");
465
390
  const include = selectBy?.include;
@@ -487,7 +412,7 @@ var MemoryMSSQL = class extends MemoryStorage {
487
412
  m.[resourceId],
488
413
  m.seq_id
489
414
  FROM (
490
- SELECT *, ROW_NUMBER() OVER (${orderByStatement}) as row_num
415
+ SELECT *, ROW_NUMBER() OVER (ORDER BY [createdAt] ASC) as row_num
491
416
  FROM ${getTableName({ indexName: TABLE_MESSAGES, schemaName: getSchemaName(this.schema) })}
492
417
  WHERE [thread_id] = ${pThreadId}
493
418
  ) AS m
@@ -495,15 +420,17 @@ var MemoryMSSQL = class extends MemoryStorage {
495
420
  OR EXISTS (
496
421
  SELECT 1
497
422
  FROM (
498
- SELECT *, ROW_NUMBER() OVER (${orderByStatement}) as row_num
423
+ SELECT *, ROW_NUMBER() OVER (ORDER BY [createdAt] ASC) as row_num
499
424
  FROM ${getTableName({ indexName: TABLE_MESSAGES, schemaName: getSchemaName(this.schema) })}
500
425
  WHERE [thread_id] = ${pThreadId}
501
426
  ) AS target
502
427
  WHERE target.id = ${pId}
503
428
  AND (
504
- (m.row_num <= target.row_num + ${pPrev} AND m.row_num > target.row_num)
429
+ -- Get previous messages (messages that come BEFORE the target)
430
+ (m.row_num < target.row_num AND m.row_num >= target.row_num - ${pPrev})
505
431
  OR
506
- (m.row_num >= target.row_num - ${pNext} AND m.row_num < target.row_num)
432
+ -- Get next messages (messages that come AFTER the target)
433
+ (m.row_num > target.row_num AND m.row_num <= target.row_num + ${pNext})
507
434
  )
508
435
  )
509
436
  `
@@ -542,7 +469,7 @@ var MemoryMSSQL = class extends MemoryStorage {
542
469
  let rows = [];
543
470
  const include = selectBy?.include || [];
544
471
  if (include?.length) {
545
- const includeMessages = await this._getIncludedMessages({ threadId, selectBy, orderByStatement });
472
+ const includeMessages = await this._getIncludedMessages({ threadId, selectBy });
546
473
  if (includeMessages) {
547
474
  rows.push(...includeMessages);
548
475
  }
@@ -583,14 +510,11 @@ var MemoryMSSQL = class extends MemoryStorage {
583
510
  error
584
511
  );
585
512
  this.logger?.error?.(mastraError.toString());
586
- this.logger?.trackException(mastraError);
513
+ this.logger?.trackException?.(mastraError);
587
514
  return [];
588
515
  }
589
516
  }
590
- async getMessagesById({
591
- messageIds,
592
- format
593
- }) {
517
+ async listMessagesById({ messageIds }) {
594
518
  if (messageIds.length === 0) return [];
595
519
  const selectStatement = `SELECT seq_id, id, content, role, type, [createdAt], thread_id AS threadId, resourceId`;
596
520
  const orderByStatement = `ORDER BY [seq_id] DESC`;
@@ -608,8 +532,8 @@ var MemoryMSSQL = class extends MemoryStorage {
608
532
  return timeDiff;
609
533
  });
610
534
  rows = rows.map(({ seq_id, ...rest }) => rest);
611
- if (format === `v1`) return this._parseAndFormatMessages(rows, format);
612
- return this._parseAndFormatMessages(rows, `v2`);
535
+ const messages = this._parseAndFormatMessages(rows, `v2`);
536
+ return messages;
613
537
  } catch (error) {
614
538
  const mastraError = new MastraError(
615
539
  {
@@ -623,10 +547,139 @@ var MemoryMSSQL = class extends MemoryStorage {
623
547
  error
624
548
  );
625
549
  this.logger?.error?.(mastraError.toString());
626
- this.logger?.trackException(mastraError);
550
+ this.logger?.trackException?.(mastraError);
627
551
  return [];
628
552
  }
629
553
  }
554
+ async listMessages(args) {
555
+ const { threadId, resourceId, include, filter, limit, offset = 0, orderBy } = args;
556
+ if (!threadId.trim()) {
557
+ throw new MastraError(
558
+ {
559
+ id: "STORAGE_MSSQL_LIST_MESSAGES_INVALID_THREAD_ID",
560
+ domain: ErrorDomain.STORAGE,
561
+ category: ErrorCategory.THIRD_PARTY,
562
+ details: { threadId }
563
+ },
564
+ new Error("threadId must be a non-empty string")
565
+ );
566
+ }
567
+ try {
568
+ let perPage = 40;
569
+ if (limit !== void 0) {
570
+ if (limit === false) {
571
+ perPage = Number.MAX_SAFE_INTEGER;
572
+ } else if (limit === 0) {
573
+ perPage = 0;
574
+ } else if (typeof limit === "number" && limit > 0) {
575
+ perPage = limit;
576
+ }
577
+ }
578
+ const page = perPage === 0 ? 0 : Math.floor(offset / perPage);
579
+ const sortField = orderBy?.field || "createdAt";
580
+ const sortDirection = orderBy?.direction || "DESC";
581
+ const orderByStatement = `ORDER BY [${sortField}] ${sortDirection}`;
582
+ const selectStatement = `SELECT seq_id, id, content, role, type, [createdAt], thread_id AS threadId, resourceId`;
583
+ const tableName = getTableName({ indexName: TABLE_MESSAGES, schemaName: getSchemaName(this.schema) });
584
+ const conditions = ["[thread_id] = @threadId"];
585
+ const request = this.pool.request();
586
+ request.input("threadId", threadId);
587
+ if (resourceId) {
588
+ conditions.push("[resourceId] = @resourceId");
589
+ request.input("resourceId", resourceId);
590
+ }
591
+ if (filter?.dateRange?.start) {
592
+ conditions.push("[createdAt] >= @fromDate");
593
+ request.input("fromDate", filter.dateRange.start);
594
+ }
595
+ if (filter?.dateRange?.end) {
596
+ conditions.push("[createdAt] <= @toDate");
597
+ request.input("toDate", filter.dateRange.end);
598
+ }
599
+ const whereClause = `WHERE ${conditions.join(" AND ")}`;
600
+ const countQuery = `SELECT COUNT(*) as total FROM ${tableName} ${whereClause}`;
601
+ const countResult = await request.query(countQuery);
602
+ const total = parseInt(countResult.recordset[0]?.total, 10) || 0;
603
+ const dataQuery = `${selectStatement} FROM ${tableName} ${whereClause} ${orderByStatement} OFFSET @offset ROWS FETCH NEXT @limit ROWS ONLY`;
604
+ request.input("offset", offset);
605
+ if (perPage > 2147483647) {
606
+ request.input("limit", sql2.BigInt, perPage);
607
+ } else {
608
+ request.input("limit", perPage);
609
+ }
610
+ const rowsResult = await request.query(dataQuery);
611
+ const rows = rowsResult.recordset || [];
612
+ const messages = [...rows];
613
+ if (total === 0 && messages.length === 0) {
614
+ return {
615
+ messages: [],
616
+ total: 0,
617
+ page,
618
+ perPage,
619
+ hasMore: false
620
+ };
621
+ }
622
+ const messageIds = new Set(messages.map((m) => m.id));
623
+ if (include && include.length > 0) {
624
+ const selectBy = { include };
625
+ const includeMessages = await this._getIncludedMessages({ threadId, selectBy });
626
+ if (includeMessages) {
627
+ for (const includeMsg of includeMessages) {
628
+ if (!messageIds.has(includeMsg.id)) {
629
+ messages.push(includeMsg);
630
+ messageIds.add(includeMsg.id);
631
+ }
632
+ }
633
+ }
634
+ }
635
+ const parsed = this._parseAndFormatMessages(messages, "v2");
636
+ let finalMessages = parsed;
637
+ finalMessages = finalMessages.sort((a, b) => {
638
+ const aValue = sortField === "createdAt" ? new Date(a.createdAt).getTime() : a[sortField];
639
+ const bValue = sortField === "createdAt" ? new Date(b.createdAt).getTime() : b[sortField];
640
+ return sortDirection === "ASC" ? aValue - bValue : bValue - aValue;
641
+ });
642
+ const returnedThreadMessageIds = new Set(finalMessages.filter((m) => m.threadId === threadId).map((m) => m.id));
643
+ const allThreadMessagesReturned = returnedThreadMessageIds.size >= total;
644
+ const hasMore = limit === false ? false : allThreadMessagesReturned ? false : offset + rows.length < total;
645
+ return {
646
+ messages: finalMessages,
647
+ total,
648
+ page,
649
+ perPage,
650
+ hasMore
651
+ };
652
+ } catch (error) {
653
+ const errorPerPage = limit === false ? Number.MAX_SAFE_INTEGER : limit === 0 ? 0 : limit || 40;
654
+ const mastraError = new MastraError(
655
+ {
656
+ id: "MASTRA_STORAGE_MSSQL_STORE_LIST_MESSAGES_FAILED",
657
+ domain: ErrorDomain.STORAGE,
658
+ category: ErrorCategory.THIRD_PARTY,
659
+ details: {
660
+ threadId,
661
+ resourceId: resourceId ?? ""
662
+ }
663
+ },
664
+ error
665
+ );
666
+ this.logger?.error?.(mastraError.toString());
667
+ this.logger?.trackException?.(mastraError);
668
+ return {
669
+ messages: [],
670
+ total: 0,
671
+ page: errorPerPage === 0 ? 0 : Math.floor(offset / errorPerPage),
672
+ perPage: errorPerPage,
673
+ hasMore: false
674
+ };
675
+ }
676
+ }
677
+ async listThreadsByResourceId(args) {
678
+ const { resourceId, limit, offset, orderBy, sortDirection } = args;
679
+ const page = Math.floor(offset / limit);
680
+ const perPage = limit;
681
+ return this.getThreadsByResourceIdPaginated({ resourceId, page, perPage, orderBy, sortDirection });
682
+ }
630
683
  async getMessagesPaginated(args) {
631
684
  const { threadId, resourceId, format, selectBy } = args;
632
685
  const { page = 0, perPage: perPageInput, dateRange } = selectBy?.pagination || {};
@@ -638,7 +691,7 @@ var MemoryMSSQL = class extends MemoryStorage {
638
691
  const orderByStatement = `ORDER BY [seq_id] DESC`;
639
692
  let messages = [];
640
693
  if (selectBy?.include?.length) {
641
- const includeMessages = await this._getIncludedMessages({ threadId, selectBy, orderByStatement });
694
+ const includeMessages = await this._getIncludedMessages({ threadId, selectBy });
642
695
  if (includeMessages) messages.push(...includeMessages);
643
696
  }
644
697
  const perPage = perPageInput !== void 0 ? perPageInput : resolveMessageLimit({ last: selectBy?.last, defaultLimit: 40 });
@@ -685,7 +738,7 @@ var MemoryMSSQL = class extends MemoryStorage {
685
738
  const parsed = this._parseAndFormatMessages(messages, format);
686
739
  return {
687
740
  messages: parsed,
688
- total: total + excludeIds.length,
741
+ total,
689
742
  page,
690
743
  perPage,
691
744
  hasMore: currentOffset + rows.length < total
@@ -705,7 +758,7 @@ var MemoryMSSQL = class extends MemoryStorage {
705
758
  error
706
759
  );
707
760
  this.logger?.error?.(mastraError.toString());
708
- this.logger?.trackException(mastraError);
761
+ this.logger?.trackException?.(mastraError);
709
762
  return { messages: [], total: 0, page, perPage: perPageInput || 40, hasMore: false };
710
763
  }
711
764
  }
@@ -972,8 +1025,10 @@ var MemoryMSSQL = class extends MemoryStorage {
972
1025
  return null;
973
1026
  }
974
1027
  return {
975
- ...result,
976
- workingMemory: typeof result.workingMemory === "object" ? JSON.stringify(result.workingMemory) : result.workingMemory,
1028
+ id: result.id,
1029
+ createdAt: result.createdAt,
1030
+ updatedAt: result.updatedAt,
1031
+ workingMemory: result.workingMemory,
977
1032
  metadata: typeof result.metadata === "string" ? JSON.parse(result.metadata) : result.metadata
978
1033
  };
979
1034
  } catch (error) {
@@ -987,7 +1042,7 @@ var MemoryMSSQL = class extends MemoryStorage {
987
1042
  error
988
1043
  );
989
1044
  this.logger?.error?.(mastraError.toString());
990
- this.logger?.trackException(mastraError);
1045
+ this.logger?.trackException?.(mastraError);
991
1046
  throw mastraError;
992
1047
  }
993
1048
  }
@@ -996,7 +1051,7 @@ var MemoryMSSQL = class extends MemoryStorage {
996
1051
  tableName: TABLE_RESOURCES,
997
1052
  record: {
998
1053
  ...resource,
999
- metadata: JSON.stringify(resource.metadata)
1054
+ metadata: resource.metadata
1000
1055
  }
1001
1056
  });
1002
1057
  return resource;
@@ -1054,141 +1109,466 @@ var MemoryMSSQL = class extends MemoryStorage {
1054
1109
  error
1055
1110
  );
1056
1111
  this.logger?.error?.(mastraError.toString());
1057
- this.logger?.trackException(mastraError);
1112
+ this.logger?.trackException?.(mastraError);
1058
1113
  throw mastraError;
1059
1114
  }
1060
1115
  }
1061
1116
  };
1062
- var StoreOperationsMSSQL = class extends StoreOperations {
1117
+ var ObservabilityMSSQL = class extends ObservabilityStorage {
1063
1118
  pool;
1064
- schemaName;
1065
- setupSchemaPromise = null;
1066
- schemaSetupComplete = void 0;
1067
- getSqlType(type, isPrimaryKey = false) {
1068
- switch (type) {
1069
- case "text":
1070
- return isPrimaryKey ? "NVARCHAR(255)" : "NVARCHAR(MAX)";
1071
- case "timestamp":
1072
- return "DATETIME2(7)";
1073
- case "uuid":
1074
- return "UNIQUEIDENTIFIER";
1075
- case "jsonb":
1076
- return "NVARCHAR(MAX)";
1077
- case "integer":
1078
- return "INT";
1079
- case "bigint":
1080
- return "BIGINT";
1081
- case "float":
1082
- return "FLOAT";
1083
- default:
1084
- throw new MastraError({
1085
- id: "MASTRA_STORAGE_MSSQL_STORE_TYPE_NOT_SUPPORTED",
1086
- domain: ErrorDomain.STORAGE,
1087
- category: ErrorCategory.THIRD_PARTY
1088
- });
1089
- }
1090
- }
1091
- constructor({ pool, schemaName }) {
1119
+ operations;
1120
+ schema;
1121
+ constructor({
1122
+ pool,
1123
+ operations,
1124
+ schema
1125
+ }) {
1092
1126
  super();
1093
1127
  this.pool = pool;
1094
- this.schemaName = schemaName;
1095
- }
1096
- async hasColumn(table, column) {
1097
- const schema = this.schemaName || "dbo";
1098
- const request = this.pool.request();
1099
- request.input("schema", schema);
1100
- request.input("table", table);
1101
- request.input("column", column);
1102
- request.input("columnLower", column.toLowerCase());
1103
- const result = await request.query(
1104
- `SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = @schema AND TABLE_NAME = @table AND (COLUMN_NAME = @column OR COLUMN_NAME = @columnLower)`
1105
- );
1106
- return result.recordset.length > 0;
1128
+ this.operations = operations;
1129
+ this.schema = schema;
1107
1130
  }
1108
- async setupSchema() {
1109
- if (!this.schemaName || this.schemaSetupComplete) {
1110
- return;
1111
- }
1112
- if (!this.setupSchemaPromise) {
1113
- this.setupSchemaPromise = (async () => {
1114
- try {
1115
- const checkRequest = this.pool.request();
1116
- checkRequest.input("schemaName", this.schemaName);
1117
- const checkResult = await checkRequest.query(`
1118
- SELECT 1 AS found FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = @schemaName
1119
- `);
1120
- const schemaExists = Array.isArray(checkResult.recordset) && checkResult.recordset.length > 0;
1121
- if (!schemaExists) {
1122
- try {
1123
- await this.pool.request().query(`CREATE SCHEMA [${this.schemaName}]`);
1124
- this.logger?.info?.(`Schema "${this.schemaName}" created successfully`);
1125
- } catch (error) {
1126
- this.logger?.error?.(`Failed to create schema "${this.schemaName}"`, { error });
1127
- throw new Error(
1128
- `Unable to create schema "${this.schemaName}". This requires CREATE privilege on the database. Either create the schema manually or grant CREATE privilege to the user.`
1129
- );
1130
- }
1131
- }
1132
- this.schemaSetupComplete = true;
1133
- this.logger?.debug?.(`Schema "${this.schemaName}" is ready for use`);
1134
- } catch (error) {
1135
- this.schemaSetupComplete = void 0;
1136
- this.setupSchemaPromise = null;
1137
- throw error;
1138
- } finally {
1139
- this.setupSchemaPromise = null;
1140
- }
1141
- })();
1142
- }
1143
- await this.setupSchemaPromise;
1131
+ get aiTracingStrategy() {
1132
+ return {
1133
+ preferred: "batch-with-updates",
1134
+ supported: ["batch-with-updates", "insert-only"]
1135
+ };
1144
1136
  }
1145
- async insert({ tableName, record }) {
1137
+ async createAISpan(span) {
1146
1138
  try {
1147
- const columns = Object.keys(record).map((col) => parseSqlIdentifier(col, "column name"));
1148
- const values = Object.values(record);
1149
- const paramNames = values.map((_, i) => `@param${i}`);
1150
- const insertSql = `INSERT INTO ${getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) })} (${columns.map((c) => `[${c}]`).join(", ")}) VALUES (${paramNames.join(", ")})`;
1151
- const request = this.pool.request();
1152
- values.forEach((value, i) => {
1153
- if (value instanceof Date) {
1154
- request.input(`param${i}`, sql2.DateTime2, value);
1155
- } else if (typeof value === "object" && value !== null) {
1156
- request.input(`param${i}`, JSON.stringify(value));
1157
- } else {
1158
- request.input(`param${i}`, value);
1159
- }
1160
- });
1161
- await request.query(insertSql);
1139
+ const startedAt = span.startedAt instanceof Date ? span.startedAt.toISOString() : span.startedAt;
1140
+ const endedAt = span.endedAt instanceof Date ? span.endedAt.toISOString() : span.endedAt;
1141
+ const record = {
1142
+ ...span,
1143
+ startedAt,
1144
+ endedAt
1145
+ // Note: createdAt/updatedAt will be set by default values
1146
+ };
1147
+ return this.operations.insert({ tableName: TABLE_AI_SPANS, record });
1162
1148
  } catch (error) {
1163
1149
  throw new MastraError(
1164
1150
  {
1165
- id: "MASTRA_STORAGE_MSSQL_STORE_INSERT_FAILED",
1151
+ id: "MSSQL_STORE_CREATE_AI_SPAN_FAILED",
1166
1152
  domain: ErrorDomain.STORAGE,
1167
- category: ErrorCategory.THIRD_PARTY,
1153
+ category: ErrorCategory.USER,
1168
1154
  details: {
1169
- tableName
1155
+ spanId: span.spanId,
1156
+ traceId: span.traceId,
1157
+ spanType: span.spanType,
1158
+ spanName: span.name
1170
1159
  }
1171
1160
  },
1172
1161
  error
1173
1162
  );
1174
1163
  }
1175
1164
  }
1176
- async clearTable({ tableName }) {
1177
- const fullTableName = getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) });
1165
+ async getAITrace(traceId) {
1178
1166
  try {
1179
- try {
1180
- await this.pool.request().query(`TRUNCATE TABLE ${fullTableName}`);
1181
- } catch (truncateError) {
1182
- if (truncateError.message && truncateError.message.includes("foreign key")) {
1183
- await this.pool.request().query(`DELETE FROM ${fullTableName}`);
1184
- } else {
1185
- throw truncateError;
1186
- }
1167
+ const tableName = getTableName({
1168
+ indexName: TABLE_AI_SPANS,
1169
+ schemaName: getSchemaName(this.schema)
1170
+ });
1171
+ const request = this.pool.request();
1172
+ request.input("traceId", traceId);
1173
+ const result = await request.query(
1174
+ `SELECT
1175
+ [traceId], [spanId], [parentSpanId], [name], [scope], [spanType],
1176
+ [attributes], [metadata], [links], [input], [output], [error], [isEvent],
1177
+ [startedAt], [endedAt], [createdAt], [updatedAt]
1178
+ FROM ${tableName}
1179
+ WHERE [traceId] = @traceId
1180
+ ORDER BY [startedAt] DESC`
1181
+ );
1182
+ if (!result.recordset || result.recordset.length === 0) {
1183
+ return null;
1187
1184
  }
1185
+ return {
1186
+ traceId,
1187
+ spans: result.recordset.map(
1188
+ (span) => transformFromSqlRow({
1189
+ tableName: TABLE_AI_SPANS,
1190
+ sqlRow: span
1191
+ })
1192
+ )
1193
+ };
1188
1194
  } catch (error) {
1189
1195
  throw new MastraError(
1190
1196
  {
1191
- id: "MASTRA_STORAGE_MSSQL_STORE_CLEAR_TABLE_FAILED",
1197
+ id: "MSSQL_STORE_GET_AI_TRACE_FAILED",
1198
+ domain: ErrorDomain.STORAGE,
1199
+ category: ErrorCategory.USER,
1200
+ details: {
1201
+ traceId
1202
+ }
1203
+ },
1204
+ error
1205
+ );
1206
+ }
1207
+ }
1208
+ async updateAISpan({
1209
+ spanId,
1210
+ traceId,
1211
+ updates
1212
+ }) {
1213
+ try {
1214
+ const data = { ...updates };
1215
+ if (data.endedAt instanceof Date) {
1216
+ data.endedAt = data.endedAt.toISOString();
1217
+ }
1218
+ if (data.startedAt instanceof Date) {
1219
+ data.startedAt = data.startedAt.toISOString();
1220
+ }
1221
+ await this.operations.update({
1222
+ tableName: TABLE_AI_SPANS,
1223
+ keys: { spanId, traceId },
1224
+ data
1225
+ });
1226
+ } catch (error) {
1227
+ throw new MastraError(
1228
+ {
1229
+ id: "MSSQL_STORE_UPDATE_AI_SPAN_FAILED",
1230
+ domain: ErrorDomain.STORAGE,
1231
+ category: ErrorCategory.USER,
1232
+ details: {
1233
+ spanId,
1234
+ traceId
1235
+ }
1236
+ },
1237
+ error
1238
+ );
1239
+ }
1240
+ }
1241
+ async getAITracesPaginated({
1242
+ filters,
1243
+ pagination
1244
+ }) {
1245
+ const page = pagination?.page ?? 0;
1246
+ const perPage = pagination?.perPage ?? 10;
1247
+ const { entityId, entityType, ...actualFilters } = filters || {};
1248
+ const filtersWithDateRange = {
1249
+ ...actualFilters,
1250
+ ...buildDateRangeFilter(pagination?.dateRange, "startedAt"),
1251
+ parentSpanId: null
1252
+ // Only get root spans for traces
1253
+ };
1254
+ const whereClause = prepareWhereClause(filtersWithDateRange);
1255
+ let actualWhereClause = whereClause.sql;
1256
+ const params = { ...whereClause.params };
1257
+ let currentParamIndex = Object.keys(params).length + 1;
1258
+ if (entityId && entityType) {
1259
+ let name = "";
1260
+ if (entityType === "workflow") {
1261
+ name = `workflow run: '${entityId}'`;
1262
+ } else if (entityType === "agent") {
1263
+ name = `agent run: '${entityId}'`;
1264
+ } else {
1265
+ const error = new MastraError({
1266
+ id: "MSSQL_STORE_GET_AI_TRACES_PAGINATED_FAILED",
1267
+ domain: ErrorDomain.STORAGE,
1268
+ category: ErrorCategory.USER,
1269
+ details: {
1270
+ entityType
1271
+ },
1272
+ text: `Cannot filter by entity type: ${entityType}`
1273
+ });
1274
+ throw error;
1275
+ }
1276
+ const entityParam = `p${currentParamIndex++}`;
1277
+ if (actualWhereClause) {
1278
+ actualWhereClause += ` AND [name] = @${entityParam}`;
1279
+ } else {
1280
+ actualWhereClause = ` WHERE [name] = @${entityParam}`;
1281
+ }
1282
+ params[entityParam] = name;
1283
+ }
1284
+ const tableName = getTableName({
1285
+ indexName: TABLE_AI_SPANS,
1286
+ schemaName: getSchemaName(this.schema)
1287
+ });
1288
+ try {
1289
+ const countRequest = this.pool.request();
1290
+ Object.entries(params).forEach(([key, value]) => {
1291
+ countRequest.input(key, value);
1292
+ });
1293
+ const countResult = await countRequest.query(
1294
+ `SELECT COUNT(*) as count FROM ${tableName}${actualWhereClause}`
1295
+ );
1296
+ const total = countResult.recordset[0]?.count ?? 0;
1297
+ if (total === 0) {
1298
+ return {
1299
+ pagination: {
1300
+ total: 0,
1301
+ page,
1302
+ perPage,
1303
+ hasMore: false
1304
+ },
1305
+ spans: []
1306
+ };
1307
+ }
1308
+ const dataRequest = this.pool.request();
1309
+ Object.entries(params).forEach(([key, value]) => {
1310
+ dataRequest.input(key, value);
1311
+ });
1312
+ dataRequest.input("offset", page * perPage);
1313
+ dataRequest.input("limit", perPage);
1314
+ const dataResult = await dataRequest.query(
1315
+ `SELECT * FROM ${tableName}${actualWhereClause} ORDER BY [startedAt] DESC OFFSET @offset ROWS FETCH NEXT @limit ROWS ONLY`
1316
+ );
1317
+ const spans = dataResult.recordset.map(
1318
+ (row) => transformFromSqlRow({
1319
+ tableName: TABLE_AI_SPANS,
1320
+ sqlRow: row
1321
+ })
1322
+ );
1323
+ return {
1324
+ pagination: {
1325
+ total,
1326
+ page,
1327
+ perPage,
1328
+ hasMore: (page + 1) * perPage < total
1329
+ },
1330
+ spans
1331
+ };
1332
+ } catch (error) {
1333
+ throw new MastraError(
1334
+ {
1335
+ id: "MSSQL_STORE_GET_AI_TRACES_PAGINATED_FAILED",
1336
+ domain: ErrorDomain.STORAGE,
1337
+ category: ErrorCategory.USER
1338
+ },
1339
+ error
1340
+ );
1341
+ }
1342
+ }
1343
+ async batchCreateAISpans(args) {
1344
+ if (!args.records || args.records.length === 0) {
1345
+ return;
1346
+ }
1347
+ try {
1348
+ await this.operations.batchInsert({
1349
+ tableName: TABLE_AI_SPANS,
1350
+ records: args.records.map((span) => ({
1351
+ ...span,
1352
+ startedAt: span.startedAt instanceof Date ? span.startedAt.toISOString() : span.startedAt,
1353
+ endedAt: span.endedAt instanceof Date ? span.endedAt.toISOString() : span.endedAt
1354
+ }))
1355
+ });
1356
+ } catch (error) {
1357
+ throw new MastraError(
1358
+ {
1359
+ id: "MSSQL_STORE_BATCH_CREATE_AI_SPANS_FAILED",
1360
+ domain: ErrorDomain.STORAGE,
1361
+ category: ErrorCategory.USER,
1362
+ details: {
1363
+ count: args.records.length
1364
+ }
1365
+ },
1366
+ error
1367
+ );
1368
+ }
1369
+ }
1370
+ async batchUpdateAISpans(args) {
1371
+ if (!args.records || args.records.length === 0) {
1372
+ return;
1373
+ }
1374
+ try {
1375
+ const updates = args.records.map(({ traceId, spanId, updates: data }) => {
1376
+ const processedData = { ...data };
1377
+ if (processedData.endedAt instanceof Date) {
1378
+ processedData.endedAt = processedData.endedAt.toISOString();
1379
+ }
1380
+ if (processedData.startedAt instanceof Date) {
1381
+ processedData.startedAt = processedData.startedAt.toISOString();
1382
+ }
1383
+ return {
1384
+ keys: { spanId, traceId },
1385
+ data: processedData
1386
+ };
1387
+ });
1388
+ await this.operations.batchUpdate({
1389
+ tableName: TABLE_AI_SPANS,
1390
+ updates
1391
+ });
1392
+ } catch (error) {
1393
+ throw new MastraError(
1394
+ {
1395
+ id: "MSSQL_STORE_BATCH_UPDATE_AI_SPANS_FAILED",
1396
+ domain: ErrorDomain.STORAGE,
1397
+ category: ErrorCategory.USER,
1398
+ details: {
1399
+ count: args.records.length
1400
+ }
1401
+ },
1402
+ error
1403
+ );
1404
+ }
1405
+ }
1406
+ async batchDeleteAITraces(args) {
1407
+ if (!args.traceIds || args.traceIds.length === 0) {
1408
+ return;
1409
+ }
1410
+ try {
1411
+ const keys = args.traceIds.map((traceId) => ({ traceId }));
1412
+ await this.operations.batchDelete({
1413
+ tableName: TABLE_AI_SPANS,
1414
+ keys
1415
+ });
1416
+ } catch (error) {
1417
+ throw new MastraError(
1418
+ {
1419
+ id: "MSSQL_STORE_BATCH_DELETE_AI_TRACES_FAILED",
1420
+ domain: ErrorDomain.STORAGE,
1421
+ category: ErrorCategory.USER,
1422
+ details: {
1423
+ count: args.traceIds.length
1424
+ }
1425
+ },
1426
+ error
1427
+ );
1428
+ }
1429
+ }
1430
+ };
1431
+ var StoreOperationsMSSQL = class extends StoreOperations {
1432
+ pool;
1433
+ schemaName;
1434
+ setupSchemaPromise = null;
1435
+ schemaSetupComplete = void 0;
1436
+ getSqlType(type, isPrimaryKey = false, useLargeStorage = false) {
1437
+ switch (type) {
1438
+ case "text":
1439
+ if (useLargeStorage) {
1440
+ return "NVARCHAR(MAX)";
1441
+ }
1442
+ return isPrimaryKey ? "NVARCHAR(255)" : "NVARCHAR(400)";
1443
+ case "timestamp":
1444
+ return "DATETIME2(7)";
1445
+ case "uuid":
1446
+ return "UNIQUEIDENTIFIER";
1447
+ case "jsonb":
1448
+ return "NVARCHAR(MAX)";
1449
+ case "integer":
1450
+ return "INT";
1451
+ case "bigint":
1452
+ return "BIGINT";
1453
+ case "float":
1454
+ return "FLOAT";
1455
+ case "boolean":
1456
+ return "BIT";
1457
+ default:
1458
+ throw new MastraError({
1459
+ id: "MASTRA_STORAGE_MSSQL_STORE_TYPE_NOT_SUPPORTED",
1460
+ domain: ErrorDomain.STORAGE,
1461
+ category: ErrorCategory.THIRD_PARTY
1462
+ });
1463
+ }
1464
+ }
1465
+ constructor({ pool, schemaName }) {
1466
+ super();
1467
+ this.pool = pool;
1468
+ this.schemaName = schemaName;
1469
+ }
1470
+ async hasColumn(table, column) {
1471
+ const schema = this.schemaName || "dbo";
1472
+ const request = this.pool.request();
1473
+ request.input("schema", schema);
1474
+ request.input("table", table);
1475
+ request.input("column", column);
1476
+ request.input("columnLower", column.toLowerCase());
1477
+ const result = await request.query(
1478
+ `SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = @schema AND TABLE_NAME = @table AND (COLUMN_NAME = @column OR COLUMN_NAME = @columnLower)`
1479
+ );
1480
+ return result.recordset.length > 0;
1481
+ }
1482
+ async setupSchema() {
1483
+ if (!this.schemaName || this.schemaSetupComplete) {
1484
+ return;
1485
+ }
1486
+ if (!this.setupSchemaPromise) {
1487
+ this.setupSchemaPromise = (async () => {
1488
+ try {
1489
+ const checkRequest = this.pool.request();
1490
+ checkRequest.input("schemaName", this.schemaName);
1491
+ const checkResult = await checkRequest.query(`
1492
+ SELECT 1 AS found FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = @schemaName
1493
+ `);
1494
+ const schemaExists = Array.isArray(checkResult.recordset) && checkResult.recordset.length > 0;
1495
+ if (!schemaExists) {
1496
+ try {
1497
+ await this.pool.request().query(`CREATE SCHEMA [${this.schemaName}]`);
1498
+ this.logger?.info?.(`Schema "${this.schemaName}" created successfully`);
1499
+ } catch (error) {
1500
+ this.logger?.error?.(`Failed to create schema "${this.schemaName}"`, { error });
1501
+ throw new Error(
1502
+ `Unable to create schema "${this.schemaName}". This requires CREATE privilege on the database. Either create the schema manually or grant CREATE privilege to the user.`
1503
+ );
1504
+ }
1505
+ }
1506
+ this.schemaSetupComplete = true;
1507
+ this.logger?.debug?.(`Schema "${this.schemaName}" is ready for use`);
1508
+ } catch (error) {
1509
+ this.schemaSetupComplete = void 0;
1510
+ this.setupSchemaPromise = null;
1511
+ throw error;
1512
+ } finally {
1513
+ this.setupSchemaPromise = null;
1514
+ }
1515
+ })();
1516
+ }
1517
+ await this.setupSchemaPromise;
1518
+ }
1519
+ async insert({
1520
+ tableName,
1521
+ record,
1522
+ transaction
1523
+ }) {
1524
+ try {
1525
+ const columns = Object.keys(record);
1526
+ const parsedColumns = columns.map((col) => parseSqlIdentifier(col, "column name"));
1527
+ const paramNames = columns.map((_, i) => `@param${i}`);
1528
+ const insertSql = `INSERT INTO ${getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) })} (${parsedColumns.map((c) => `[${c}]`).join(", ")}) VALUES (${paramNames.join(", ")})`;
1529
+ const request = transaction ? transaction.request() : this.pool.request();
1530
+ columns.forEach((col, i) => {
1531
+ const value = record[col];
1532
+ const preparedValue = this.prepareValue(value, col, tableName);
1533
+ if (preparedValue instanceof Date) {
1534
+ request.input(`param${i}`, sql2.DateTime2, preparedValue);
1535
+ } else if (preparedValue === null || preparedValue === void 0) {
1536
+ request.input(`param${i}`, this.getMssqlType(tableName, col), null);
1537
+ } else {
1538
+ request.input(`param${i}`, preparedValue);
1539
+ }
1540
+ });
1541
+ await request.query(insertSql);
1542
+ } catch (error) {
1543
+ throw new MastraError(
1544
+ {
1545
+ id: "MASTRA_STORAGE_MSSQL_STORE_INSERT_FAILED",
1546
+ domain: ErrorDomain.STORAGE,
1547
+ category: ErrorCategory.THIRD_PARTY,
1548
+ details: {
1549
+ tableName
1550
+ }
1551
+ },
1552
+ error
1553
+ );
1554
+ }
1555
+ }
1556
+ async clearTable({ tableName }) {
1557
+ const fullTableName = getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) });
1558
+ try {
1559
+ try {
1560
+ await this.pool.request().query(`TRUNCATE TABLE ${fullTableName}`);
1561
+ } catch (truncateError) {
1562
+ if (truncateError?.number === 4712) {
1563
+ await this.pool.request().query(`DELETE FROM ${fullTableName}`);
1564
+ } else {
1565
+ throw truncateError;
1566
+ }
1567
+ }
1568
+ } catch (error) {
1569
+ throw new MastraError(
1570
+ {
1571
+ id: "MASTRA_STORAGE_MSSQL_STORE_CLEAR_TABLE_FAILED",
1192
1572
  domain: ErrorDomain.STORAGE,
1193
1573
  category: ErrorCategory.THIRD_PARTY,
1194
1574
  details: {
@@ -1202,9 +1582,11 @@ var StoreOperationsMSSQL = class extends StoreOperations {
1202
1582
  getDefaultValue(type) {
1203
1583
  switch (type) {
1204
1584
  case "timestamp":
1205
- return "DEFAULT SYSDATETIMEOFFSET()";
1585
+ return "DEFAULT SYSUTCDATETIME()";
1206
1586
  case "jsonb":
1207
1587
  return "DEFAULT N'{}'";
1588
+ case "boolean":
1589
+ return "DEFAULT 0";
1208
1590
  default:
1209
1591
  return super.getDefaultValue(type);
1210
1592
  }
@@ -1215,13 +1597,29 @@ var StoreOperationsMSSQL = class extends StoreOperations {
1215
1597
  }) {
1216
1598
  try {
1217
1599
  const uniqueConstraintColumns = tableName === TABLE_WORKFLOW_SNAPSHOT ? ["workflow_name", "run_id"] : [];
1600
+ const largeDataColumns = [
1601
+ "workingMemory",
1602
+ "snapshot",
1603
+ "metadata",
1604
+ "content",
1605
+ // messages.content - can be very long conversation content
1606
+ "input",
1607
+ // evals.input - test input data
1608
+ "output",
1609
+ // evals.output - test output data
1610
+ "instructions",
1611
+ // evals.instructions - evaluation instructions
1612
+ "other"
1613
+ // traces.other - additional trace data
1614
+ ];
1218
1615
  const columns = Object.entries(schema).map(([name, def]) => {
1219
1616
  const parsedName = parseSqlIdentifier(name, "column name");
1220
1617
  const constraints = [];
1221
1618
  if (def.primaryKey) constraints.push("PRIMARY KEY");
1222
1619
  if (!def.nullable) constraints.push("NOT NULL");
1223
1620
  const isIndexed = !!def.primaryKey || uniqueConstraintColumns.includes(name);
1224
- return `[${parsedName}] ${this.getSqlType(def.type, isIndexed)} ${constraints.join(" ")}`.trim();
1621
+ const useLargeStorage = largeDataColumns.includes(name);
1622
+ return `[${parsedName}] ${this.getSqlType(def.type, isIndexed, useLargeStorage)} ${constraints.join(" ")}`.trim();
1225
1623
  }).join(",\n");
1226
1624
  if (this.schemaName) {
1227
1625
  await this.setupSchema();
@@ -1308,7 +1706,19 @@ ${columns}
1308
1706
  const columnExists = Array.isArray(checkResult.recordset) && checkResult.recordset.length > 0;
1309
1707
  if (!columnExists) {
1310
1708
  const columnDef = schema[columnName];
1311
- const sqlType = this.getSqlType(columnDef.type);
1709
+ const largeDataColumns = [
1710
+ "workingMemory",
1711
+ "snapshot",
1712
+ "metadata",
1713
+ "content",
1714
+ "input",
1715
+ "output",
1716
+ "instructions",
1717
+ "other"
1718
+ ];
1719
+ const useLargeStorage = largeDataColumns.includes(columnName);
1720
+ const isIndexed = !!columnDef.primaryKey;
1721
+ const sqlType = this.getSqlType(columnDef.type, isIndexed, useLargeStorage);
1312
1722
  const nullable = columnDef.nullable === false ? "NOT NULL" : "";
1313
1723
  const defaultValue = columnDef.nullable === false ? this.getDefaultValue(columnDef.type) : "";
1314
1724
  const parsedColumnName = parseSqlIdentifier(columnName, "column name");
@@ -1336,13 +1746,17 @@ ${columns}
1336
1746
  try {
1337
1747
  const keyEntries = Object.entries(keys).map(([key, value]) => [parseSqlIdentifier(key, "column name"), value]);
1338
1748
  const conditions = keyEntries.map(([key], i) => `[${key}] = @param${i}`).join(" AND ");
1339
- const values = keyEntries.map(([_, value]) => value);
1340
- const sql7 = `SELECT * FROM ${getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) })} WHERE ${conditions}`;
1749
+ const sql5 = `SELECT * FROM ${getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) })} WHERE ${conditions}`;
1341
1750
  const request = this.pool.request();
1342
- values.forEach((value, i) => {
1343
- request.input(`param${i}`, value);
1751
+ keyEntries.forEach(([key, value], i) => {
1752
+ const preparedValue = this.prepareValue(value, key, tableName);
1753
+ if (preparedValue === null || preparedValue === void 0) {
1754
+ request.input(`param${i}`, this.getMssqlType(tableName, key), null);
1755
+ } else {
1756
+ request.input(`param${i}`, preparedValue);
1757
+ }
1344
1758
  });
1345
- const resultSet = await request.query(sql7);
1759
+ const resultSet = await request.query(sql5);
1346
1760
  const result = resultSet.recordset[0] || null;
1347
1761
  if (!result) {
1348
1762
  return null;
@@ -1374,63 +1788,599 @@ ${columns}
1374
1788
  try {
1375
1789
  await transaction.begin();
1376
1790
  for (const record of records) {
1377
- await this.insert({ tableName, record });
1791
+ await this.insert({ tableName, record, transaction });
1792
+ }
1793
+ await transaction.commit();
1794
+ } catch (error) {
1795
+ await transaction.rollback();
1796
+ throw new MastraError(
1797
+ {
1798
+ id: "MASTRA_STORAGE_MSSQL_STORE_BATCH_INSERT_FAILED",
1799
+ domain: ErrorDomain.STORAGE,
1800
+ category: ErrorCategory.THIRD_PARTY,
1801
+ details: {
1802
+ tableName,
1803
+ numberOfRecords: records.length
1804
+ }
1805
+ },
1806
+ error
1807
+ );
1808
+ }
1809
+ }
1810
+ async dropTable({ tableName }) {
1811
+ try {
1812
+ const tableNameWithSchema = getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) });
1813
+ await this.pool.request().query(`DROP TABLE IF EXISTS ${tableNameWithSchema}`);
1814
+ } catch (error) {
1815
+ throw new MastraError(
1816
+ {
1817
+ id: "MASTRA_STORAGE_MSSQL_STORE_DROP_TABLE_FAILED",
1818
+ domain: ErrorDomain.STORAGE,
1819
+ category: ErrorCategory.THIRD_PARTY,
1820
+ details: {
1821
+ tableName
1822
+ }
1823
+ },
1824
+ error
1825
+ );
1826
+ }
1827
+ }
1828
+ /**
1829
+ * Prepares a value for database operations, handling Date objects and JSON serialization
1830
+ */
1831
+ prepareValue(value, columnName, tableName) {
1832
+ if (value === null || value === void 0) {
1833
+ return value;
1834
+ }
1835
+ if (value instanceof Date) {
1836
+ return value;
1837
+ }
1838
+ const schema = TABLE_SCHEMAS[tableName];
1839
+ const columnSchema = schema?.[columnName];
1840
+ if (columnSchema?.type === "boolean") {
1841
+ return value ? 1 : 0;
1842
+ }
1843
+ if (columnSchema?.type === "jsonb") {
1844
+ return JSON.stringify(value);
1845
+ }
1846
+ if (typeof value === "object") {
1847
+ return JSON.stringify(value);
1848
+ }
1849
+ return value;
1850
+ }
1851
+ /**
1852
+ * Maps TABLE_SCHEMAS types to mssql param types (used when value is null)
1853
+ */
1854
+ getMssqlType(tableName, columnName) {
1855
+ const col = TABLE_SCHEMAS[tableName]?.[columnName];
1856
+ switch (col?.type) {
1857
+ case "text":
1858
+ return sql2.NVarChar;
1859
+ case "timestamp":
1860
+ return sql2.DateTime2;
1861
+ case "uuid":
1862
+ return sql2.UniqueIdentifier;
1863
+ case "jsonb":
1864
+ return sql2.NVarChar;
1865
+ case "integer":
1866
+ return sql2.Int;
1867
+ case "bigint":
1868
+ return sql2.BigInt;
1869
+ case "float":
1870
+ return sql2.Float;
1871
+ case "boolean":
1872
+ return sql2.Bit;
1873
+ default:
1874
+ return sql2.NVarChar;
1875
+ }
1876
+ }
1877
+ /**
1878
+ * Update a single record in the database
1879
+ */
1880
+ async update({
1881
+ tableName,
1882
+ keys,
1883
+ data,
1884
+ transaction
1885
+ }) {
1886
+ try {
1887
+ if (!data || Object.keys(data).length === 0) {
1888
+ throw new MastraError({
1889
+ id: "MASTRA_STORAGE_MSSQL_UPDATE_EMPTY_DATA",
1890
+ domain: ErrorDomain.STORAGE,
1891
+ category: ErrorCategory.USER,
1892
+ text: "Cannot update with empty data payload"
1893
+ });
1894
+ }
1895
+ if (!keys || Object.keys(keys).length === 0) {
1896
+ throw new MastraError({
1897
+ id: "MASTRA_STORAGE_MSSQL_UPDATE_EMPTY_KEYS",
1898
+ domain: ErrorDomain.STORAGE,
1899
+ category: ErrorCategory.USER,
1900
+ text: "Cannot update without keys to identify records"
1901
+ });
1902
+ }
1903
+ const setClauses = [];
1904
+ const request = transaction ? transaction.request() : this.pool.request();
1905
+ let paramIndex = 0;
1906
+ Object.entries(data).forEach(([key, value]) => {
1907
+ const parsedKey = parseSqlIdentifier(key, "column name");
1908
+ const paramName = `set${paramIndex++}`;
1909
+ setClauses.push(`[${parsedKey}] = @${paramName}`);
1910
+ const preparedValue = this.prepareValue(value, key, tableName);
1911
+ if (preparedValue === null || preparedValue === void 0) {
1912
+ request.input(paramName, this.getMssqlType(tableName, key), null);
1913
+ } else {
1914
+ request.input(paramName, preparedValue);
1915
+ }
1916
+ });
1917
+ const whereConditions = [];
1918
+ Object.entries(keys).forEach(([key, value]) => {
1919
+ const parsedKey = parseSqlIdentifier(key, "column name");
1920
+ const paramName = `where${paramIndex++}`;
1921
+ whereConditions.push(`[${parsedKey}] = @${paramName}`);
1922
+ const preparedValue = this.prepareValue(value, key, tableName);
1923
+ if (preparedValue === null || preparedValue === void 0) {
1924
+ request.input(paramName, this.getMssqlType(tableName, key), null);
1925
+ } else {
1926
+ request.input(paramName, preparedValue);
1927
+ }
1928
+ });
1929
+ const tableName_ = getTableName({
1930
+ indexName: tableName,
1931
+ schemaName: getSchemaName(this.schemaName)
1932
+ });
1933
+ const updateSql = `UPDATE ${tableName_} SET ${setClauses.join(", ")} WHERE ${whereConditions.join(" AND ")}`;
1934
+ await request.query(updateSql);
1935
+ } catch (error) {
1936
+ throw new MastraError(
1937
+ {
1938
+ id: "MASTRA_STORAGE_MSSQL_STORE_UPDATE_FAILED",
1939
+ domain: ErrorDomain.STORAGE,
1940
+ category: ErrorCategory.THIRD_PARTY,
1941
+ details: {
1942
+ tableName
1943
+ }
1944
+ },
1945
+ error
1946
+ );
1947
+ }
1948
+ }
1949
+ /**
1950
+ * Update multiple records in a single batch transaction
1951
+ */
1952
+ async batchUpdate({
1953
+ tableName,
1954
+ updates
1955
+ }) {
1956
+ const transaction = this.pool.transaction();
1957
+ try {
1958
+ await transaction.begin();
1959
+ for (const { keys, data } of updates) {
1960
+ await this.update({ tableName, keys, data, transaction });
1961
+ }
1962
+ await transaction.commit();
1963
+ } catch (error) {
1964
+ await transaction.rollback();
1965
+ throw new MastraError(
1966
+ {
1967
+ id: "MASTRA_STORAGE_MSSQL_STORE_BATCH_UPDATE_FAILED",
1968
+ domain: ErrorDomain.STORAGE,
1969
+ category: ErrorCategory.THIRD_PARTY,
1970
+ details: {
1971
+ tableName,
1972
+ numberOfRecords: updates.length
1973
+ }
1974
+ },
1975
+ error
1976
+ );
1977
+ }
1978
+ }
1979
+ /**
1980
+ * Delete multiple records by keys
1981
+ */
1982
+ async batchDelete({ tableName, keys }) {
1983
+ if (keys.length === 0) {
1984
+ return;
1985
+ }
1986
+ const tableName_ = getTableName({
1987
+ indexName: tableName,
1988
+ schemaName: getSchemaName(this.schemaName)
1989
+ });
1990
+ const transaction = this.pool.transaction();
1991
+ try {
1992
+ await transaction.begin();
1993
+ for (const keySet of keys) {
1994
+ const conditions = [];
1995
+ const request = transaction.request();
1996
+ let paramIndex = 0;
1997
+ Object.entries(keySet).forEach(([key, value]) => {
1998
+ const parsedKey = parseSqlIdentifier(key, "column name");
1999
+ const paramName = `p${paramIndex++}`;
2000
+ conditions.push(`[${parsedKey}] = @${paramName}`);
2001
+ const preparedValue = this.prepareValue(value, key, tableName);
2002
+ if (preparedValue === null || preparedValue === void 0) {
2003
+ request.input(paramName, this.getMssqlType(tableName, key), null);
2004
+ } else {
2005
+ request.input(paramName, preparedValue);
2006
+ }
2007
+ });
2008
+ const deleteSql = `DELETE FROM ${tableName_} WHERE ${conditions.join(" AND ")}`;
2009
+ await request.query(deleteSql);
2010
+ }
2011
+ await transaction.commit();
2012
+ } catch (error) {
2013
+ await transaction.rollback();
2014
+ throw new MastraError(
2015
+ {
2016
+ id: "MASTRA_STORAGE_MSSQL_STORE_BATCH_DELETE_FAILED",
2017
+ domain: ErrorDomain.STORAGE,
2018
+ category: ErrorCategory.THIRD_PARTY,
2019
+ details: {
2020
+ tableName,
2021
+ numberOfRecords: keys.length
2022
+ }
2023
+ },
2024
+ error
2025
+ );
2026
+ }
2027
+ }
2028
+ /**
2029
+ * Create a new index on a table
2030
+ */
2031
+ async createIndex(options) {
2032
+ try {
2033
+ const { name, table, columns, unique = false, where } = options;
2034
+ const schemaName = this.schemaName || "dbo";
2035
+ const fullTableName = getTableName({
2036
+ indexName: table,
2037
+ schemaName: getSchemaName(this.schemaName)
2038
+ });
2039
+ const indexNameSafe = parseSqlIdentifier(name, "index name");
2040
+ const checkRequest = this.pool.request();
2041
+ checkRequest.input("indexName", indexNameSafe);
2042
+ checkRequest.input("schemaName", schemaName);
2043
+ checkRequest.input("tableName", table);
2044
+ const indexExists = await checkRequest.query(`
2045
+ SELECT 1 as found
2046
+ FROM sys.indexes i
2047
+ INNER JOIN sys.tables t ON i.object_id = t.object_id
2048
+ INNER JOIN sys.schemas s ON t.schema_id = s.schema_id
2049
+ WHERE i.name = @indexName
2050
+ AND s.name = @schemaName
2051
+ AND t.name = @tableName
2052
+ `);
2053
+ if (indexExists.recordset && indexExists.recordset.length > 0) {
2054
+ return;
2055
+ }
2056
+ const uniqueStr = unique ? "UNIQUE " : "";
2057
+ const columnsStr = columns.map((col) => {
2058
+ if (col.includes(" DESC") || col.includes(" ASC")) {
2059
+ const [colName, ...modifiers] = col.split(" ");
2060
+ if (!colName) {
2061
+ throw new Error(`Invalid column specification: ${col}`);
2062
+ }
2063
+ return `[${parseSqlIdentifier(colName, "column name")}] ${modifiers.join(" ")}`;
2064
+ }
2065
+ return `[${parseSqlIdentifier(col, "column name")}]`;
2066
+ }).join(", ");
2067
+ const whereStr = where ? ` WHERE ${where}` : "";
2068
+ const createIndexSql = `CREATE ${uniqueStr}INDEX [${indexNameSafe}] ON ${fullTableName} (${columnsStr})${whereStr}`;
2069
+ await this.pool.request().query(createIndexSql);
2070
+ } catch (error) {
2071
+ throw new MastraError(
2072
+ {
2073
+ id: "MASTRA_STORAGE_MSSQL_INDEX_CREATE_FAILED",
2074
+ domain: ErrorDomain.STORAGE,
2075
+ category: ErrorCategory.THIRD_PARTY,
2076
+ details: {
2077
+ indexName: options.name,
2078
+ tableName: options.table
2079
+ }
2080
+ },
2081
+ error
2082
+ );
2083
+ }
2084
+ }
2085
+ /**
2086
+ * Drop an existing index
2087
+ */
2088
+ async dropIndex(indexName) {
2089
+ try {
2090
+ const schemaName = this.schemaName || "dbo";
2091
+ const indexNameSafe = parseSqlIdentifier(indexName, "index name");
2092
+ const checkRequest = this.pool.request();
2093
+ checkRequest.input("indexName", indexNameSafe);
2094
+ checkRequest.input("schemaName", schemaName);
2095
+ const result = await checkRequest.query(`
2096
+ SELECT t.name as table_name
2097
+ FROM sys.indexes i
2098
+ INNER JOIN sys.tables t ON i.object_id = t.object_id
2099
+ INNER JOIN sys.schemas s ON t.schema_id = s.schema_id
2100
+ WHERE i.name = @indexName
2101
+ AND s.name = @schemaName
2102
+ `);
2103
+ if (!result.recordset || result.recordset.length === 0) {
2104
+ return;
2105
+ }
2106
+ if (result.recordset.length > 1) {
2107
+ const tables = result.recordset.map((r) => r.table_name).join(", ");
2108
+ throw new MastraError({
2109
+ id: "MASTRA_STORAGE_MSSQL_INDEX_AMBIGUOUS",
2110
+ domain: ErrorDomain.STORAGE,
2111
+ category: ErrorCategory.USER,
2112
+ text: `Index "${indexNameSafe}" exists on multiple tables (${tables}) in schema "${schemaName}". Please drop indexes manually or ensure unique index names.`
2113
+ });
2114
+ }
2115
+ const tableName = result.recordset[0].table_name;
2116
+ const fullTableName = getTableName({
2117
+ indexName: tableName,
2118
+ schemaName: getSchemaName(this.schemaName)
2119
+ });
2120
+ const dropSql = `DROP INDEX [${indexNameSafe}] ON ${fullTableName}`;
2121
+ await this.pool.request().query(dropSql);
2122
+ } catch (error) {
2123
+ throw new MastraError(
2124
+ {
2125
+ id: "MASTRA_STORAGE_MSSQL_INDEX_DROP_FAILED",
2126
+ domain: ErrorDomain.STORAGE,
2127
+ category: ErrorCategory.THIRD_PARTY,
2128
+ details: {
2129
+ indexName
2130
+ }
2131
+ },
2132
+ error
2133
+ );
2134
+ }
2135
+ }
2136
+ /**
2137
+ * List indexes for a specific table or all tables
2138
+ */
2139
+ async listIndexes(tableName) {
2140
+ try {
2141
+ const schemaName = this.schemaName || "dbo";
2142
+ let query;
2143
+ const request = this.pool.request();
2144
+ request.input("schemaName", schemaName);
2145
+ if (tableName) {
2146
+ query = `
2147
+ SELECT
2148
+ i.name as name,
2149
+ o.name as [table],
2150
+ i.is_unique as is_unique,
2151
+ CAST(SUM(s.used_page_count) * 8 / 1024.0 AS VARCHAR(50)) + ' MB' as size
2152
+ FROM sys.indexes i
2153
+ INNER JOIN sys.objects o ON i.object_id = o.object_id
2154
+ INNER JOIN sys.schemas sch ON o.schema_id = sch.schema_id
2155
+ LEFT JOIN sys.dm_db_partition_stats s ON i.object_id = s.object_id AND i.index_id = s.index_id
2156
+ WHERE sch.name = @schemaName
2157
+ AND o.name = @tableName
2158
+ AND i.name IS NOT NULL
2159
+ GROUP BY i.name, o.name, i.is_unique
2160
+ `;
2161
+ request.input("tableName", tableName);
2162
+ } else {
2163
+ query = `
2164
+ SELECT
2165
+ i.name as name,
2166
+ o.name as [table],
2167
+ i.is_unique as is_unique,
2168
+ CAST(SUM(s.used_page_count) * 8 / 1024.0 AS VARCHAR(50)) + ' MB' as size
2169
+ FROM sys.indexes i
2170
+ INNER JOIN sys.objects o ON i.object_id = o.object_id
2171
+ INNER JOIN sys.schemas sch ON o.schema_id = sch.schema_id
2172
+ LEFT JOIN sys.dm_db_partition_stats s ON i.object_id = s.object_id AND i.index_id = s.index_id
2173
+ WHERE sch.name = @schemaName
2174
+ AND i.name IS NOT NULL
2175
+ GROUP BY i.name, o.name, i.is_unique
2176
+ `;
2177
+ }
2178
+ const result = await request.query(query);
2179
+ const indexes = [];
2180
+ for (const row of result.recordset) {
2181
+ const colRequest = this.pool.request();
2182
+ colRequest.input("indexName", row.name);
2183
+ colRequest.input("schemaName", schemaName);
2184
+ const colResult = await colRequest.query(`
2185
+ SELECT c.name as column_name
2186
+ FROM sys.indexes i
2187
+ INNER JOIN sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id
2188
+ INNER JOIN sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id
2189
+ INNER JOIN sys.objects o ON i.object_id = o.object_id
2190
+ INNER JOIN sys.schemas s ON o.schema_id = s.schema_id
2191
+ WHERE i.name = @indexName
2192
+ AND s.name = @schemaName
2193
+ ORDER BY ic.key_ordinal
2194
+ `);
2195
+ indexes.push({
2196
+ name: row.name,
2197
+ table: row.table,
2198
+ columns: colResult.recordset.map((c) => c.column_name),
2199
+ unique: row.is_unique || false,
2200
+ size: row.size || "0 MB",
2201
+ definition: ""
2202
+ // MSSQL doesn't store definition like PG
2203
+ });
2204
+ }
2205
+ return indexes;
2206
+ } catch (error) {
2207
+ throw new MastraError(
2208
+ {
2209
+ id: "MASTRA_STORAGE_MSSQL_INDEX_LIST_FAILED",
2210
+ domain: ErrorDomain.STORAGE,
2211
+ category: ErrorCategory.THIRD_PARTY,
2212
+ details: tableName ? {
2213
+ tableName
2214
+ } : {}
2215
+ },
2216
+ error
2217
+ );
2218
+ }
2219
+ }
2220
+ /**
2221
+ * Get detailed statistics for a specific index
2222
+ */
2223
+ async describeIndex(indexName) {
2224
+ try {
2225
+ const schemaName = this.schemaName || "dbo";
2226
+ const request = this.pool.request();
2227
+ request.input("indexName", indexName);
2228
+ request.input("schemaName", schemaName);
2229
+ const query = `
2230
+ SELECT
2231
+ i.name as name,
2232
+ o.name as [table],
2233
+ i.is_unique as is_unique,
2234
+ CAST(SUM(s.used_page_count) * 8 / 1024.0 AS VARCHAR(50)) + ' MB' as size,
2235
+ i.type_desc as method,
2236
+ ISNULL(us.user_scans, 0) as scans,
2237
+ ISNULL(us.user_seeks + us.user_scans, 0) as tuples_read,
2238
+ ISNULL(us.user_lookups, 0) as tuples_fetched
2239
+ FROM sys.indexes i
2240
+ INNER JOIN sys.objects o ON i.object_id = o.object_id
2241
+ INNER JOIN sys.schemas sch ON o.schema_id = sch.schema_id
2242
+ LEFT JOIN sys.dm_db_partition_stats s ON i.object_id = s.object_id AND i.index_id = s.index_id
2243
+ LEFT JOIN sys.dm_db_index_usage_stats us ON i.object_id = us.object_id AND i.index_id = us.index_id
2244
+ WHERE i.name = @indexName
2245
+ AND sch.name = @schemaName
2246
+ GROUP BY i.name, o.name, i.is_unique, i.type_desc, us.user_seeks, us.user_scans, us.user_lookups
2247
+ `;
2248
+ const result = await request.query(query);
2249
+ if (!result.recordset || result.recordset.length === 0) {
2250
+ throw new Error(`Index "${indexName}" not found in schema "${schemaName}"`);
1378
2251
  }
1379
- await transaction.commit();
2252
+ const row = result.recordset[0];
2253
+ const colRequest = this.pool.request();
2254
+ colRequest.input("indexName", indexName);
2255
+ colRequest.input("schemaName", schemaName);
2256
+ const colResult = await colRequest.query(`
2257
+ SELECT c.name as column_name
2258
+ FROM sys.indexes i
2259
+ INNER JOIN sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id
2260
+ INNER JOIN sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id
2261
+ INNER JOIN sys.objects o ON i.object_id = o.object_id
2262
+ INNER JOIN sys.schemas s ON o.schema_id = s.schema_id
2263
+ WHERE i.name = @indexName
2264
+ AND s.name = @schemaName
2265
+ ORDER BY ic.key_ordinal
2266
+ `);
2267
+ return {
2268
+ name: row.name,
2269
+ table: row.table,
2270
+ columns: colResult.recordset.map((c) => c.column_name),
2271
+ unique: row.is_unique || false,
2272
+ size: row.size || "0 MB",
2273
+ definition: "",
2274
+ method: row.method?.toLowerCase() || "nonclustered",
2275
+ scans: Number(row.scans) || 0,
2276
+ tuples_read: Number(row.tuples_read) || 0,
2277
+ tuples_fetched: Number(row.tuples_fetched) || 0
2278
+ };
1380
2279
  } catch (error) {
1381
- await transaction.rollback();
1382
2280
  throw new MastraError(
1383
2281
  {
1384
- id: "MASTRA_STORAGE_MSSQL_STORE_BATCH_INSERT_FAILED",
2282
+ id: "MASTRA_STORAGE_MSSQL_INDEX_DESCRIBE_FAILED",
1385
2283
  domain: ErrorDomain.STORAGE,
1386
2284
  category: ErrorCategory.THIRD_PARTY,
1387
2285
  details: {
1388
- tableName,
1389
- numberOfRecords: records.length
2286
+ indexName
1390
2287
  }
1391
2288
  },
1392
2289
  error
1393
2290
  );
1394
2291
  }
1395
2292
  }
1396
- async dropTable({ tableName }) {
2293
+ /**
2294
+ * Returns definitions for automatic performance indexes
2295
+ * IMPORTANT: Uses seq_id DESC instead of createdAt DESC for MSSQL due to millisecond accuracy limitations
2296
+ * NOTE: Using NVARCHAR(400) for text columns (800 bytes) leaves room for composite indexes
2297
+ */
2298
+ getAutomaticIndexDefinitions() {
2299
+ const schemaPrefix = this.schemaName ? `${this.schemaName}_` : "";
2300
+ return [
2301
+ // Composite indexes for optimal filtering + sorting performance
2302
+ // NVARCHAR(400) = 800 bytes, plus BIGINT (8 bytes) = 808 bytes total (under 900-byte limit)
2303
+ {
2304
+ name: `${schemaPrefix}mastra_threads_resourceid_seqid_idx`,
2305
+ table: TABLE_THREADS,
2306
+ columns: ["resourceId", "seq_id DESC"]
2307
+ },
2308
+ {
2309
+ name: `${schemaPrefix}mastra_messages_thread_id_seqid_idx`,
2310
+ table: TABLE_MESSAGES,
2311
+ columns: ["thread_id", "seq_id DESC"]
2312
+ },
2313
+ {
2314
+ name: `${schemaPrefix}mastra_traces_name_seqid_idx`,
2315
+ table: TABLE_TRACES,
2316
+ columns: ["name", "seq_id DESC"]
2317
+ },
2318
+ {
2319
+ name: `${schemaPrefix}mastra_scores_trace_id_span_id_seqid_idx`,
2320
+ table: TABLE_SCORERS,
2321
+ columns: ["traceId", "spanId", "seq_id DESC"]
2322
+ },
2323
+ // AI Spans indexes for optimal trace querying
2324
+ {
2325
+ name: `${schemaPrefix}mastra_ai_spans_traceid_startedat_idx`,
2326
+ table: TABLE_AI_SPANS,
2327
+ columns: ["traceId", "startedAt DESC"]
2328
+ },
2329
+ {
2330
+ name: `${schemaPrefix}mastra_ai_spans_parentspanid_startedat_idx`,
2331
+ table: TABLE_AI_SPANS,
2332
+ columns: ["parentSpanId", "startedAt DESC"]
2333
+ },
2334
+ {
2335
+ name: `${schemaPrefix}mastra_ai_spans_name_idx`,
2336
+ table: TABLE_AI_SPANS,
2337
+ columns: ["name"]
2338
+ },
2339
+ {
2340
+ name: `${schemaPrefix}mastra_ai_spans_spantype_startedat_idx`,
2341
+ table: TABLE_AI_SPANS,
2342
+ columns: ["spanType", "startedAt DESC"]
2343
+ }
2344
+ ];
2345
+ }
2346
+ /**
2347
+ * Creates automatic indexes for optimal query performance
2348
+ * Uses getAutomaticIndexDefinitions() to determine which indexes to create
2349
+ */
2350
+ async createAutomaticIndexes() {
1397
2351
  try {
1398
- const tableNameWithSchema = getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) });
1399
- await this.pool.request().query(`DROP TABLE IF EXISTS ${tableNameWithSchema}`);
2352
+ const indexes = this.getAutomaticIndexDefinitions();
2353
+ for (const indexOptions of indexes) {
2354
+ try {
2355
+ await this.createIndex(indexOptions);
2356
+ } catch (error) {
2357
+ this.logger?.warn?.(`Failed to create index ${indexOptions.name}:`, error);
2358
+ }
2359
+ }
1400
2360
  } catch (error) {
1401
2361
  throw new MastraError(
1402
2362
  {
1403
- id: "MASTRA_STORAGE_MSSQL_STORE_DROP_TABLE_FAILED",
2363
+ id: "MASTRA_STORAGE_MSSQL_STORE_CREATE_PERFORMANCE_INDEXES_FAILED",
1404
2364
  domain: ErrorDomain.STORAGE,
1405
- category: ErrorCategory.THIRD_PARTY,
1406
- details: {
1407
- tableName
1408
- }
2365
+ category: ErrorCategory.THIRD_PARTY
1409
2366
  },
1410
2367
  error
1411
2368
  );
1412
2369
  }
1413
2370
  }
1414
2371
  };
1415
- function parseJSON(jsonString) {
1416
- try {
1417
- return JSON.parse(jsonString);
1418
- } catch {
1419
- return jsonString;
1420
- }
1421
- }
1422
2372
  function transformScoreRow(row) {
1423
2373
  return {
1424
2374
  ...row,
1425
- input: parseJSON(row.input),
1426
- scorer: parseJSON(row.scorer),
1427
- preprocessStepResult: parseJSON(row.preprocessStepResult),
1428
- analyzeStepResult: parseJSON(row.analyzeStepResult),
1429
- metadata: parseJSON(row.metadata),
1430
- output: parseJSON(row.output),
1431
- additionalContext: parseJSON(row.additionalContext),
1432
- runtimeContext: parseJSON(row.runtimeContext),
1433
- entity: parseJSON(row.entity),
2375
+ input: safelyParseJSON(row.input),
2376
+ scorer: safelyParseJSON(row.scorer),
2377
+ preprocessStepResult: safelyParseJSON(row.preprocessStepResult),
2378
+ analyzeStepResult: safelyParseJSON(row.analyzeStepResult),
2379
+ metadata: safelyParseJSON(row.metadata),
2380
+ output: safelyParseJSON(row.output),
2381
+ additionalContext: safelyParseJSON(row.additionalContext),
2382
+ requestContext: safelyParseJSON(row.requestContext),
2383
+ entity: safelyParseJSON(row.entity),
1434
2384
  createdAt: row.createdAt,
1435
2385
  updatedAt: row.updatedAt
1436
2386
  };
@@ -1473,8 +2423,21 @@ var ScoresMSSQL = class extends ScoresStorage {
1473
2423
  }
1474
2424
  }
1475
2425
  async saveScore(score) {
2426
+ let validatedScore;
1476
2427
  try {
1477
- const scoreId = crypto.randomUUID();
2428
+ validatedScore = saveScorePayloadSchema.parse(score);
2429
+ } catch (error) {
2430
+ throw new MastraError(
2431
+ {
2432
+ id: "MASTRA_STORAGE_MSSQL_STORE_SAVE_SCORE_VALIDATION_FAILED",
2433
+ domain: ErrorDomain.STORAGE,
2434
+ category: ErrorCategory.THIRD_PARTY
2435
+ },
2436
+ error
2437
+ );
2438
+ }
2439
+ try {
2440
+ const scoreId = randomUUID();
1478
2441
  const {
1479
2442
  scorer,
1480
2443
  preprocessStepResult,
@@ -1483,24 +2446,24 @@ var ScoresMSSQL = class extends ScoresStorage {
1483
2446
  input,
1484
2447
  output,
1485
2448
  additionalContext,
1486
- runtimeContext,
2449
+ requestContext,
1487
2450
  entity,
1488
2451
  ...rest
1489
- } = score;
2452
+ } = validatedScore;
1490
2453
  await this.operations.insert({
1491
2454
  tableName: TABLE_SCORERS,
1492
2455
  record: {
1493
2456
  id: scoreId,
1494
2457
  ...rest,
1495
- input: JSON.stringify(input) || "",
1496
- output: JSON.stringify(output) || "",
1497
- preprocessStepResult: preprocessStepResult ? JSON.stringify(preprocessStepResult) : null,
1498
- analyzeStepResult: analyzeStepResult ? JSON.stringify(analyzeStepResult) : null,
1499
- metadata: metadata ? JSON.stringify(metadata) : null,
1500
- additionalContext: additionalContext ? JSON.stringify(additionalContext) : null,
1501
- runtimeContext: runtimeContext ? JSON.stringify(runtimeContext) : null,
1502
- entity: entity ? JSON.stringify(entity) : null,
1503
- scorer: scorer ? JSON.stringify(scorer) : null,
2458
+ input: input || "",
2459
+ output: output || "",
2460
+ preprocessStepResult: preprocessStepResult || null,
2461
+ analyzeStepResult: analyzeStepResult || null,
2462
+ metadata: metadata || null,
2463
+ additionalContext: additionalContext || null,
2464
+ requestContext: requestContext || null,
2465
+ entity: entity || null,
2466
+ scorer: scorer || null,
1504
2467
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1505
2468
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1506
2469
  }
@@ -1520,14 +2483,37 @@ var ScoresMSSQL = class extends ScoresStorage {
1520
2483
  }
1521
2484
  async getScoresByScorerId({
1522
2485
  scorerId,
1523
- pagination
2486
+ pagination,
2487
+ entityId,
2488
+ entityType,
2489
+ source
1524
2490
  }) {
1525
2491
  try {
1526
- const request = this.pool.request();
1527
- request.input("p1", scorerId);
1528
- const totalResult = await request.query(
1529
- `SELECT COUNT(*) as count FROM ${getTableName({ indexName: TABLE_SCORERS, schemaName: getSchemaName(this.schema) })} WHERE [scorerId] = @p1`
1530
- );
2492
+ const conditions = ["[scorerId] = @p1"];
2493
+ const params = { p1: scorerId };
2494
+ let paramIndex = 2;
2495
+ if (entityId) {
2496
+ conditions.push(`[entityId] = @p${paramIndex}`);
2497
+ params[`p${paramIndex}`] = entityId;
2498
+ paramIndex++;
2499
+ }
2500
+ if (entityType) {
2501
+ conditions.push(`[entityType] = @p${paramIndex}`);
2502
+ params[`p${paramIndex}`] = entityType;
2503
+ paramIndex++;
2504
+ }
2505
+ if (source) {
2506
+ conditions.push(`[source] = @p${paramIndex}`);
2507
+ params[`p${paramIndex}`] = source;
2508
+ paramIndex++;
2509
+ }
2510
+ const whereClause = conditions.join(" AND ");
2511
+ const tableName = getTableName({ indexName: TABLE_SCORERS, schemaName: getSchemaName(this.schema) });
2512
+ const countRequest = this.pool.request();
2513
+ Object.entries(params).forEach(([key, value]) => {
2514
+ countRequest.input(key, value);
2515
+ });
2516
+ const totalResult = await countRequest.query(`SELECT COUNT(*) as count FROM ${tableName} WHERE ${whereClause}`);
1531
2517
  const total = totalResult.recordset[0]?.count || 0;
1532
2518
  if (total === 0) {
1533
2519
  return {
@@ -1541,12 +2527,13 @@ var ScoresMSSQL = class extends ScoresStorage {
1541
2527
  };
1542
2528
  }
1543
2529
  const dataRequest = this.pool.request();
1544
- dataRequest.input("p1", scorerId);
1545
- dataRequest.input("p2", pagination.perPage);
1546
- dataRequest.input("p3", pagination.page * pagination.perPage);
1547
- const result = await dataRequest.query(
1548
- `SELECT * FROM ${getTableName({ indexName: TABLE_SCORERS, schemaName: getSchemaName(this.schema) })} WHERE [scorerId] = @p1 ORDER BY [createdAt] DESC OFFSET @p3 ROWS FETCH NEXT @p2 ROWS ONLY`
1549
- );
2530
+ Object.entries(params).forEach(([key, value]) => {
2531
+ dataRequest.input(key, value);
2532
+ });
2533
+ dataRequest.input("perPage", pagination.perPage);
2534
+ dataRequest.input("offset", pagination.page * pagination.perPage);
2535
+ const dataQuery = `SELECT * FROM ${tableName} WHERE ${whereClause} ORDER BY [createdAt] DESC OFFSET @offset ROWS FETCH NEXT @perPage ROWS ONLY`;
2536
+ const result = await dataRequest.query(dataQuery);
1550
2537
  return {
1551
2538
  pagination: {
1552
2539
  total: Number(total),
@@ -1671,8 +2658,62 @@ var ScoresMSSQL = class extends ScoresStorage {
1671
2658
  );
1672
2659
  }
1673
2660
  }
2661
+ async getScoresBySpan({
2662
+ traceId,
2663
+ spanId,
2664
+ pagination
2665
+ }) {
2666
+ try {
2667
+ const request = this.pool.request();
2668
+ request.input("p1", traceId);
2669
+ request.input("p2", spanId);
2670
+ const totalResult = await request.query(
2671
+ `SELECT COUNT(*) as count FROM ${getTableName({ indexName: TABLE_SCORERS, schemaName: getSchemaName(this.schema) })} WHERE [traceId] = @p1 AND [spanId] = @p2`
2672
+ );
2673
+ const total = totalResult.recordset[0]?.count || 0;
2674
+ if (total === 0) {
2675
+ return {
2676
+ pagination: {
2677
+ total: 0,
2678
+ page: pagination.page,
2679
+ perPage: pagination.perPage,
2680
+ hasMore: false
2681
+ },
2682
+ scores: []
2683
+ };
2684
+ }
2685
+ const limit = pagination.perPage + 1;
2686
+ const dataRequest = this.pool.request();
2687
+ dataRequest.input("p1", traceId);
2688
+ dataRequest.input("p2", spanId);
2689
+ dataRequest.input("p3", limit);
2690
+ dataRequest.input("p4", pagination.page * pagination.perPage);
2691
+ const result = await dataRequest.query(
2692
+ `SELECT * FROM ${getTableName({ indexName: TABLE_SCORERS, schemaName: getSchemaName(this.schema) })} WHERE [traceId] = @p1 AND [spanId] = @p2 ORDER BY [createdAt] DESC OFFSET @p4 ROWS FETCH NEXT @p3 ROWS ONLY`
2693
+ );
2694
+ return {
2695
+ pagination: {
2696
+ total: Number(total),
2697
+ page: pagination.page,
2698
+ perPage: pagination.perPage,
2699
+ hasMore: result.recordset.length > pagination.perPage
2700
+ },
2701
+ scores: result.recordset.slice(0, pagination.perPage).map((row) => transformScoreRow(row))
2702
+ };
2703
+ } catch (error) {
2704
+ throw new MastraError(
2705
+ {
2706
+ id: "MASTRA_STORAGE_MSSQL_STORE_GET_SCORES_BY_SPAN_FAILED",
2707
+ domain: ErrorDomain.STORAGE,
2708
+ category: ErrorCategory.THIRD_PARTY,
2709
+ details: { traceId, spanId }
2710
+ },
2711
+ error
2712
+ );
2713
+ }
2714
+ }
1674
2715
  };
1675
- var TracesMSSQL = class extends TracesStorage {
2716
+ var WorkflowsMSSQL = class extends WorkflowsStorage {
1676
2717
  pool;
1677
2718
  operations;
1678
2719
  schema;
@@ -1686,210 +2727,168 @@ var TracesMSSQL = class extends TracesStorage {
1686
2727
  this.operations = operations;
1687
2728
  this.schema = schema;
1688
2729
  }
1689
- /** @deprecated use getTracesPaginated instead*/
1690
- async getTraces(args) {
1691
- if (args.fromDate || args.toDate) {
1692
- args.dateRange = {
1693
- start: args.fromDate,
1694
- end: args.toDate
1695
- };
1696
- }
1697
- const result = await this.getTracesPaginated(args);
1698
- return result.traces;
1699
- }
1700
- async getTracesPaginated(args) {
1701
- const { name, scope, page = 0, perPage: perPageInput, attributes, filters, dateRange } = args;
1702
- const fromDate = dateRange?.start;
1703
- const toDate = dateRange?.end;
1704
- const perPage = perPageInput !== void 0 ? perPageInput : 100;
1705
- const currentOffset = page * perPage;
1706
- const paramMap = {};
1707
- const conditions = [];
1708
- let paramIndex = 1;
1709
- if (name) {
1710
- const paramName = `p${paramIndex++}`;
1711
- conditions.push(`[name] LIKE @${paramName}`);
1712
- paramMap[paramName] = `${name}%`;
1713
- }
1714
- if (scope) {
1715
- const paramName = `p${paramIndex++}`;
1716
- conditions.push(`[scope] = @${paramName}`);
1717
- paramMap[paramName] = scope;
1718
- }
1719
- if (attributes) {
1720
- Object.entries(attributes).forEach(([key, value]) => {
1721
- const parsedKey = parseFieldKey(key);
1722
- const paramName = `p${paramIndex++}`;
1723
- conditions.push(`JSON_VALUE([attributes], '$.${parsedKey}') = @${paramName}`);
1724
- paramMap[paramName] = value;
1725
- });
1726
- }
1727
- if (filters) {
1728
- Object.entries(filters).forEach(([key, value]) => {
1729
- const parsedKey = parseFieldKey(key);
1730
- const paramName = `p${paramIndex++}`;
1731
- conditions.push(`[${parsedKey}] = @${paramName}`);
1732
- paramMap[paramName] = value;
1733
- });
1734
- }
1735
- if (fromDate instanceof Date && !isNaN(fromDate.getTime())) {
1736
- const paramName = `p${paramIndex++}`;
1737
- conditions.push(`[createdAt] >= @${paramName}`);
1738
- paramMap[paramName] = fromDate.toISOString();
1739
- }
1740
- if (toDate instanceof Date && !isNaN(toDate.getTime())) {
1741
- const paramName = `p${paramIndex++}`;
1742
- conditions.push(`[createdAt] <= @${paramName}`);
1743
- paramMap[paramName] = toDate.toISOString();
2730
+ parseWorkflowRun(row) {
2731
+ let parsedSnapshot = row.snapshot;
2732
+ if (typeof parsedSnapshot === "string") {
2733
+ try {
2734
+ parsedSnapshot = JSON.parse(row.snapshot);
2735
+ } catch (e) {
2736
+ this.logger?.warn?.(`Failed to parse snapshot for workflow ${row.workflow_name}:`, e);
2737
+ }
1744
2738
  }
1745
- const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
1746
- const countQuery = `SELECT COUNT(*) as total FROM ${getTableName({ indexName: TABLE_TRACES, schemaName: getSchemaName(this.schema) })} ${whereClause}`;
1747
- let total = 0;
2739
+ return {
2740
+ workflowName: row.workflow_name,
2741
+ runId: row.run_id,
2742
+ snapshot: parsedSnapshot,
2743
+ createdAt: row.createdAt,
2744
+ updatedAt: row.updatedAt,
2745
+ resourceId: row.resourceId
2746
+ };
2747
+ }
2748
+ async updateWorkflowResults({
2749
+ workflowName,
2750
+ runId,
2751
+ stepId,
2752
+ result,
2753
+ requestContext
2754
+ }) {
2755
+ const table = getTableName({ indexName: TABLE_WORKFLOW_SNAPSHOT, schemaName: getSchemaName(this.schema) });
2756
+ const transaction = this.pool.transaction();
1748
2757
  try {
1749
- const countRequest = this.pool.request();
1750
- Object.entries(paramMap).forEach(([key, value]) => {
1751
- if (value instanceof Date) {
1752
- countRequest.input(key, sql2.DateTime, value);
1753
- } else {
1754
- countRequest.input(key, value);
1755
- }
1756
- });
1757
- const countResult = await countRequest.query(countQuery);
1758
- total = parseInt(countResult.recordset[0].total, 10);
2758
+ await transaction.begin();
2759
+ const selectRequest = new sql2.Request(transaction);
2760
+ selectRequest.input("workflow_name", workflowName);
2761
+ selectRequest.input("run_id", runId);
2762
+ const existingSnapshotResult = await selectRequest.query(
2763
+ `SELECT snapshot FROM ${table} WITH (UPDLOCK, HOLDLOCK) WHERE workflow_name = @workflow_name AND run_id = @run_id`
2764
+ );
2765
+ let snapshot;
2766
+ if (!existingSnapshotResult.recordset || existingSnapshotResult.recordset.length === 0) {
2767
+ snapshot = {
2768
+ context: {},
2769
+ activePaths: [],
2770
+ timestamp: Date.now(),
2771
+ suspendedPaths: {},
2772
+ resumeLabels: {},
2773
+ serializedStepGraph: [],
2774
+ value: {},
2775
+ waitingPaths: {},
2776
+ status: "pending",
2777
+ runId,
2778
+ requestContext: {}
2779
+ };
2780
+ } else {
2781
+ const existingSnapshot = existingSnapshotResult.recordset[0].snapshot;
2782
+ snapshot = typeof existingSnapshot === "string" ? JSON.parse(existingSnapshot) : existingSnapshot;
2783
+ }
2784
+ snapshot.context[stepId] = result;
2785
+ snapshot.requestContext = { ...snapshot.requestContext, ...requestContext };
2786
+ const upsertReq = new sql2.Request(transaction);
2787
+ upsertReq.input("workflow_name", workflowName);
2788
+ upsertReq.input("run_id", runId);
2789
+ upsertReq.input("snapshot", JSON.stringify(snapshot));
2790
+ upsertReq.input("createdAt", sql2.DateTime2, /* @__PURE__ */ new Date());
2791
+ upsertReq.input("updatedAt", sql2.DateTime2, /* @__PURE__ */ new Date());
2792
+ await upsertReq.query(
2793
+ `MERGE ${table} AS target
2794
+ USING (SELECT @workflow_name AS workflow_name, @run_id AS run_id) AS src
2795
+ ON target.workflow_name = src.workflow_name AND target.run_id = src.run_id
2796
+ WHEN MATCHED THEN UPDATE SET snapshot = @snapshot, [updatedAt] = @updatedAt
2797
+ WHEN NOT MATCHED THEN INSERT (workflow_name, run_id, snapshot, [createdAt], [updatedAt])
2798
+ VALUES (@workflow_name, @run_id, @snapshot, @createdAt, @updatedAt);`
2799
+ );
2800
+ await transaction.commit();
2801
+ return snapshot.context;
1759
2802
  } catch (error) {
2803
+ try {
2804
+ await transaction.rollback();
2805
+ } catch {
2806
+ }
1760
2807
  throw new MastraError(
1761
2808
  {
1762
- id: "MASTRA_STORAGE_MSSQL_STORE_GET_TRACES_PAGINATED_FAILED_TO_RETRIEVE_TOTAL_COUNT",
2809
+ id: "MASTRA_STORAGE_MSSQL_STORE_UPDATE_WORKFLOW_RESULTS_FAILED",
1763
2810
  domain: ErrorDomain.STORAGE,
1764
2811
  category: ErrorCategory.THIRD_PARTY,
1765
2812
  details: {
1766
- name: args.name ?? "",
1767
- scope: args.scope ?? ""
2813
+ workflowName,
2814
+ runId,
2815
+ stepId
1768
2816
  }
1769
2817
  },
1770
2818
  error
1771
2819
  );
1772
2820
  }
1773
- if (total === 0) {
1774
- return {
1775
- traces: [],
1776
- total: 0,
1777
- page,
1778
- perPage,
1779
- hasMore: false
1780
- };
1781
- }
1782
- const dataQuery = `SELECT * FROM ${getTableName({ indexName: TABLE_TRACES, schemaName: getSchemaName(this.schema) })} ${whereClause} ORDER BY [seq_id] DESC OFFSET @offset ROWS FETCH NEXT @limit ROWS ONLY`;
1783
- const dataRequest = this.pool.request();
1784
- Object.entries(paramMap).forEach(([key, value]) => {
1785
- if (value instanceof Date) {
1786
- dataRequest.input(key, sql2.DateTime, value);
1787
- } else {
1788
- dataRequest.input(key, value);
1789
- }
1790
- });
1791
- dataRequest.input("offset", currentOffset);
1792
- dataRequest.input("limit", perPage);
2821
+ }
2822
+ async updateWorkflowState({
2823
+ workflowName,
2824
+ runId,
2825
+ opts
2826
+ }) {
2827
+ const table = getTableName({ indexName: TABLE_WORKFLOW_SNAPSHOT, schemaName: getSchemaName(this.schema) });
2828
+ const transaction = this.pool.transaction();
1793
2829
  try {
1794
- const rowsResult = await dataRequest.query(dataQuery);
1795
- const rows = rowsResult.recordset;
1796
- const traces = rows.map((row) => ({
1797
- id: row.id,
1798
- parentSpanId: row.parentSpanId,
1799
- traceId: row.traceId,
1800
- name: row.name,
1801
- scope: row.scope,
1802
- kind: row.kind,
1803
- status: JSON.parse(row.status),
1804
- events: JSON.parse(row.events),
1805
- links: JSON.parse(row.links),
1806
- attributes: JSON.parse(row.attributes),
1807
- startTime: row.startTime,
1808
- endTime: row.endTime,
1809
- other: row.other,
1810
- createdAt: row.createdAt
1811
- }));
1812
- return {
1813
- traces,
1814
- total,
1815
- page,
1816
- perPage,
1817
- hasMore: currentOffset + traces.length < total
1818
- };
2830
+ await transaction.begin();
2831
+ const selectRequest = new sql2.Request(transaction);
2832
+ selectRequest.input("workflow_name", workflowName);
2833
+ selectRequest.input("run_id", runId);
2834
+ const existingSnapshotResult = await selectRequest.query(
2835
+ `SELECT snapshot FROM ${table} WITH (UPDLOCK, HOLDLOCK) WHERE workflow_name = @workflow_name AND run_id = @run_id`
2836
+ );
2837
+ if (!existingSnapshotResult.recordset || existingSnapshotResult.recordset.length === 0) {
2838
+ await transaction.rollback();
2839
+ return void 0;
2840
+ }
2841
+ const existingSnapshot = existingSnapshotResult.recordset[0].snapshot;
2842
+ const snapshot = typeof existingSnapshot === "string" ? JSON.parse(existingSnapshot) : existingSnapshot;
2843
+ if (!snapshot || !snapshot?.context) {
2844
+ await transaction.rollback();
2845
+ throw new MastraError(
2846
+ {
2847
+ id: "MASTRA_STORAGE_MSSQL_STORE_UPDATE_WORKFLOW_STATE_SNAPSHOT_NOT_FOUND",
2848
+ domain: ErrorDomain.STORAGE,
2849
+ category: ErrorCategory.SYSTEM,
2850
+ details: {
2851
+ workflowName,
2852
+ runId
2853
+ }
2854
+ },
2855
+ new Error(`Snapshot not found for runId ${runId}`)
2856
+ );
2857
+ }
2858
+ const updatedSnapshot = { ...snapshot, ...opts };
2859
+ const updateRequest = new sql2.Request(transaction);
2860
+ updateRequest.input("snapshot", JSON.stringify(updatedSnapshot));
2861
+ updateRequest.input("workflow_name", workflowName);
2862
+ updateRequest.input("run_id", runId);
2863
+ updateRequest.input("updatedAt", sql2.DateTime2, /* @__PURE__ */ new Date());
2864
+ await updateRequest.query(
2865
+ `UPDATE ${table} SET snapshot = @snapshot, [updatedAt] = @updatedAt WHERE workflow_name = @workflow_name AND run_id = @run_id`
2866
+ );
2867
+ await transaction.commit();
2868
+ return updatedSnapshot;
1819
2869
  } catch (error) {
2870
+ try {
2871
+ await transaction.rollback();
2872
+ } catch {
2873
+ }
1820
2874
  throw new MastraError(
1821
2875
  {
1822
- id: "MASTRA_STORAGE_MSSQL_STORE_GET_TRACES_PAGINATED_FAILED_TO_RETRIEVE_TRACES",
2876
+ id: "MASTRA_STORAGE_MSSQL_STORE_UPDATE_WORKFLOW_STATE_FAILED",
1823
2877
  domain: ErrorDomain.STORAGE,
1824
2878
  category: ErrorCategory.THIRD_PARTY,
1825
2879
  details: {
1826
- name: args.name ?? "",
1827
- scope: args.scope ?? ""
2880
+ workflowName,
2881
+ runId
1828
2882
  }
1829
2883
  },
1830
2884
  error
1831
2885
  );
1832
2886
  }
1833
2887
  }
1834
- async batchTraceInsert({ records }) {
1835
- this.logger.debug("Batch inserting traces", { count: records.length });
1836
- await this.operations.batchInsert({
1837
- tableName: TABLE_TRACES,
1838
- records
1839
- });
1840
- }
1841
- };
1842
- function parseWorkflowRun(row) {
1843
- let parsedSnapshot = row.snapshot;
1844
- if (typeof parsedSnapshot === "string") {
1845
- try {
1846
- parsedSnapshot = JSON.parse(row.snapshot);
1847
- } catch (e) {
1848
- console.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
1849
- }
1850
- }
1851
- return {
1852
- workflowName: row.workflow_name,
1853
- runId: row.run_id,
1854
- snapshot: parsedSnapshot,
1855
- createdAt: row.createdAt,
1856
- updatedAt: row.updatedAt,
1857
- resourceId: row.resourceId
1858
- };
1859
- }
1860
- var WorkflowsMSSQL = class extends WorkflowsStorage {
1861
- pool;
1862
- operations;
1863
- schema;
1864
- constructor({
1865
- pool,
1866
- operations,
1867
- schema
1868
- }) {
1869
- super();
1870
- this.pool = pool;
1871
- this.operations = operations;
1872
- this.schema = schema;
1873
- }
1874
- updateWorkflowResults({
1875
- // workflowName,
1876
- // runId,
1877
- // stepId,
1878
- // result,
1879
- // runtimeContext,
1880
- }) {
1881
- throw new Error("Method not implemented.");
1882
- }
1883
- updateWorkflowState({
1884
- // workflowName,
1885
- // runId,
1886
- // opts,
1887
- }) {
1888
- throw new Error("Method not implemented.");
1889
- }
1890
2888
  async persistWorkflowSnapshot({
1891
2889
  workflowName,
1892
2890
  runId,
2891
+ resourceId,
1893
2892
  snapshot
1894
2893
  }) {
1895
2894
  const table = getTableName({ indexName: TABLE_WORKFLOW_SNAPSHOT, schemaName: getSchemaName(this.schema) });
@@ -1898,6 +2897,7 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
1898
2897
  const request = this.pool.request();
1899
2898
  request.input("workflow_name", workflowName);
1900
2899
  request.input("run_id", runId);
2900
+ request.input("resourceId", resourceId);
1901
2901
  request.input("snapshot", JSON.stringify(snapshot));
1902
2902
  request.input("createdAt", sql2.DateTime2, new Date(now));
1903
2903
  request.input("updatedAt", sql2.DateTime2, new Date(now));
@@ -1905,10 +2905,11 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
1905
2905
  USING (SELECT @workflow_name AS workflow_name, @run_id AS run_id) AS src
1906
2906
  ON target.workflow_name = src.workflow_name AND target.run_id = src.run_id
1907
2907
  WHEN MATCHED THEN UPDATE SET
2908
+ resourceId = @resourceId,
1908
2909
  snapshot = @snapshot,
1909
2910
  [updatedAt] = @updatedAt
1910
- WHEN NOT MATCHED THEN INSERT (workflow_name, run_id, snapshot, [createdAt], [updatedAt])
1911
- VALUES (@workflow_name, @run_id, @snapshot, @createdAt, @updatedAt);`;
2911
+ WHEN NOT MATCHED THEN INSERT (workflow_name, run_id, resourceId, snapshot, [createdAt], [updatedAt])
2912
+ VALUES (@workflow_name, @run_id, @resourceId, @snapshot, @createdAt, @updatedAt);`;
1912
2913
  await request.query(mergeSql);
1913
2914
  } catch (error) {
1914
2915
  throw new MastraError(
@@ -1980,7 +2981,7 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
1980
2981
  if (!result.recordset || result.recordset.length === 0) {
1981
2982
  return null;
1982
2983
  }
1983
- return parseWorkflowRun(result.recordset[0]);
2984
+ return this.parseWorkflowRun(result.recordset[0]);
1984
2985
  } catch (error) {
1985
2986
  throw new MastraError(
1986
2987
  {
@@ -1996,7 +2997,7 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
1996
2997
  );
1997
2998
  }
1998
2999
  }
1999
- async getWorkflowRuns({
3000
+ async listWorkflowRuns({
2000
3001
  workflowName,
2001
3002
  fromDate,
2002
3003
  toDate,
@@ -2017,7 +3018,7 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
2017
3018
  conditions.push(`[resourceId] = @resourceId`);
2018
3019
  paramMap["resourceId"] = resourceId;
2019
3020
  } else {
2020
- console.warn(`[${TABLE_WORKFLOW_SNAPSHOT}] resourceId column not found. Skipping resourceId filter.`);
3021
+ this.logger?.warn?.(`[${TABLE_WORKFLOW_SNAPSHOT}] resourceId column not found. Skipping resourceId filter.`);
2021
3022
  }
2022
3023
  }
2023
3024
  if (fromDate instanceof Date && !isNaN(fromDate.getTime())) {
@@ -2051,7 +3052,7 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
2051
3052
  request.input("offset", offset);
2052
3053
  }
2053
3054
  const result = await request.query(query);
2054
- const runs = (result.recordset || []).map((row) => parseWorkflowRun(row));
3055
+ const runs = (result.recordset || []).map((row) => this.parseWorkflowRun(row));
2055
3056
  return { runs, total: total || runs.length };
2056
3057
  } catch (error) {
2057
3058
  throw new MastraError(
@@ -2099,19 +3100,17 @@ var MSSQLStore = class extends MastraStorage {
2099
3100
  port: config.port,
2100
3101
  options: config.options || { encrypt: true, trustServerCertificate: true }
2101
3102
  });
2102
- const legacyEvals = new LegacyEvalsMSSQL({ pool: this.pool, schema: this.schema });
2103
3103
  const operations = new StoreOperationsMSSQL({ pool: this.pool, schemaName: this.schema });
2104
3104
  const scores = new ScoresMSSQL({ pool: this.pool, operations, schema: this.schema });
2105
- const traces = new TracesMSSQL({ pool: this.pool, operations, schema: this.schema });
2106
3105
  const workflows = new WorkflowsMSSQL({ pool: this.pool, operations, schema: this.schema });
2107
3106
  const memory = new MemoryMSSQL({ pool: this.pool, schema: this.schema, operations });
3107
+ const observability = new ObservabilityMSSQL({ pool: this.pool, operations, schema: this.schema });
2108
3108
  this.stores = {
2109
3109
  operations,
2110
3110
  scores,
2111
- traces,
2112
3111
  workflows,
2113
- legacyEvals,
2114
- memory
3112
+ memory,
3113
+ observability
2115
3114
  };
2116
3115
  } catch (e) {
2117
3116
  throw new MastraError(
@@ -2131,6 +3130,11 @@ var MSSQLStore = class extends MastraStorage {
2131
3130
  try {
2132
3131
  await this.isConnected;
2133
3132
  await super.init();
3133
+ try {
3134
+ await this.stores.operations.createAutomaticIndexes();
3135
+ } catch (indexError) {
3136
+ this.logger?.warn?.("Failed to create indexes:", indexError);
3137
+ }
2134
3138
  } catch (error) {
2135
3139
  this.isConnected = null;
2136
3140
  throw new MastraError(
@@ -2157,28 +3161,12 @@ var MSSQLStore = class extends MastraStorage {
2157
3161
  resourceWorkingMemory: true,
2158
3162
  hasColumn: true,
2159
3163
  createTable: true,
2160
- deleteMessages: true
3164
+ deleteMessages: true,
3165
+ getScoresBySpan: true,
3166
+ aiTracing: true,
3167
+ indexManagement: true
2161
3168
  };
2162
3169
  }
2163
- /** @deprecated use getEvals instead */
2164
- async getEvalsByAgentName(agentName, type) {
2165
- return this.stores.legacyEvals.getEvalsByAgentName(agentName, type);
2166
- }
2167
- async getEvals(options = {}) {
2168
- return this.stores.legacyEvals.getEvals(options);
2169
- }
2170
- /**
2171
- * @deprecated use getTracesPaginated instead
2172
- */
2173
- async getTraces(args) {
2174
- return this.stores.traces.getTraces(args);
2175
- }
2176
- async getTracesPaginated(args) {
2177
- return this.stores.traces.getTracesPaginated(args);
2178
- }
2179
- async batchTraceInsert({ records }) {
2180
- return this.stores.traces.batchTraceInsert({ records });
2181
- }
2182
3170
  async createTable({
2183
3171
  tableName,
2184
3172
  schema
@@ -2238,12 +3226,6 @@ var MSSQLStore = class extends MastraStorage {
2238
3226
  async getMessages(args) {
2239
3227
  return this.stores.memory.getMessages(args);
2240
3228
  }
2241
- async getMessagesById({
2242
- messageIds,
2243
- format
2244
- }) {
2245
- return this.stores.memory.getMessagesById({ messageIds, format });
2246
- }
2247
3229
  async getMessagesPaginated(args) {
2248
3230
  return this.stores.memory.getMessagesPaginated(args);
2249
3231
  }
@@ -2279,9 +3261,9 @@ var MSSQLStore = class extends MastraStorage {
2279
3261
  runId,
2280
3262
  stepId,
2281
3263
  result,
2282
- runtimeContext
3264
+ requestContext
2283
3265
  }) {
2284
- return this.stores.workflows.updateWorkflowResults({ workflowName, runId, stepId, result, runtimeContext });
3266
+ return this.stores.workflows.updateWorkflowResults({ workflowName, runId, stepId, result, requestContext });
2285
3267
  }
2286
3268
  async updateWorkflowState({
2287
3269
  workflowName,
@@ -2293,9 +3275,10 @@ var MSSQLStore = class extends MastraStorage {
2293
3275
  async persistWorkflowSnapshot({
2294
3276
  workflowName,
2295
3277
  runId,
3278
+ resourceId,
2296
3279
  snapshot
2297
3280
  }) {
2298
- return this.stores.workflows.persistWorkflowSnapshot({ workflowName, runId, snapshot });
3281
+ return this.stores.workflows.persistWorkflowSnapshot({ workflowName, runId, resourceId, snapshot });
2299
3282
  }
2300
3283
  async loadWorkflowSnapshot({
2301
3284
  workflowName,
@@ -2303,7 +3286,7 @@ var MSSQLStore = class extends MastraStorage {
2303
3286
  }) {
2304
3287
  return this.stores.workflows.loadWorkflowSnapshot({ workflowName, runId });
2305
3288
  }
2306
- async getWorkflowRuns({
3289
+ async listWorkflowRuns({
2307
3290
  workflowName,
2308
3291
  fromDate,
2309
3292
  toDate,
@@ -2311,7 +3294,7 @@ var MSSQLStore = class extends MastraStorage {
2311
3294
  offset,
2312
3295
  resourceId
2313
3296
  } = {}) {
2314
- return this.stores.workflows.getWorkflowRuns({ workflowName, fromDate, toDate, limit, offset, resourceId });
3297
+ return this.stores.workflows.listWorkflowRuns({ workflowName, fromDate, toDate, limit, offset, resourceId });
2315
3298
  }
2316
3299
  async getWorkflowRunById({
2317
3300
  runId,
@@ -2322,6 +3305,60 @@ var MSSQLStore = class extends MastraStorage {
2322
3305
  async close() {
2323
3306
  await this.pool.close();
2324
3307
  }
3308
+ /**
3309
+ * Index Management
3310
+ */
3311
+ async createIndex(options) {
3312
+ return this.stores.operations.createIndex(options);
3313
+ }
3314
+ async listIndexes(tableName) {
3315
+ return this.stores.operations.listIndexes(tableName);
3316
+ }
3317
+ async describeIndex(indexName) {
3318
+ return this.stores.operations.describeIndex(indexName);
3319
+ }
3320
+ async dropIndex(indexName) {
3321
+ return this.stores.operations.dropIndex(indexName);
3322
+ }
3323
+ /**
3324
+ * AI Tracing / Observability
3325
+ */
3326
+ getObservabilityStore() {
3327
+ if (!this.stores.observability) {
3328
+ throw new MastraError({
3329
+ id: "MSSQL_STORE_OBSERVABILITY_NOT_INITIALIZED",
3330
+ domain: ErrorDomain.STORAGE,
3331
+ category: ErrorCategory.SYSTEM,
3332
+ text: "Observability storage is not initialized"
3333
+ });
3334
+ }
3335
+ return this.stores.observability;
3336
+ }
3337
+ async createAISpan(span) {
3338
+ return this.getObservabilityStore().createAISpan(span);
3339
+ }
3340
+ async updateAISpan({
3341
+ spanId,
3342
+ traceId,
3343
+ updates
3344
+ }) {
3345
+ return this.getObservabilityStore().updateAISpan({ spanId, traceId, updates });
3346
+ }
3347
+ async getAITrace(traceId) {
3348
+ return this.getObservabilityStore().getAITrace(traceId);
3349
+ }
3350
+ async getAITracesPaginated(args) {
3351
+ return this.getObservabilityStore().getAITracesPaginated(args);
3352
+ }
3353
+ async batchCreateAISpans(args) {
3354
+ return this.getObservabilityStore().batchCreateAISpans(args);
3355
+ }
3356
+ async batchUpdateAISpans(args) {
3357
+ return this.getObservabilityStore().batchUpdateAISpans(args);
3358
+ }
3359
+ async batchDeleteAITraces(args) {
3360
+ return this.getObservabilityStore().batchDeleteAITraces(args);
3361
+ }
2325
3362
  /**
2326
3363
  * Scorers
2327
3364
  */
@@ -2330,9 +3367,18 @@ var MSSQLStore = class extends MastraStorage {
2330
3367
  }
2331
3368
  async getScoresByScorerId({
2332
3369
  scorerId: _scorerId,
2333
- pagination: _pagination
3370
+ pagination: _pagination,
3371
+ entityId: _entityId,
3372
+ entityType: _entityType,
3373
+ source: _source
2334
3374
  }) {
2335
- return this.stores.scores.getScoresByScorerId({ scorerId: _scorerId, pagination: _pagination });
3375
+ return this.stores.scores.getScoresByScorerId({
3376
+ scorerId: _scorerId,
3377
+ pagination: _pagination,
3378
+ entityId: _entityId,
3379
+ entityType: _entityType,
3380
+ source: _source
3381
+ });
2336
3382
  }
2337
3383
  async saveScore(_score) {
2338
3384
  return this.stores.scores.saveScore(_score);
@@ -2354,6 +3400,13 @@ var MSSQLStore = class extends MastraStorage {
2354
3400
  pagination: _pagination
2355
3401
  });
2356
3402
  }
3403
+ async getScoresBySpan({
3404
+ traceId,
3405
+ spanId,
3406
+ pagination: _pagination
3407
+ }) {
3408
+ return this.stores.scores.getScoresBySpan({ traceId, spanId, pagination: _pagination });
3409
+ }
2357
3410
  };
2358
3411
 
2359
3412
  export { MSSQLStore };