@mastra/mssql 0.0.0-fix-memory-search-fetch-20251027160505 → 0.0.0-fix-persist-session-cache-option-mcp-server-20251030161352

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,9 @@
1
1
  import { MastraError, ErrorCategory, ErrorDomain } from '@mastra/core/error';
2
- import { MastraStorage, LegacyEvalsStorage, StoreOperations, TABLE_WORKFLOW_SNAPSHOT, ScoresStorage, TABLE_SCORERS, 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 } from '@mastra/core/utils';
5
4
  import { MessageList } from '@mastra/core/agent';
5
+ import { parseSqlIdentifier } from '@mastra/core/utils';
6
+ import { randomUUID } from 'crypto';
6
7
  import { saveScorePayloadSchema } from '@mastra/core/scores';
7
8
 
8
9
  // src/storage/index.ts
@@ -15,154 +16,71 @@ function getTableName({ indexName, schemaName }) {
15
16
  const quotedSchemaName = schemaName;
16
17
  return quotedSchemaName ? `${quotedSchemaName}.${quotedIndexName}` : quotedIndexName;
17
18
  }
18
-
19
- // src/storage/domains/legacy-evals/index.ts
20
- function transformEvalRow(row) {
21
- let testInfoValue = null, resultValue = null;
22
- if (row.test_info) {
23
- try {
24
- testInfoValue = typeof row.test_info === "string" ? JSON.parse(row.test_info) : row.test_info;
25
- } catch {
26
- }
19
+ function buildDateRangeFilter(dateRange, fieldName) {
20
+ const filters = {};
21
+ if (dateRange?.start) {
22
+ filters[`${fieldName}_gte`] = dateRange.start;
27
23
  }
28
- if (row.test_info) {
29
- try {
30
- resultValue = typeof row.result === "string" ? JSON.parse(row.result) : row.result;
31
- } catch {
32
- }
24
+ if (dateRange?.end) {
25
+ filters[`${fieldName}_lte`] = dateRange.end;
33
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
+ });
34
51
  return {
35
- agentName: row.agent_name,
36
- input: row.input,
37
- output: row.output,
38
- result: resultValue,
39
- metricName: row.metric_name,
40
- instructions: row.instructions,
41
- testInfo: testInfoValue,
42
- globalRunId: row.global_run_id,
43
- runId: row.run_id,
44
- createdAt: row.created_at
52
+ sql: conditions.length > 0 ? ` WHERE ${conditions.join(" AND ")}` : "",
53
+ params
45
54
  };
46
55
  }
47
- var LegacyEvalsMSSQL = class extends LegacyEvalsStorage {
48
- pool;
49
- schema;
50
- constructor({ pool, schema }) {
51
- super();
52
- this.pool = pool;
53
- this.schema = schema;
54
- }
55
- /** @deprecated use getEvals instead */
56
- async getEvalsByAgentName(agentName, type) {
57
- try {
58
- let query = `SELECT * FROM ${getTableName({ indexName: TABLE_EVALS, schemaName: getSchemaName(this.schema) })} WHERE agent_name = @p1`;
59
- if (type === "test") {
60
- query += " AND test_info IS NOT NULL AND JSON_VALUE(test_info, '$.testPath') IS NOT NULL";
61
- } else if (type === "live") {
62
- query += " AND (test_info IS NULL OR JSON_VALUE(test_info, '$.testPath') IS NULL)";
63
- }
64
- query += " ORDER BY created_at DESC";
65
- const request = this.pool.request();
66
- request.input("p1", agentName);
67
- const result = await request.query(query);
68
- const rows = result.recordset;
69
- return typeof transformEvalRow === "function" ? rows?.map((row) => transformEvalRow(row)) ?? [] : rows ?? [];
70
- } catch (error) {
71
- if (error && error.number === 208 && error.message && error.message.includes("Invalid object name")) {
72
- return [];
73
- }
74
- console.error("Failed to get evals for the specified agent: " + error?.message);
75
- throw error;
76
- }
77
- }
78
- async getEvals(options = {}) {
79
- const { agentName, type, page = 0, perPage = 100, dateRange } = options;
80
- const fromDate = dateRange?.start;
81
- const toDate = dateRange?.end;
82
- const where = [];
83
- const params = {};
84
- if (agentName) {
85
- where.push("agent_name = @agentName");
86
- params["agentName"] = agentName;
87
- }
88
- if (type === "test") {
89
- where.push("test_info IS NOT NULL AND JSON_VALUE(test_info, '$.testPath') IS NOT NULL");
90
- } else if (type === "live") {
91
- where.push("(test_info IS NULL OR JSON_VALUE(test_info, '$.testPath') IS NULL)");
92
- }
93
- if (fromDate instanceof Date && !isNaN(fromDate.getTime())) {
94
- where.push(`[created_at] >= @fromDate`);
95
- params[`fromDate`] = fromDate.toISOString();
96
- }
97
- if (toDate instanceof Date && !isNaN(toDate.getTime())) {
98
- where.push(`[created_at] <= @toDate`);
99
- params[`toDate`] = toDate.toISOString();
100
- }
101
- const whereClause = where.length > 0 ? `WHERE ${where.join(" AND ")}` : "";
102
- const tableName = getTableName({ indexName: TABLE_EVALS, schemaName: getSchemaName(this.schema) });
103
- const offset = page * perPage;
104
- const countQuery = `SELECT COUNT(*) as total FROM ${tableName} ${whereClause}`;
105
- const dataQuery = `SELECT * FROM ${tableName} ${whereClause} ORDER BY seq_id DESC OFFSET @offset ROWS FETCH NEXT @perPage ROWS ONLY`;
106
- try {
107
- const countReq = this.pool.request();
108
- Object.entries(params).forEach(([key, value]) => {
109
- if (value instanceof Date) {
110
- countReq.input(key, sql2.DateTime, value);
111
- } else {
112
- countReq.input(key, value);
113
- }
114
- });
115
- const countResult = await countReq.query(countQuery);
116
- const total = countResult.recordset[0]?.total || 0;
117
- if (total === 0) {
118
- return {
119
- evals: [],
120
- total: 0,
121
- page,
122
- perPage,
123
- hasMore: false
124
- };
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;
125
69
  }
126
- const req = this.pool.request();
127
- Object.entries(params).forEach(([key, value]) => {
128
- if (value instanceof Date) {
129
- req.input(key, sql2.DateTime, value);
130
- } else {
131
- req.input(key, value);
132
- }
133
- });
134
- req.input("offset", offset);
135
- req.input("perPage", perPage);
136
- const result = await req.query(dataQuery);
137
- const rows = result.recordset;
138
- return {
139
- evals: rows?.map((row) => transformEvalRow(row)) ?? [],
140
- total,
141
- page,
142
- perPage,
143
- hasMore: offset + (rows?.length ?? 0) < total
144
- };
145
- } catch (error) {
146
- const mastraError = new MastraError(
147
- {
148
- id: "MASTRA_STORAGE_MSSQL_STORE_GET_EVALS_FAILED",
149
- domain: ErrorDomain.STORAGE,
150
- category: ErrorCategory.THIRD_PARTY,
151
- details: {
152
- agentName: agentName || "all",
153
- type: type || "all",
154
- page,
155
- perPage
156
- }
157
- },
158
- error
159
- );
160
- this.logger?.error?.(mastraError.toString());
161
- this.logger?.trackException(mastraError);
162
- 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;
163
78
  }
164
- }
165
- };
79
+ });
80
+ return result;
81
+ }
82
+
83
+ // src/storage/domains/memory/index.ts
166
84
  var MemoryMSSQL = class extends MemoryStorage {
167
85
  pool;
168
86
  schema;
@@ -194,7 +112,7 @@ var MemoryMSSQL = class extends MemoryStorage {
194
112
  }
195
113
  async getThreadById({ threadId }) {
196
114
  try {
197
- const sql6 = `SELECT
115
+ const sql5 = `SELECT
198
116
  id,
199
117
  [resourceId],
200
118
  title,
@@ -205,7 +123,7 @@ var MemoryMSSQL = class extends MemoryStorage {
205
123
  WHERE id = @threadId`;
206
124
  const request = this.pool.request();
207
125
  request.input("threadId", threadId);
208
- const resultSet = await request.query(sql6);
126
+ const resultSet = await request.query(sql5);
209
127
  const thread = resultSet.recordset[0] || null;
210
128
  if (!thread) {
211
129
  return null;
@@ -251,7 +169,8 @@ var MemoryMSSQL = class extends MemoryStorage {
251
169
  };
252
170
  }
253
171
  const orderByField = orderBy === "createdAt" ? "[createdAt]" : "[updatedAt]";
254
- 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`;
255
174
  const dataRequest = this.pool.request();
256
175
  dataRequest.input("resourceId", resourceId);
257
176
  dataRequest.input("perPage", perPage);
@@ -308,7 +227,12 @@ var MemoryMSSQL = class extends MemoryStorage {
308
227
  req.input("id", thread.id);
309
228
  req.input("resourceId", thread.resourceId);
310
229
  req.input("title", thread.title);
311
- 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
+ }
312
236
  req.input("createdAt", sql2.DateTime2, thread.createdAt);
313
237
  req.input("updatedAt", sql2.DateTime2, thread.updatedAt);
314
238
  await req.query(mergeSql);
@@ -335,7 +259,8 @@ var MemoryMSSQL = class extends MemoryStorage {
335
259
  try {
336
260
  const baseQuery = `FROM ${getTableName({ indexName: TABLE_THREADS, schemaName: getSchemaName(this.schema) })} WHERE [resourceId] = @resourceId`;
337
261
  const orderByField = orderBy === "createdAt" ? "[createdAt]" : "[updatedAt]";
338
- 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}`;
339
264
  const request = this.pool.request();
340
265
  request.input("resourceId", resourceId);
341
266
  const resultSet = await request.query(dataQuery);
@@ -378,7 +303,7 @@ var MemoryMSSQL = class extends MemoryStorage {
378
303
  };
379
304
  try {
380
305
  const table = getTableName({ indexName: TABLE_THREADS, schemaName: getSchemaName(this.schema) });
381
- const sql6 = `UPDATE ${table}
306
+ const sql5 = `UPDATE ${table}
382
307
  SET title = @title,
383
308
  metadata = @metadata,
384
309
  [updatedAt] = @updatedAt
@@ -389,7 +314,7 @@ var MemoryMSSQL = class extends MemoryStorage {
389
314
  req.input("title", title);
390
315
  req.input("metadata", JSON.stringify(mergedMetadata));
391
316
  req.input("updatedAt", /* @__PURE__ */ new Date());
392
- const result = await req.query(sql6);
317
+ const result = await req.query(sql5);
393
318
  let thread = result.recordset && result.recordset[0];
394
319
  if (thread && "seq_id" in thread) {
395
320
  const { seq_id, ...rest } = thread;
@@ -584,7 +509,7 @@ var MemoryMSSQL = class extends MemoryStorage {
584
509
  error
585
510
  );
586
511
  this.logger?.error?.(mastraError.toString());
587
- this.logger?.trackException(mastraError);
512
+ this.logger?.trackException?.(mastraError);
588
513
  return [];
589
514
  }
590
515
  }
@@ -624,10 +549,24 @@ var MemoryMSSQL = class extends MemoryStorage {
624
549
  error
625
550
  );
626
551
  this.logger?.error?.(mastraError.toString());
627
- this.logger?.trackException(mastraError);
552
+ this.logger?.trackException?.(mastraError);
628
553
  return [];
629
554
  }
630
555
  }
556
+ async listMessages(_args) {
557
+ throw new Error(
558
+ `listMessages is not yet implemented by this storage adapter (${this.constructor.name}). This method is currently being rolled out across all storage adapters. Please use getMessages or getMessagesPaginated as an alternative, or wait for the implementation.`
559
+ );
560
+ }
561
+ async listMessagesById({ messageIds }) {
562
+ return this.getMessagesById({ messageIds, format: "v2" });
563
+ }
564
+ async listThreadsByResourceId(args) {
565
+ const { resourceId, limit, offset, orderBy, sortDirection } = args;
566
+ const page = Math.floor(offset / limit);
567
+ const perPage = limit;
568
+ return this.getThreadsByResourceIdPaginated({ resourceId, page, perPage, orderBy, sortDirection });
569
+ }
631
570
  async getMessagesPaginated(args) {
632
571
  const { threadId, resourceId, format, selectBy } = args;
633
572
  const { page = 0, perPage: perPageInput, dateRange } = selectBy?.pagination || {};
@@ -686,7 +625,7 @@ var MemoryMSSQL = class extends MemoryStorage {
686
625
  const parsed = this._parseAndFormatMessages(messages, format);
687
626
  return {
688
627
  messages: parsed,
689
- total: total + excludeIds.length,
628
+ total,
690
629
  page,
691
630
  perPage,
692
631
  hasMore: currentOffset + rows.length < total
@@ -706,7 +645,7 @@ var MemoryMSSQL = class extends MemoryStorage {
706
645
  error
707
646
  );
708
647
  this.logger?.error?.(mastraError.toString());
709
- this.logger?.trackException(mastraError);
648
+ this.logger?.trackException?.(mastraError);
710
649
  return { messages: [], total: 0, page, perPage: perPageInput || 40, hasMore: false };
711
650
  }
712
651
  }
@@ -973,8 +912,10 @@ var MemoryMSSQL = class extends MemoryStorage {
973
912
  return null;
974
913
  }
975
914
  return {
976
- ...result,
977
- workingMemory: typeof result.workingMemory === "object" ? JSON.stringify(result.workingMemory) : result.workingMemory,
915
+ id: result.id,
916
+ createdAt: result.createdAt,
917
+ updatedAt: result.updatedAt,
918
+ workingMemory: result.workingMemory,
978
919
  metadata: typeof result.metadata === "string" ? JSON.parse(result.metadata) : result.metadata
979
920
  };
980
921
  } catch (error) {
@@ -988,7 +929,7 @@ var MemoryMSSQL = class extends MemoryStorage {
988
929
  error
989
930
  );
990
931
  this.logger?.error?.(mastraError.toString());
991
- this.logger?.trackException(mastraError);
932
+ this.logger?.trackException?.(mastraError);
992
933
  throw mastraError;
993
934
  }
994
935
  }
@@ -997,7 +938,7 @@ var MemoryMSSQL = class extends MemoryStorage {
997
938
  tableName: TABLE_RESOURCES,
998
939
  record: {
999
940
  ...resource,
1000
- metadata: JSON.stringify(resource.metadata)
941
+ metadata: resource.metadata
1001
942
  }
1002
943
  });
1003
944
  return resource;
@@ -1055,20 +996,337 @@ var MemoryMSSQL = class extends MemoryStorage {
1055
996
  error
1056
997
  );
1057
998
  this.logger?.error?.(mastraError.toString());
1058
- this.logger?.trackException(mastraError);
999
+ this.logger?.trackException?.(mastraError);
1059
1000
  throw mastraError;
1060
1001
  }
1061
1002
  }
1062
1003
  };
1004
+ var ObservabilityMSSQL = class extends ObservabilityStorage {
1005
+ pool;
1006
+ operations;
1007
+ schema;
1008
+ constructor({
1009
+ pool,
1010
+ operations,
1011
+ schema
1012
+ }) {
1013
+ super();
1014
+ this.pool = pool;
1015
+ this.operations = operations;
1016
+ this.schema = schema;
1017
+ }
1018
+ get aiTracingStrategy() {
1019
+ return {
1020
+ preferred: "batch-with-updates",
1021
+ supported: ["batch-with-updates", "insert-only"]
1022
+ };
1023
+ }
1024
+ async createAISpan(span) {
1025
+ try {
1026
+ const startedAt = span.startedAt instanceof Date ? span.startedAt.toISOString() : span.startedAt;
1027
+ const endedAt = span.endedAt instanceof Date ? span.endedAt.toISOString() : span.endedAt;
1028
+ const record = {
1029
+ ...span,
1030
+ startedAt,
1031
+ endedAt
1032
+ // Note: createdAt/updatedAt will be set by default values
1033
+ };
1034
+ return this.operations.insert({ tableName: TABLE_AI_SPANS, record });
1035
+ } catch (error) {
1036
+ throw new MastraError(
1037
+ {
1038
+ id: "MSSQL_STORE_CREATE_AI_SPAN_FAILED",
1039
+ domain: ErrorDomain.STORAGE,
1040
+ category: ErrorCategory.USER,
1041
+ details: {
1042
+ spanId: span.spanId,
1043
+ traceId: span.traceId,
1044
+ spanType: span.spanType,
1045
+ spanName: span.name
1046
+ }
1047
+ },
1048
+ error
1049
+ );
1050
+ }
1051
+ }
1052
+ async getAITrace(traceId) {
1053
+ try {
1054
+ const tableName = getTableName({
1055
+ indexName: TABLE_AI_SPANS,
1056
+ schemaName: getSchemaName(this.schema)
1057
+ });
1058
+ const request = this.pool.request();
1059
+ request.input("traceId", traceId);
1060
+ const result = await request.query(
1061
+ `SELECT
1062
+ [traceId], [spanId], [parentSpanId], [name], [scope], [spanType],
1063
+ [attributes], [metadata], [links], [input], [output], [error], [isEvent],
1064
+ [startedAt], [endedAt], [createdAt], [updatedAt]
1065
+ FROM ${tableName}
1066
+ WHERE [traceId] = @traceId
1067
+ ORDER BY [startedAt] DESC`
1068
+ );
1069
+ if (!result.recordset || result.recordset.length === 0) {
1070
+ return null;
1071
+ }
1072
+ return {
1073
+ traceId,
1074
+ spans: result.recordset.map(
1075
+ (span) => transformFromSqlRow({
1076
+ tableName: TABLE_AI_SPANS,
1077
+ sqlRow: span
1078
+ })
1079
+ )
1080
+ };
1081
+ } catch (error) {
1082
+ throw new MastraError(
1083
+ {
1084
+ id: "MSSQL_STORE_GET_AI_TRACE_FAILED",
1085
+ domain: ErrorDomain.STORAGE,
1086
+ category: ErrorCategory.USER,
1087
+ details: {
1088
+ traceId
1089
+ }
1090
+ },
1091
+ error
1092
+ );
1093
+ }
1094
+ }
1095
+ async updateAISpan({
1096
+ spanId,
1097
+ traceId,
1098
+ updates
1099
+ }) {
1100
+ try {
1101
+ const data = { ...updates };
1102
+ if (data.endedAt instanceof Date) {
1103
+ data.endedAt = data.endedAt.toISOString();
1104
+ }
1105
+ if (data.startedAt instanceof Date) {
1106
+ data.startedAt = data.startedAt.toISOString();
1107
+ }
1108
+ await this.operations.update({
1109
+ tableName: TABLE_AI_SPANS,
1110
+ keys: { spanId, traceId },
1111
+ data
1112
+ });
1113
+ } catch (error) {
1114
+ throw new MastraError(
1115
+ {
1116
+ id: "MSSQL_STORE_UPDATE_AI_SPAN_FAILED",
1117
+ domain: ErrorDomain.STORAGE,
1118
+ category: ErrorCategory.USER,
1119
+ details: {
1120
+ spanId,
1121
+ traceId
1122
+ }
1123
+ },
1124
+ error
1125
+ );
1126
+ }
1127
+ }
1128
+ async getAITracesPaginated({
1129
+ filters,
1130
+ pagination
1131
+ }) {
1132
+ const page = pagination?.page ?? 0;
1133
+ const perPage = pagination?.perPage ?? 10;
1134
+ const { entityId, entityType, ...actualFilters } = filters || {};
1135
+ const filtersWithDateRange = {
1136
+ ...actualFilters,
1137
+ ...buildDateRangeFilter(pagination?.dateRange, "startedAt"),
1138
+ parentSpanId: null
1139
+ // Only get root spans for traces
1140
+ };
1141
+ const whereClause = prepareWhereClause(filtersWithDateRange);
1142
+ let actualWhereClause = whereClause.sql;
1143
+ const params = { ...whereClause.params };
1144
+ let currentParamIndex = Object.keys(params).length + 1;
1145
+ if (entityId && entityType) {
1146
+ let name = "";
1147
+ if (entityType === "workflow") {
1148
+ name = `workflow run: '${entityId}'`;
1149
+ } else if (entityType === "agent") {
1150
+ name = `agent run: '${entityId}'`;
1151
+ } else {
1152
+ const error = new MastraError({
1153
+ id: "MSSQL_STORE_GET_AI_TRACES_PAGINATED_FAILED",
1154
+ domain: ErrorDomain.STORAGE,
1155
+ category: ErrorCategory.USER,
1156
+ details: {
1157
+ entityType
1158
+ },
1159
+ text: `Cannot filter by entity type: ${entityType}`
1160
+ });
1161
+ throw error;
1162
+ }
1163
+ const entityParam = `p${currentParamIndex++}`;
1164
+ if (actualWhereClause) {
1165
+ actualWhereClause += ` AND [name] = @${entityParam}`;
1166
+ } else {
1167
+ actualWhereClause = ` WHERE [name] = @${entityParam}`;
1168
+ }
1169
+ params[entityParam] = name;
1170
+ }
1171
+ const tableName = getTableName({
1172
+ indexName: TABLE_AI_SPANS,
1173
+ schemaName: getSchemaName(this.schema)
1174
+ });
1175
+ try {
1176
+ const countRequest = this.pool.request();
1177
+ Object.entries(params).forEach(([key, value]) => {
1178
+ countRequest.input(key, value);
1179
+ });
1180
+ const countResult = await countRequest.query(
1181
+ `SELECT COUNT(*) as count FROM ${tableName}${actualWhereClause}`
1182
+ );
1183
+ const total = countResult.recordset[0]?.count ?? 0;
1184
+ if (total === 0) {
1185
+ return {
1186
+ pagination: {
1187
+ total: 0,
1188
+ page,
1189
+ perPage,
1190
+ hasMore: false
1191
+ },
1192
+ spans: []
1193
+ };
1194
+ }
1195
+ const dataRequest = this.pool.request();
1196
+ Object.entries(params).forEach(([key, value]) => {
1197
+ dataRequest.input(key, value);
1198
+ });
1199
+ dataRequest.input("offset", page * perPage);
1200
+ dataRequest.input("limit", perPage);
1201
+ const dataResult = await dataRequest.query(
1202
+ `SELECT * FROM ${tableName}${actualWhereClause} ORDER BY [startedAt] DESC OFFSET @offset ROWS FETCH NEXT @limit ROWS ONLY`
1203
+ );
1204
+ const spans = dataResult.recordset.map(
1205
+ (row) => transformFromSqlRow({
1206
+ tableName: TABLE_AI_SPANS,
1207
+ sqlRow: row
1208
+ })
1209
+ );
1210
+ return {
1211
+ pagination: {
1212
+ total,
1213
+ page,
1214
+ perPage,
1215
+ hasMore: (page + 1) * perPage < total
1216
+ },
1217
+ spans
1218
+ };
1219
+ } catch (error) {
1220
+ throw new MastraError(
1221
+ {
1222
+ id: "MSSQL_STORE_GET_AI_TRACES_PAGINATED_FAILED",
1223
+ domain: ErrorDomain.STORAGE,
1224
+ category: ErrorCategory.USER
1225
+ },
1226
+ error
1227
+ );
1228
+ }
1229
+ }
1230
+ async batchCreateAISpans(args) {
1231
+ if (!args.records || args.records.length === 0) {
1232
+ return;
1233
+ }
1234
+ try {
1235
+ await this.operations.batchInsert({
1236
+ tableName: TABLE_AI_SPANS,
1237
+ records: args.records.map((span) => ({
1238
+ ...span,
1239
+ startedAt: span.startedAt instanceof Date ? span.startedAt.toISOString() : span.startedAt,
1240
+ endedAt: span.endedAt instanceof Date ? span.endedAt.toISOString() : span.endedAt
1241
+ }))
1242
+ });
1243
+ } catch (error) {
1244
+ throw new MastraError(
1245
+ {
1246
+ id: "MSSQL_STORE_BATCH_CREATE_AI_SPANS_FAILED",
1247
+ domain: ErrorDomain.STORAGE,
1248
+ category: ErrorCategory.USER,
1249
+ details: {
1250
+ count: args.records.length
1251
+ }
1252
+ },
1253
+ error
1254
+ );
1255
+ }
1256
+ }
1257
+ async batchUpdateAISpans(args) {
1258
+ if (!args.records || args.records.length === 0) {
1259
+ return;
1260
+ }
1261
+ try {
1262
+ const updates = args.records.map(({ traceId, spanId, updates: data }) => {
1263
+ const processedData = { ...data };
1264
+ if (processedData.endedAt instanceof Date) {
1265
+ processedData.endedAt = processedData.endedAt.toISOString();
1266
+ }
1267
+ if (processedData.startedAt instanceof Date) {
1268
+ processedData.startedAt = processedData.startedAt.toISOString();
1269
+ }
1270
+ return {
1271
+ keys: { spanId, traceId },
1272
+ data: processedData
1273
+ };
1274
+ });
1275
+ await this.operations.batchUpdate({
1276
+ tableName: TABLE_AI_SPANS,
1277
+ updates
1278
+ });
1279
+ } catch (error) {
1280
+ throw new MastraError(
1281
+ {
1282
+ id: "MSSQL_STORE_BATCH_UPDATE_AI_SPANS_FAILED",
1283
+ domain: ErrorDomain.STORAGE,
1284
+ category: ErrorCategory.USER,
1285
+ details: {
1286
+ count: args.records.length
1287
+ }
1288
+ },
1289
+ error
1290
+ );
1291
+ }
1292
+ }
1293
+ async batchDeleteAITraces(args) {
1294
+ if (!args.traceIds || args.traceIds.length === 0) {
1295
+ return;
1296
+ }
1297
+ try {
1298
+ const keys = args.traceIds.map((traceId) => ({ traceId }));
1299
+ await this.operations.batchDelete({
1300
+ tableName: TABLE_AI_SPANS,
1301
+ keys
1302
+ });
1303
+ } catch (error) {
1304
+ throw new MastraError(
1305
+ {
1306
+ id: "MSSQL_STORE_BATCH_DELETE_AI_TRACES_FAILED",
1307
+ domain: ErrorDomain.STORAGE,
1308
+ category: ErrorCategory.USER,
1309
+ details: {
1310
+ count: args.traceIds.length
1311
+ }
1312
+ },
1313
+ error
1314
+ );
1315
+ }
1316
+ }
1317
+ };
1063
1318
  var StoreOperationsMSSQL = class extends StoreOperations {
1064
1319
  pool;
1065
1320
  schemaName;
1066
1321
  setupSchemaPromise = null;
1067
1322
  schemaSetupComplete = void 0;
1068
- getSqlType(type, isPrimaryKey = false) {
1323
+ getSqlType(type, isPrimaryKey = false, useLargeStorage = false) {
1069
1324
  switch (type) {
1070
1325
  case "text":
1071
- return isPrimaryKey ? "NVARCHAR(255)" : "NVARCHAR(MAX)";
1326
+ if (useLargeStorage) {
1327
+ return "NVARCHAR(MAX)";
1328
+ }
1329
+ return isPrimaryKey ? "NVARCHAR(255)" : "NVARCHAR(400)";
1072
1330
  case "timestamp":
1073
1331
  return "DATETIME2(7)";
1074
1332
  case "uuid":
@@ -1081,6 +1339,8 @@ var StoreOperationsMSSQL = class extends StoreOperations {
1081
1339
  return "BIGINT";
1082
1340
  case "float":
1083
1341
  return "FLOAT";
1342
+ case "boolean":
1343
+ return "BIT";
1084
1344
  default:
1085
1345
  throw new MastraError({
1086
1346
  id: "MASTRA_STORAGE_MSSQL_STORE_TYPE_NOT_SUPPORTED",
@@ -1143,20 +1403,26 @@ var StoreOperationsMSSQL = class extends StoreOperations {
1143
1403
  }
1144
1404
  await this.setupSchemaPromise;
1145
1405
  }
1146
- async insert({ tableName, record }) {
1406
+ async insert({
1407
+ tableName,
1408
+ record,
1409
+ transaction
1410
+ }) {
1147
1411
  try {
1148
- const columns = Object.keys(record).map((col) => parseSqlIdentifier(col, "column name"));
1149
- const values = Object.values(record);
1150
- const paramNames = values.map((_, i) => `@param${i}`);
1151
- const insertSql = `INSERT INTO ${getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) })} (${columns.map((c) => `[${c}]`).join(", ")}) VALUES (${paramNames.join(", ")})`;
1152
- const request = this.pool.request();
1153
- values.forEach((value, i) => {
1154
- if (value instanceof Date) {
1155
- request.input(`param${i}`, sql2.DateTime2, value);
1156
- } else if (typeof value === "object" && value !== null) {
1157
- request.input(`param${i}`, JSON.stringify(value));
1412
+ const columns = Object.keys(record);
1413
+ const parsedColumns = columns.map((col) => parseSqlIdentifier(col, "column name"));
1414
+ const paramNames = columns.map((_, i) => `@param${i}`);
1415
+ const insertSql = `INSERT INTO ${getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) })} (${parsedColumns.map((c) => `[${c}]`).join(", ")}) VALUES (${paramNames.join(", ")})`;
1416
+ const request = transaction ? transaction.request() : this.pool.request();
1417
+ columns.forEach((col, i) => {
1418
+ const value = record[col];
1419
+ const preparedValue = this.prepareValue(value, col, tableName);
1420
+ if (preparedValue instanceof Date) {
1421
+ request.input(`param${i}`, sql2.DateTime2, preparedValue);
1422
+ } else if (preparedValue === null || preparedValue === void 0) {
1423
+ request.input(`param${i}`, this.getMssqlType(tableName, col), null);
1158
1424
  } else {
1159
- request.input(`param${i}`, value);
1425
+ request.input(`param${i}`, preparedValue);
1160
1426
  }
1161
1427
  });
1162
1428
  await request.query(insertSql);
@@ -1180,7 +1446,7 @@ var StoreOperationsMSSQL = class extends StoreOperations {
1180
1446
  try {
1181
1447
  await this.pool.request().query(`TRUNCATE TABLE ${fullTableName}`);
1182
1448
  } catch (truncateError) {
1183
- if (truncateError.message && truncateError.message.includes("foreign key")) {
1449
+ if (truncateError?.number === 4712) {
1184
1450
  await this.pool.request().query(`DELETE FROM ${fullTableName}`);
1185
1451
  } else {
1186
1452
  throw truncateError;
@@ -1203,9 +1469,11 @@ var StoreOperationsMSSQL = class extends StoreOperations {
1203
1469
  getDefaultValue(type) {
1204
1470
  switch (type) {
1205
1471
  case "timestamp":
1206
- return "DEFAULT SYSDATETIMEOFFSET()";
1472
+ return "DEFAULT SYSUTCDATETIME()";
1207
1473
  case "jsonb":
1208
1474
  return "DEFAULT N'{}'";
1475
+ case "boolean":
1476
+ return "DEFAULT 0";
1209
1477
  default:
1210
1478
  return super.getDefaultValue(type);
1211
1479
  }
@@ -1216,13 +1484,29 @@ var StoreOperationsMSSQL = class extends StoreOperations {
1216
1484
  }) {
1217
1485
  try {
1218
1486
  const uniqueConstraintColumns = tableName === TABLE_WORKFLOW_SNAPSHOT ? ["workflow_name", "run_id"] : [];
1487
+ const largeDataColumns = [
1488
+ "workingMemory",
1489
+ "snapshot",
1490
+ "metadata",
1491
+ "content",
1492
+ // messages.content - can be very long conversation content
1493
+ "input",
1494
+ // evals.input - test input data
1495
+ "output",
1496
+ // evals.output - test output data
1497
+ "instructions",
1498
+ // evals.instructions - evaluation instructions
1499
+ "other"
1500
+ // traces.other - additional trace data
1501
+ ];
1219
1502
  const columns = Object.entries(schema).map(([name, def]) => {
1220
1503
  const parsedName = parseSqlIdentifier(name, "column name");
1221
1504
  const constraints = [];
1222
1505
  if (def.primaryKey) constraints.push("PRIMARY KEY");
1223
1506
  if (!def.nullable) constraints.push("NOT NULL");
1224
1507
  const isIndexed = !!def.primaryKey || uniqueConstraintColumns.includes(name);
1225
- return `[${parsedName}] ${this.getSqlType(def.type, isIndexed)} ${constraints.join(" ")}`.trim();
1508
+ const useLargeStorage = largeDataColumns.includes(name);
1509
+ return `[${parsedName}] ${this.getSqlType(def.type, isIndexed, useLargeStorage)} ${constraints.join(" ")}`.trim();
1226
1510
  }).join(",\n");
1227
1511
  if (this.schemaName) {
1228
1512
  await this.setupSchema();
@@ -1309,7 +1593,19 @@ ${columns}
1309
1593
  const columnExists = Array.isArray(checkResult.recordset) && checkResult.recordset.length > 0;
1310
1594
  if (!columnExists) {
1311
1595
  const columnDef = schema[columnName];
1312
- const sqlType = this.getSqlType(columnDef.type);
1596
+ const largeDataColumns = [
1597
+ "workingMemory",
1598
+ "snapshot",
1599
+ "metadata",
1600
+ "content",
1601
+ "input",
1602
+ "output",
1603
+ "instructions",
1604
+ "other"
1605
+ ];
1606
+ const useLargeStorage = largeDataColumns.includes(columnName);
1607
+ const isIndexed = !!columnDef.primaryKey;
1608
+ const sqlType = this.getSqlType(columnDef.type, isIndexed, useLargeStorage);
1313
1609
  const nullable = columnDef.nullable === false ? "NOT NULL" : "";
1314
1610
  const defaultValue = columnDef.nullable === false ? this.getDefaultValue(columnDef.type) : "";
1315
1611
  const parsedColumnName = parseSqlIdentifier(columnName, "column name");
@@ -1337,13 +1633,17 @@ ${columns}
1337
1633
  try {
1338
1634
  const keyEntries = Object.entries(keys).map(([key, value]) => [parseSqlIdentifier(key, "column name"), value]);
1339
1635
  const conditions = keyEntries.map(([key], i) => `[${key}] = @param${i}`).join(" AND ");
1340
- const values = keyEntries.map(([_, value]) => value);
1341
- const sql6 = `SELECT * FROM ${getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) })} WHERE ${conditions}`;
1636
+ const sql5 = `SELECT * FROM ${getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) })} WHERE ${conditions}`;
1342
1637
  const request = this.pool.request();
1343
- values.forEach((value, i) => {
1344
- request.input(`param${i}`, value);
1638
+ keyEntries.forEach(([key, value], i) => {
1639
+ const preparedValue = this.prepareValue(value, key, tableName);
1640
+ if (preparedValue === null || preparedValue === void 0) {
1641
+ request.input(`param${i}`, this.getMssqlType(tableName, key), null);
1642
+ } else {
1643
+ request.input(`param${i}`, preparedValue);
1644
+ }
1345
1645
  });
1346
- const resultSet = await request.query(sql6);
1646
+ const resultSet = await request.query(sql5);
1347
1647
  const result = resultSet.recordset[0] || null;
1348
1648
  if (!result) {
1349
1649
  return null;
@@ -1375,7 +1675,7 @@ ${columns}
1375
1675
  try {
1376
1676
  await transaction.begin();
1377
1677
  for (const record of records) {
1378
- await this.insert({ tableName, record });
1678
+ await this.insert({ tableName, record, transaction });
1379
1679
  }
1380
1680
  await transaction.commit();
1381
1681
  } catch (error) {
@@ -1412,26 +1712,562 @@ ${columns}
1412
1712
  );
1413
1713
  }
1414
1714
  }
1415
- };
1416
- function parseJSON(jsonString) {
1417
- try {
1418
- return JSON.parse(jsonString);
1419
- } catch {
1420
- return jsonString;
1715
+ /**
1716
+ * Prepares a value for database operations, handling Date objects and JSON serialization
1717
+ */
1718
+ prepareValue(value, columnName, tableName) {
1719
+ if (value === null || value === void 0) {
1720
+ return value;
1721
+ }
1722
+ if (value instanceof Date) {
1723
+ return value;
1724
+ }
1725
+ const schema = TABLE_SCHEMAS[tableName];
1726
+ const columnSchema = schema?.[columnName];
1727
+ if (columnSchema?.type === "boolean") {
1728
+ return value ? 1 : 0;
1729
+ }
1730
+ if (columnSchema?.type === "jsonb") {
1731
+ return JSON.stringify(value);
1732
+ }
1733
+ if (typeof value === "object") {
1734
+ return JSON.stringify(value);
1735
+ }
1736
+ return value;
1421
1737
  }
1422
- }
1738
+ /**
1739
+ * Maps TABLE_SCHEMAS types to mssql param types (used when value is null)
1740
+ */
1741
+ getMssqlType(tableName, columnName) {
1742
+ const col = TABLE_SCHEMAS[tableName]?.[columnName];
1743
+ switch (col?.type) {
1744
+ case "text":
1745
+ return sql2.NVarChar;
1746
+ case "timestamp":
1747
+ return sql2.DateTime2;
1748
+ case "uuid":
1749
+ return sql2.UniqueIdentifier;
1750
+ case "jsonb":
1751
+ return sql2.NVarChar;
1752
+ case "integer":
1753
+ return sql2.Int;
1754
+ case "bigint":
1755
+ return sql2.BigInt;
1756
+ case "float":
1757
+ return sql2.Float;
1758
+ case "boolean":
1759
+ return sql2.Bit;
1760
+ default:
1761
+ return sql2.NVarChar;
1762
+ }
1763
+ }
1764
+ /**
1765
+ * Update a single record in the database
1766
+ */
1767
+ async update({
1768
+ tableName,
1769
+ keys,
1770
+ data,
1771
+ transaction
1772
+ }) {
1773
+ try {
1774
+ if (!data || Object.keys(data).length === 0) {
1775
+ throw new MastraError({
1776
+ id: "MASTRA_STORAGE_MSSQL_UPDATE_EMPTY_DATA",
1777
+ domain: ErrorDomain.STORAGE,
1778
+ category: ErrorCategory.USER,
1779
+ text: "Cannot update with empty data payload"
1780
+ });
1781
+ }
1782
+ if (!keys || Object.keys(keys).length === 0) {
1783
+ throw new MastraError({
1784
+ id: "MASTRA_STORAGE_MSSQL_UPDATE_EMPTY_KEYS",
1785
+ domain: ErrorDomain.STORAGE,
1786
+ category: ErrorCategory.USER,
1787
+ text: "Cannot update without keys to identify records"
1788
+ });
1789
+ }
1790
+ const setClauses = [];
1791
+ const request = transaction ? transaction.request() : this.pool.request();
1792
+ let paramIndex = 0;
1793
+ Object.entries(data).forEach(([key, value]) => {
1794
+ const parsedKey = parseSqlIdentifier(key, "column name");
1795
+ const paramName = `set${paramIndex++}`;
1796
+ setClauses.push(`[${parsedKey}] = @${paramName}`);
1797
+ const preparedValue = this.prepareValue(value, key, tableName);
1798
+ if (preparedValue === null || preparedValue === void 0) {
1799
+ request.input(paramName, this.getMssqlType(tableName, key), null);
1800
+ } else {
1801
+ request.input(paramName, preparedValue);
1802
+ }
1803
+ });
1804
+ const whereConditions = [];
1805
+ Object.entries(keys).forEach(([key, value]) => {
1806
+ const parsedKey = parseSqlIdentifier(key, "column name");
1807
+ const paramName = `where${paramIndex++}`;
1808
+ whereConditions.push(`[${parsedKey}] = @${paramName}`);
1809
+ const preparedValue = this.prepareValue(value, key, tableName);
1810
+ if (preparedValue === null || preparedValue === void 0) {
1811
+ request.input(paramName, this.getMssqlType(tableName, key), null);
1812
+ } else {
1813
+ request.input(paramName, preparedValue);
1814
+ }
1815
+ });
1816
+ const tableName_ = getTableName({
1817
+ indexName: tableName,
1818
+ schemaName: getSchemaName(this.schemaName)
1819
+ });
1820
+ const updateSql = `UPDATE ${tableName_} SET ${setClauses.join(", ")} WHERE ${whereConditions.join(" AND ")}`;
1821
+ await request.query(updateSql);
1822
+ } catch (error) {
1823
+ throw new MastraError(
1824
+ {
1825
+ id: "MASTRA_STORAGE_MSSQL_STORE_UPDATE_FAILED",
1826
+ domain: ErrorDomain.STORAGE,
1827
+ category: ErrorCategory.THIRD_PARTY,
1828
+ details: {
1829
+ tableName
1830
+ }
1831
+ },
1832
+ error
1833
+ );
1834
+ }
1835
+ }
1836
+ /**
1837
+ * Update multiple records in a single batch transaction
1838
+ */
1839
+ async batchUpdate({
1840
+ tableName,
1841
+ updates
1842
+ }) {
1843
+ const transaction = this.pool.transaction();
1844
+ try {
1845
+ await transaction.begin();
1846
+ for (const { keys, data } of updates) {
1847
+ await this.update({ tableName, keys, data, transaction });
1848
+ }
1849
+ await transaction.commit();
1850
+ } catch (error) {
1851
+ await transaction.rollback();
1852
+ throw new MastraError(
1853
+ {
1854
+ id: "MASTRA_STORAGE_MSSQL_STORE_BATCH_UPDATE_FAILED",
1855
+ domain: ErrorDomain.STORAGE,
1856
+ category: ErrorCategory.THIRD_PARTY,
1857
+ details: {
1858
+ tableName,
1859
+ numberOfRecords: updates.length
1860
+ }
1861
+ },
1862
+ error
1863
+ );
1864
+ }
1865
+ }
1866
+ /**
1867
+ * Delete multiple records by keys
1868
+ */
1869
+ async batchDelete({ tableName, keys }) {
1870
+ if (keys.length === 0) {
1871
+ return;
1872
+ }
1873
+ const tableName_ = getTableName({
1874
+ indexName: tableName,
1875
+ schemaName: getSchemaName(this.schemaName)
1876
+ });
1877
+ const transaction = this.pool.transaction();
1878
+ try {
1879
+ await transaction.begin();
1880
+ for (const keySet of keys) {
1881
+ const conditions = [];
1882
+ const request = transaction.request();
1883
+ let paramIndex = 0;
1884
+ Object.entries(keySet).forEach(([key, value]) => {
1885
+ const parsedKey = parseSqlIdentifier(key, "column name");
1886
+ const paramName = `p${paramIndex++}`;
1887
+ conditions.push(`[${parsedKey}] = @${paramName}`);
1888
+ const preparedValue = this.prepareValue(value, key, tableName);
1889
+ if (preparedValue === null || preparedValue === void 0) {
1890
+ request.input(paramName, this.getMssqlType(tableName, key), null);
1891
+ } else {
1892
+ request.input(paramName, preparedValue);
1893
+ }
1894
+ });
1895
+ const deleteSql = `DELETE FROM ${tableName_} WHERE ${conditions.join(" AND ")}`;
1896
+ await request.query(deleteSql);
1897
+ }
1898
+ await transaction.commit();
1899
+ } catch (error) {
1900
+ await transaction.rollback();
1901
+ throw new MastraError(
1902
+ {
1903
+ id: "MASTRA_STORAGE_MSSQL_STORE_BATCH_DELETE_FAILED",
1904
+ domain: ErrorDomain.STORAGE,
1905
+ category: ErrorCategory.THIRD_PARTY,
1906
+ details: {
1907
+ tableName,
1908
+ numberOfRecords: keys.length
1909
+ }
1910
+ },
1911
+ error
1912
+ );
1913
+ }
1914
+ }
1915
+ /**
1916
+ * Create a new index on a table
1917
+ */
1918
+ async createIndex(options) {
1919
+ try {
1920
+ const { name, table, columns, unique = false, where } = options;
1921
+ const schemaName = this.schemaName || "dbo";
1922
+ const fullTableName = getTableName({
1923
+ indexName: table,
1924
+ schemaName: getSchemaName(this.schemaName)
1925
+ });
1926
+ const indexNameSafe = parseSqlIdentifier(name, "index name");
1927
+ const checkRequest = this.pool.request();
1928
+ checkRequest.input("indexName", indexNameSafe);
1929
+ checkRequest.input("schemaName", schemaName);
1930
+ checkRequest.input("tableName", table);
1931
+ const indexExists = await checkRequest.query(`
1932
+ SELECT 1 as found
1933
+ FROM sys.indexes i
1934
+ INNER JOIN sys.tables t ON i.object_id = t.object_id
1935
+ INNER JOIN sys.schemas s ON t.schema_id = s.schema_id
1936
+ WHERE i.name = @indexName
1937
+ AND s.name = @schemaName
1938
+ AND t.name = @tableName
1939
+ `);
1940
+ if (indexExists.recordset && indexExists.recordset.length > 0) {
1941
+ return;
1942
+ }
1943
+ const uniqueStr = unique ? "UNIQUE " : "";
1944
+ const columnsStr = columns.map((col) => {
1945
+ if (col.includes(" DESC") || col.includes(" ASC")) {
1946
+ const [colName, ...modifiers] = col.split(" ");
1947
+ if (!colName) {
1948
+ throw new Error(`Invalid column specification: ${col}`);
1949
+ }
1950
+ return `[${parseSqlIdentifier(colName, "column name")}] ${modifiers.join(" ")}`;
1951
+ }
1952
+ return `[${parseSqlIdentifier(col, "column name")}]`;
1953
+ }).join(", ");
1954
+ const whereStr = where ? ` WHERE ${where}` : "";
1955
+ const createIndexSql = `CREATE ${uniqueStr}INDEX [${indexNameSafe}] ON ${fullTableName} (${columnsStr})${whereStr}`;
1956
+ await this.pool.request().query(createIndexSql);
1957
+ } catch (error) {
1958
+ throw new MastraError(
1959
+ {
1960
+ id: "MASTRA_STORAGE_MSSQL_INDEX_CREATE_FAILED",
1961
+ domain: ErrorDomain.STORAGE,
1962
+ category: ErrorCategory.THIRD_PARTY,
1963
+ details: {
1964
+ indexName: options.name,
1965
+ tableName: options.table
1966
+ }
1967
+ },
1968
+ error
1969
+ );
1970
+ }
1971
+ }
1972
+ /**
1973
+ * Drop an existing index
1974
+ */
1975
+ async dropIndex(indexName) {
1976
+ try {
1977
+ const schemaName = this.schemaName || "dbo";
1978
+ const indexNameSafe = parseSqlIdentifier(indexName, "index name");
1979
+ const checkRequest = this.pool.request();
1980
+ checkRequest.input("indexName", indexNameSafe);
1981
+ checkRequest.input("schemaName", schemaName);
1982
+ const result = await checkRequest.query(`
1983
+ SELECT t.name as table_name
1984
+ FROM sys.indexes i
1985
+ INNER JOIN sys.tables t ON i.object_id = t.object_id
1986
+ INNER JOIN sys.schemas s ON t.schema_id = s.schema_id
1987
+ WHERE i.name = @indexName
1988
+ AND s.name = @schemaName
1989
+ `);
1990
+ if (!result.recordset || result.recordset.length === 0) {
1991
+ return;
1992
+ }
1993
+ if (result.recordset.length > 1) {
1994
+ const tables = result.recordset.map((r) => r.table_name).join(", ");
1995
+ throw new MastraError({
1996
+ id: "MASTRA_STORAGE_MSSQL_INDEX_AMBIGUOUS",
1997
+ domain: ErrorDomain.STORAGE,
1998
+ category: ErrorCategory.USER,
1999
+ text: `Index "${indexNameSafe}" exists on multiple tables (${tables}) in schema "${schemaName}". Please drop indexes manually or ensure unique index names.`
2000
+ });
2001
+ }
2002
+ const tableName = result.recordset[0].table_name;
2003
+ const fullTableName = getTableName({
2004
+ indexName: tableName,
2005
+ schemaName: getSchemaName(this.schemaName)
2006
+ });
2007
+ const dropSql = `DROP INDEX [${indexNameSafe}] ON ${fullTableName}`;
2008
+ await this.pool.request().query(dropSql);
2009
+ } catch (error) {
2010
+ throw new MastraError(
2011
+ {
2012
+ id: "MASTRA_STORAGE_MSSQL_INDEX_DROP_FAILED",
2013
+ domain: ErrorDomain.STORAGE,
2014
+ category: ErrorCategory.THIRD_PARTY,
2015
+ details: {
2016
+ indexName
2017
+ }
2018
+ },
2019
+ error
2020
+ );
2021
+ }
2022
+ }
2023
+ /**
2024
+ * List indexes for a specific table or all tables
2025
+ */
2026
+ async listIndexes(tableName) {
2027
+ try {
2028
+ const schemaName = this.schemaName || "dbo";
2029
+ let query;
2030
+ const request = this.pool.request();
2031
+ request.input("schemaName", schemaName);
2032
+ if (tableName) {
2033
+ query = `
2034
+ SELECT
2035
+ i.name as name,
2036
+ o.name as [table],
2037
+ i.is_unique as is_unique,
2038
+ CAST(SUM(s.used_page_count) * 8 / 1024.0 AS VARCHAR(50)) + ' MB' as size
2039
+ FROM sys.indexes i
2040
+ INNER JOIN sys.objects o ON i.object_id = o.object_id
2041
+ INNER JOIN sys.schemas sch ON o.schema_id = sch.schema_id
2042
+ LEFT JOIN sys.dm_db_partition_stats s ON i.object_id = s.object_id AND i.index_id = s.index_id
2043
+ WHERE sch.name = @schemaName
2044
+ AND o.name = @tableName
2045
+ AND i.name IS NOT NULL
2046
+ GROUP BY i.name, o.name, i.is_unique
2047
+ `;
2048
+ request.input("tableName", tableName);
2049
+ } else {
2050
+ query = `
2051
+ SELECT
2052
+ i.name as name,
2053
+ o.name as [table],
2054
+ i.is_unique as is_unique,
2055
+ CAST(SUM(s.used_page_count) * 8 / 1024.0 AS VARCHAR(50)) + ' MB' as size
2056
+ FROM sys.indexes i
2057
+ INNER JOIN sys.objects o ON i.object_id = o.object_id
2058
+ INNER JOIN sys.schemas sch ON o.schema_id = sch.schema_id
2059
+ LEFT JOIN sys.dm_db_partition_stats s ON i.object_id = s.object_id AND i.index_id = s.index_id
2060
+ WHERE sch.name = @schemaName
2061
+ AND i.name IS NOT NULL
2062
+ GROUP BY i.name, o.name, i.is_unique
2063
+ `;
2064
+ }
2065
+ const result = await request.query(query);
2066
+ const indexes = [];
2067
+ for (const row of result.recordset) {
2068
+ const colRequest = this.pool.request();
2069
+ colRequest.input("indexName", row.name);
2070
+ colRequest.input("schemaName", schemaName);
2071
+ const colResult = await colRequest.query(`
2072
+ SELECT c.name as column_name
2073
+ FROM sys.indexes i
2074
+ INNER JOIN sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id
2075
+ INNER JOIN sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id
2076
+ INNER JOIN sys.objects o ON i.object_id = o.object_id
2077
+ INNER JOIN sys.schemas s ON o.schema_id = s.schema_id
2078
+ WHERE i.name = @indexName
2079
+ AND s.name = @schemaName
2080
+ ORDER BY ic.key_ordinal
2081
+ `);
2082
+ indexes.push({
2083
+ name: row.name,
2084
+ table: row.table,
2085
+ columns: colResult.recordset.map((c) => c.column_name),
2086
+ unique: row.is_unique || false,
2087
+ size: row.size || "0 MB",
2088
+ definition: ""
2089
+ // MSSQL doesn't store definition like PG
2090
+ });
2091
+ }
2092
+ return indexes;
2093
+ } catch (error) {
2094
+ throw new MastraError(
2095
+ {
2096
+ id: "MASTRA_STORAGE_MSSQL_INDEX_LIST_FAILED",
2097
+ domain: ErrorDomain.STORAGE,
2098
+ category: ErrorCategory.THIRD_PARTY,
2099
+ details: tableName ? {
2100
+ tableName
2101
+ } : {}
2102
+ },
2103
+ error
2104
+ );
2105
+ }
2106
+ }
2107
+ /**
2108
+ * Get detailed statistics for a specific index
2109
+ */
2110
+ async describeIndex(indexName) {
2111
+ try {
2112
+ const schemaName = this.schemaName || "dbo";
2113
+ const request = this.pool.request();
2114
+ request.input("indexName", indexName);
2115
+ request.input("schemaName", schemaName);
2116
+ const query = `
2117
+ SELECT
2118
+ i.name as name,
2119
+ o.name as [table],
2120
+ i.is_unique as is_unique,
2121
+ CAST(SUM(s.used_page_count) * 8 / 1024.0 AS VARCHAR(50)) + ' MB' as size,
2122
+ i.type_desc as method,
2123
+ ISNULL(us.user_scans, 0) as scans,
2124
+ ISNULL(us.user_seeks + us.user_scans, 0) as tuples_read,
2125
+ ISNULL(us.user_lookups, 0) as tuples_fetched
2126
+ FROM sys.indexes i
2127
+ INNER JOIN sys.objects o ON i.object_id = o.object_id
2128
+ INNER JOIN sys.schemas sch ON o.schema_id = sch.schema_id
2129
+ LEFT JOIN sys.dm_db_partition_stats s ON i.object_id = s.object_id AND i.index_id = s.index_id
2130
+ LEFT JOIN sys.dm_db_index_usage_stats us ON i.object_id = us.object_id AND i.index_id = us.index_id
2131
+ WHERE i.name = @indexName
2132
+ AND sch.name = @schemaName
2133
+ GROUP BY i.name, o.name, i.is_unique, i.type_desc, us.user_seeks, us.user_scans, us.user_lookups
2134
+ `;
2135
+ const result = await request.query(query);
2136
+ if (!result.recordset || result.recordset.length === 0) {
2137
+ throw new Error(`Index "${indexName}" not found in schema "${schemaName}"`);
2138
+ }
2139
+ const row = result.recordset[0];
2140
+ const colRequest = this.pool.request();
2141
+ colRequest.input("indexName", indexName);
2142
+ colRequest.input("schemaName", schemaName);
2143
+ const colResult = await colRequest.query(`
2144
+ SELECT c.name as column_name
2145
+ FROM sys.indexes i
2146
+ INNER JOIN sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id
2147
+ INNER JOIN sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id
2148
+ INNER JOIN sys.objects o ON i.object_id = o.object_id
2149
+ INNER JOIN sys.schemas s ON o.schema_id = s.schema_id
2150
+ WHERE i.name = @indexName
2151
+ AND s.name = @schemaName
2152
+ ORDER BY ic.key_ordinal
2153
+ `);
2154
+ return {
2155
+ name: row.name,
2156
+ table: row.table,
2157
+ columns: colResult.recordset.map((c) => c.column_name),
2158
+ unique: row.is_unique || false,
2159
+ size: row.size || "0 MB",
2160
+ definition: "",
2161
+ method: row.method?.toLowerCase() || "nonclustered",
2162
+ scans: Number(row.scans) || 0,
2163
+ tuples_read: Number(row.tuples_read) || 0,
2164
+ tuples_fetched: Number(row.tuples_fetched) || 0
2165
+ };
2166
+ } catch (error) {
2167
+ throw new MastraError(
2168
+ {
2169
+ id: "MASTRA_STORAGE_MSSQL_INDEX_DESCRIBE_FAILED",
2170
+ domain: ErrorDomain.STORAGE,
2171
+ category: ErrorCategory.THIRD_PARTY,
2172
+ details: {
2173
+ indexName
2174
+ }
2175
+ },
2176
+ error
2177
+ );
2178
+ }
2179
+ }
2180
+ /**
2181
+ * Returns definitions for automatic performance indexes
2182
+ * IMPORTANT: Uses seq_id DESC instead of createdAt DESC for MSSQL due to millisecond accuracy limitations
2183
+ * NOTE: Using NVARCHAR(400) for text columns (800 bytes) leaves room for composite indexes
2184
+ */
2185
+ getAutomaticIndexDefinitions() {
2186
+ const schemaPrefix = this.schemaName ? `${this.schemaName}_` : "";
2187
+ return [
2188
+ // Composite indexes for optimal filtering + sorting performance
2189
+ // NVARCHAR(400) = 800 bytes, plus BIGINT (8 bytes) = 808 bytes total (under 900-byte limit)
2190
+ {
2191
+ name: `${schemaPrefix}mastra_threads_resourceid_seqid_idx`,
2192
+ table: TABLE_THREADS,
2193
+ columns: ["resourceId", "seq_id DESC"]
2194
+ },
2195
+ {
2196
+ name: `${schemaPrefix}mastra_messages_thread_id_seqid_idx`,
2197
+ table: TABLE_MESSAGES,
2198
+ columns: ["thread_id", "seq_id DESC"]
2199
+ },
2200
+ {
2201
+ name: `${schemaPrefix}mastra_traces_name_seqid_idx`,
2202
+ table: TABLE_TRACES,
2203
+ columns: ["name", "seq_id DESC"]
2204
+ },
2205
+ {
2206
+ name: `${schemaPrefix}mastra_scores_trace_id_span_id_seqid_idx`,
2207
+ table: TABLE_SCORERS,
2208
+ columns: ["traceId", "spanId", "seq_id DESC"]
2209
+ },
2210
+ // AI Spans indexes for optimal trace querying
2211
+ {
2212
+ name: `${schemaPrefix}mastra_ai_spans_traceid_startedat_idx`,
2213
+ table: TABLE_AI_SPANS,
2214
+ columns: ["traceId", "startedAt DESC"]
2215
+ },
2216
+ {
2217
+ name: `${schemaPrefix}mastra_ai_spans_parentspanid_startedat_idx`,
2218
+ table: TABLE_AI_SPANS,
2219
+ columns: ["parentSpanId", "startedAt DESC"]
2220
+ },
2221
+ {
2222
+ name: `${schemaPrefix}mastra_ai_spans_name_idx`,
2223
+ table: TABLE_AI_SPANS,
2224
+ columns: ["name"]
2225
+ },
2226
+ {
2227
+ name: `${schemaPrefix}mastra_ai_spans_spantype_startedat_idx`,
2228
+ table: TABLE_AI_SPANS,
2229
+ columns: ["spanType", "startedAt DESC"]
2230
+ }
2231
+ ];
2232
+ }
2233
+ /**
2234
+ * Creates automatic indexes for optimal query performance
2235
+ * Uses getAutomaticIndexDefinitions() to determine which indexes to create
2236
+ */
2237
+ async createAutomaticIndexes() {
2238
+ try {
2239
+ const indexes = this.getAutomaticIndexDefinitions();
2240
+ for (const indexOptions of indexes) {
2241
+ try {
2242
+ await this.createIndex(indexOptions);
2243
+ } catch (error) {
2244
+ this.logger?.warn?.(`Failed to create index ${indexOptions.name}:`, error);
2245
+ }
2246
+ }
2247
+ } catch (error) {
2248
+ throw new MastraError(
2249
+ {
2250
+ id: "MASTRA_STORAGE_MSSQL_STORE_CREATE_PERFORMANCE_INDEXES_FAILED",
2251
+ domain: ErrorDomain.STORAGE,
2252
+ category: ErrorCategory.THIRD_PARTY
2253
+ },
2254
+ error
2255
+ );
2256
+ }
2257
+ }
2258
+ };
1423
2259
  function transformScoreRow(row) {
1424
2260
  return {
1425
2261
  ...row,
1426
- input: parseJSON(row.input),
1427
- scorer: parseJSON(row.scorer),
1428
- preprocessStepResult: parseJSON(row.preprocessStepResult),
1429
- analyzeStepResult: parseJSON(row.analyzeStepResult),
1430
- metadata: parseJSON(row.metadata),
1431
- output: parseJSON(row.output),
1432
- additionalContext: parseJSON(row.additionalContext),
1433
- runtimeContext: parseJSON(row.runtimeContext),
1434
- entity: parseJSON(row.entity),
2262
+ input: safelyParseJSON(row.input),
2263
+ scorer: safelyParseJSON(row.scorer),
2264
+ preprocessStepResult: safelyParseJSON(row.preprocessStepResult),
2265
+ analyzeStepResult: safelyParseJSON(row.analyzeStepResult),
2266
+ metadata: safelyParseJSON(row.metadata),
2267
+ output: safelyParseJSON(row.output),
2268
+ additionalContext: safelyParseJSON(row.additionalContext),
2269
+ requestContext: safelyParseJSON(row.requestContext),
2270
+ entity: safelyParseJSON(row.entity),
1435
2271
  createdAt: row.createdAt,
1436
2272
  updatedAt: row.updatedAt
1437
2273
  };
@@ -1488,7 +2324,7 @@ var ScoresMSSQL = class extends ScoresStorage {
1488
2324
  );
1489
2325
  }
1490
2326
  try {
1491
- const scoreId = crypto.randomUUID();
2327
+ const scoreId = randomUUID();
1492
2328
  const {
1493
2329
  scorer,
1494
2330
  preprocessStepResult,
@@ -1497,7 +2333,7 @@ var ScoresMSSQL = class extends ScoresStorage {
1497
2333
  input,
1498
2334
  output,
1499
2335
  additionalContext,
1500
- runtimeContext,
2336
+ requestContext,
1501
2337
  entity,
1502
2338
  ...rest
1503
2339
  } = validatedScore;
@@ -1506,15 +2342,15 @@ var ScoresMSSQL = class extends ScoresStorage {
1506
2342
  record: {
1507
2343
  id: scoreId,
1508
2344
  ...rest,
1509
- input: JSON.stringify(input) || "",
1510
- output: JSON.stringify(output) || "",
1511
- preprocessStepResult: preprocessStepResult ? JSON.stringify(preprocessStepResult) : null,
1512
- analyzeStepResult: analyzeStepResult ? JSON.stringify(analyzeStepResult) : null,
1513
- metadata: metadata ? JSON.stringify(metadata) : null,
1514
- additionalContext: additionalContext ? JSON.stringify(additionalContext) : null,
1515
- runtimeContext: runtimeContext ? JSON.stringify(runtimeContext) : null,
1516
- entity: entity ? JSON.stringify(entity) : null,
1517
- scorer: scorer ? JSON.stringify(scorer) : null,
2345
+ input: input || "",
2346
+ output: output || "",
2347
+ preprocessStepResult: preprocessStepResult || null,
2348
+ analyzeStepResult: analyzeStepResult || null,
2349
+ metadata: metadata || null,
2350
+ additionalContext: additionalContext || null,
2351
+ requestContext: requestContext || null,
2352
+ entity: entity || null,
2353
+ scorer: scorer || null,
1518
2354
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1519
2355
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1520
2356
  }
@@ -1534,14 +2370,37 @@ var ScoresMSSQL = class extends ScoresStorage {
1534
2370
  }
1535
2371
  async getScoresByScorerId({
1536
2372
  scorerId,
1537
- pagination
2373
+ pagination,
2374
+ entityId,
2375
+ entityType,
2376
+ source
1538
2377
  }) {
1539
2378
  try {
1540
- const request = this.pool.request();
1541
- request.input("p1", scorerId);
1542
- const totalResult = await request.query(
1543
- `SELECT COUNT(*) as count FROM ${getTableName({ indexName: TABLE_SCORERS, schemaName: getSchemaName(this.schema) })} WHERE [scorerId] = @p1`
1544
- );
2379
+ const conditions = ["[scorerId] = @p1"];
2380
+ const params = { p1: scorerId };
2381
+ let paramIndex = 2;
2382
+ if (entityId) {
2383
+ conditions.push(`[entityId] = @p${paramIndex}`);
2384
+ params[`p${paramIndex}`] = entityId;
2385
+ paramIndex++;
2386
+ }
2387
+ if (entityType) {
2388
+ conditions.push(`[entityType] = @p${paramIndex}`);
2389
+ params[`p${paramIndex}`] = entityType;
2390
+ paramIndex++;
2391
+ }
2392
+ if (source) {
2393
+ conditions.push(`[source] = @p${paramIndex}`);
2394
+ params[`p${paramIndex}`] = source;
2395
+ paramIndex++;
2396
+ }
2397
+ const whereClause = conditions.join(" AND ");
2398
+ const tableName = getTableName({ indexName: TABLE_SCORERS, schemaName: getSchemaName(this.schema) });
2399
+ const countRequest = this.pool.request();
2400
+ Object.entries(params).forEach(([key, value]) => {
2401
+ countRequest.input(key, value);
2402
+ });
2403
+ const totalResult = await countRequest.query(`SELECT COUNT(*) as count FROM ${tableName} WHERE ${whereClause}`);
1545
2404
  const total = totalResult.recordset[0]?.count || 0;
1546
2405
  if (total === 0) {
1547
2406
  return {
@@ -1555,12 +2414,13 @@ var ScoresMSSQL = class extends ScoresStorage {
1555
2414
  };
1556
2415
  }
1557
2416
  const dataRequest = this.pool.request();
1558
- dataRequest.input("p1", scorerId);
1559
- dataRequest.input("p2", pagination.perPage);
1560
- dataRequest.input("p3", pagination.page * pagination.perPage);
1561
- const result = await dataRequest.query(
1562
- `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`
1563
- );
2417
+ Object.entries(params).forEach(([key, value]) => {
2418
+ dataRequest.input(key, value);
2419
+ });
2420
+ dataRequest.input("perPage", pagination.perPage);
2421
+ dataRequest.input("offset", pagination.page * pagination.perPage);
2422
+ const dataQuery = `SELECT * FROM ${tableName} WHERE ${whereClause} ORDER BY [createdAt] DESC OFFSET @offset ROWS FETCH NEXT @perPage ROWS ONLY`;
2423
+ const result = await dataRequest.query(dataQuery);
1564
2424
  return {
1565
2425
  pagination: {
1566
2426
  total: Number(total),
@@ -1740,24 +2600,6 @@ var ScoresMSSQL = class extends ScoresStorage {
1740
2600
  }
1741
2601
  }
1742
2602
  };
1743
- function parseWorkflowRun(row) {
1744
- let parsedSnapshot = row.snapshot;
1745
- if (typeof parsedSnapshot === "string") {
1746
- try {
1747
- parsedSnapshot = JSON.parse(row.snapshot);
1748
- } catch (e) {
1749
- console.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
1750
- }
1751
- }
1752
- return {
1753
- workflowName: row.workflow_name,
1754
- runId: row.run_id,
1755
- snapshot: parsedSnapshot,
1756
- createdAt: row.createdAt,
1757
- updatedAt: row.updatedAt,
1758
- resourceId: row.resourceId
1759
- };
1760
- }
1761
2603
  var WorkflowsMSSQL = class extends WorkflowsStorage {
1762
2604
  pool;
1763
2605
  operations;
@@ -1772,21 +2614,163 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
1772
2614
  this.operations = operations;
1773
2615
  this.schema = schema;
1774
2616
  }
1775
- updateWorkflowResults({
1776
- // workflowName,
1777
- // runId,
1778
- // stepId,
1779
- // result,
1780
- // runtimeContext,
2617
+ parseWorkflowRun(row) {
2618
+ let parsedSnapshot = row.snapshot;
2619
+ if (typeof parsedSnapshot === "string") {
2620
+ try {
2621
+ parsedSnapshot = JSON.parse(row.snapshot);
2622
+ } catch (e) {
2623
+ this.logger?.warn?.(`Failed to parse snapshot for workflow ${row.workflow_name}:`, e);
2624
+ }
2625
+ }
2626
+ return {
2627
+ workflowName: row.workflow_name,
2628
+ runId: row.run_id,
2629
+ snapshot: parsedSnapshot,
2630
+ createdAt: row.createdAt,
2631
+ updatedAt: row.updatedAt,
2632
+ resourceId: row.resourceId
2633
+ };
2634
+ }
2635
+ async updateWorkflowResults({
2636
+ workflowName,
2637
+ runId,
2638
+ stepId,
2639
+ result,
2640
+ requestContext
1781
2641
  }) {
1782
- throw new Error("Method not implemented.");
2642
+ const table = getTableName({ indexName: TABLE_WORKFLOW_SNAPSHOT, schemaName: getSchemaName(this.schema) });
2643
+ const transaction = this.pool.transaction();
2644
+ try {
2645
+ await transaction.begin();
2646
+ const selectRequest = new sql2.Request(transaction);
2647
+ selectRequest.input("workflow_name", workflowName);
2648
+ selectRequest.input("run_id", runId);
2649
+ const existingSnapshotResult = await selectRequest.query(
2650
+ `SELECT snapshot FROM ${table} WITH (UPDLOCK, HOLDLOCK) WHERE workflow_name = @workflow_name AND run_id = @run_id`
2651
+ );
2652
+ let snapshot;
2653
+ if (!existingSnapshotResult.recordset || existingSnapshotResult.recordset.length === 0) {
2654
+ snapshot = {
2655
+ context: {},
2656
+ activePaths: [],
2657
+ timestamp: Date.now(),
2658
+ suspendedPaths: {},
2659
+ resumeLabels: {},
2660
+ serializedStepGraph: [],
2661
+ value: {},
2662
+ waitingPaths: {},
2663
+ status: "pending",
2664
+ runId,
2665
+ requestContext: {}
2666
+ };
2667
+ } else {
2668
+ const existingSnapshot = existingSnapshotResult.recordset[0].snapshot;
2669
+ snapshot = typeof existingSnapshot === "string" ? JSON.parse(existingSnapshot) : existingSnapshot;
2670
+ }
2671
+ snapshot.context[stepId] = result;
2672
+ snapshot.requestContext = { ...snapshot.requestContext, ...requestContext };
2673
+ const upsertReq = new sql2.Request(transaction);
2674
+ upsertReq.input("workflow_name", workflowName);
2675
+ upsertReq.input("run_id", runId);
2676
+ upsertReq.input("snapshot", JSON.stringify(snapshot));
2677
+ upsertReq.input("createdAt", sql2.DateTime2, /* @__PURE__ */ new Date());
2678
+ upsertReq.input("updatedAt", sql2.DateTime2, /* @__PURE__ */ new Date());
2679
+ await upsertReq.query(
2680
+ `MERGE ${table} AS target
2681
+ USING (SELECT @workflow_name AS workflow_name, @run_id AS run_id) AS src
2682
+ ON target.workflow_name = src.workflow_name AND target.run_id = src.run_id
2683
+ WHEN MATCHED THEN UPDATE SET snapshot = @snapshot, [updatedAt] = @updatedAt
2684
+ WHEN NOT MATCHED THEN INSERT (workflow_name, run_id, snapshot, [createdAt], [updatedAt])
2685
+ VALUES (@workflow_name, @run_id, @snapshot, @createdAt, @updatedAt);`
2686
+ );
2687
+ await transaction.commit();
2688
+ return snapshot.context;
2689
+ } catch (error) {
2690
+ try {
2691
+ await transaction.rollback();
2692
+ } catch {
2693
+ }
2694
+ throw new MastraError(
2695
+ {
2696
+ id: "MASTRA_STORAGE_MSSQL_STORE_UPDATE_WORKFLOW_RESULTS_FAILED",
2697
+ domain: ErrorDomain.STORAGE,
2698
+ category: ErrorCategory.THIRD_PARTY,
2699
+ details: {
2700
+ workflowName,
2701
+ runId,
2702
+ stepId
2703
+ }
2704
+ },
2705
+ error
2706
+ );
2707
+ }
1783
2708
  }
1784
- updateWorkflowState({
1785
- // workflowName,
1786
- // runId,
1787
- // opts,
2709
+ async updateWorkflowState({
2710
+ workflowName,
2711
+ runId,
2712
+ opts
1788
2713
  }) {
1789
- throw new Error("Method not implemented.");
2714
+ const table = getTableName({ indexName: TABLE_WORKFLOW_SNAPSHOT, schemaName: getSchemaName(this.schema) });
2715
+ const transaction = this.pool.transaction();
2716
+ try {
2717
+ await transaction.begin();
2718
+ const selectRequest = new sql2.Request(transaction);
2719
+ selectRequest.input("workflow_name", workflowName);
2720
+ selectRequest.input("run_id", runId);
2721
+ const existingSnapshotResult = await selectRequest.query(
2722
+ `SELECT snapshot FROM ${table} WITH (UPDLOCK, HOLDLOCK) WHERE workflow_name = @workflow_name AND run_id = @run_id`
2723
+ );
2724
+ if (!existingSnapshotResult.recordset || existingSnapshotResult.recordset.length === 0) {
2725
+ await transaction.rollback();
2726
+ return void 0;
2727
+ }
2728
+ const existingSnapshot = existingSnapshotResult.recordset[0].snapshot;
2729
+ const snapshot = typeof existingSnapshot === "string" ? JSON.parse(existingSnapshot) : existingSnapshot;
2730
+ if (!snapshot || !snapshot?.context) {
2731
+ await transaction.rollback();
2732
+ throw new MastraError(
2733
+ {
2734
+ id: "MASTRA_STORAGE_MSSQL_STORE_UPDATE_WORKFLOW_STATE_SNAPSHOT_NOT_FOUND",
2735
+ domain: ErrorDomain.STORAGE,
2736
+ category: ErrorCategory.SYSTEM,
2737
+ details: {
2738
+ workflowName,
2739
+ runId
2740
+ }
2741
+ },
2742
+ new Error(`Snapshot not found for runId ${runId}`)
2743
+ );
2744
+ }
2745
+ const updatedSnapshot = { ...snapshot, ...opts };
2746
+ const updateRequest = new sql2.Request(transaction);
2747
+ updateRequest.input("snapshot", JSON.stringify(updatedSnapshot));
2748
+ updateRequest.input("workflow_name", workflowName);
2749
+ updateRequest.input("run_id", runId);
2750
+ updateRequest.input("updatedAt", sql2.DateTime2, /* @__PURE__ */ new Date());
2751
+ await updateRequest.query(
2752
+ `UPDATE ${table} SET snapshot = @snapshot, [updatedAt] = @updatedAt WHERE workflow_name = @workflow_name AND run_id = @run_id`
2753
+ );
2754
+ await transaction.commit();
2755
+ return updatedSnapshot;
2756
+ } catch (error) {
2757
+ try {
2758
+ await transaction.rollback();
2759
+ } catch {
2760
+ }
2761
+ throw new MastraError(
2762
+ {
2763
+ id: "MASTRA_STORAGE_MSSQL_STORE_UPDATE_WORKFLOW_STATE_FAILED",
2764
+ domain: ErrorDomain.STORAGE,
2765
+ category: ErrorCategory.THIRD_PARTY,
2766
+ details: {
2767
+ workflowName,
2768
+ runId
2769
+ }
2770
+ },
2771
+ error
2772
+ );
2773
+ }
1790
2774
  }
1791
2775
  async persistWorkflowSnapshot({
1792
2776
  workflowName,
@@ -1884,7 +2868,7 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
1884
2868
  if (!result.recordset || result.recordset.length === 0) {
1885
2869
  return null;
1886
2870
  }
1887
- return parseWorkflowRun(result.recordset[0]);
2871
+ return this.parseWorkflowRun(result.recordset[0]);
1888
2872
  } catch (error) {
1889
2873
  throw new MastraError(
1890
2874
  {
@@ -1921,7 +2905,7 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
1921
2905
  conditions.push(`[resourceId] = @resourceId`);
1922
2906
  paramMap["resourceId"] = resourceId;
1923
2907
  } else {
1924
- console.warn(`[${TABLE_WORKFLOW_SNAPSHOT}] resourceId column not found. Skipping resourceId filter.`);
2908
+ this.logger?.warn?.(`[${TABLE_WORKFLOW_SNAPSHOT}] resourceId column not found. Skipping resourceId filter.`);
1925
2909
  }
1926
2910
  }
1927
2911
  if (fromDate instanceof Date && !isNaN(fromDate.getTime())) {
@@ -1955,7 +2939,7 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
1955
2939
  request.input("offset", offset);
1956
2940
  }
1957
2941
  const result = await request.query(query);
1958
- const runs = (result.recordset || []).map((row) => parseWorkflowRun(row));
2942
+ const runs = (result.recordset || []).map((row) => this.parseWorkflowRun(row));
1959
2943
  return { runs, total: total || runs.length };
1960
2944
  } catch (error) {
1961
2945
  throw new MastraError(
@@ -1971,6 +2955,9 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
1971
2955
  );
1972
2956
  }
1973
2957
  }
2958
+ async listWorkflowRuns(args) {
2959
+ return this.getWorkflowRuns(args);
2960
+ }
1974
2961
  };
1975
2962
 
1976
2963
  // src/storage/index.ts
@@ -2003,17 +2990,17 @@ var MSSQLStore = class extends MastraStorage {
2003
2990
  port: config.port,
2004
2991
  options: config.options || { encrypt: true, trustServerCertificate: true }
2005
2992
  });
2006
- const legacyEvals = new LegacyEvalsMSSQL({ pool: this.pool, schema: this.schema });
2007
2993
  const operations = new StoreOperationsMSSQL({ pool: this.pool, schemaName: this.schema });
2008
2994
  const scores = new ScoresMSSQL({ pool: this.pool, operations, schema: this.schema });
2009
2995
  const workflows = new WorkflowsMSSQL({ pool: this.pool, operations, schema: this.schema });
2010
2996
  const memory = new MemoryMSSQL({ pool: this.pool, schema: this.schema, operations });
2997
+ const observability = new ObservabilityMSSQL({ pool: this.pool, operations, schema: this.schema });
2011
2998
  this.stores = {
2012
2999
  operations,
2013
3000
  scores,
2014
3001
  workflows,
2015
- legacyEvals,
2016
- memory
3002
+ memory,
3003
+ observability
2017
3004
  };
2018
3005
  } catch (e) {
2019
3006
  throw new MastraError(
@@ -2033,6 +3020,11 @@ var MSSQLStore = class extends MastraStorage {
2033
3020
  try {
2034
3021
  await this.isConnected;
2035
3022
  await super.init();
3023
+ try {
3024
+ await this.stores.operations.createAutomaticIndexes();
3025
+ } catch (indexError) {
3026
+ this.logger?.warn?.("Failed to create indexes:", indexError);
3027
+ }
2036
3028
  } catch (error) {
2037
3029
  this.isConnected = null;
2038
3030
  throw new MastraError(
@@ -2060,16 +3052,11 @@ var MSSQLStore = class extends MastraStorage {
2060
3052
  hasColumn: true,
2061
3053
  createTable: true,
2062
3054
  deleteMessages: true,
2063
- getScoresBySpan: true
3055
+ getScoresBySpan: true,
3056
+ aiTracing: true,
3057
+ indexManagement: true
2064
3058
  };
2065
3059
  }
2066
- /** @deprecated use getEvals instead */
2067
- async getEvalsByAgentName(agentName, type) {
2068
- return this.stores.legacyEvals.getEvalsByAgentName(agentName, type);
2069
- }
2070
- async getEvals(options = {}) {
2071
- return this.stores.legacyEvals.getEvals(options);
2072
- }
2073
3060
  async createTable({
2074
3061
  tableName,
2075
3062
  schema
@@ -2170,9 +3157,9 @@ var MSSQLStore = class extends MastraStorage {
2170
3157
  runId,
2171
3158
  stepId,
2172
3159
  result,
2173
- runtimeContext
3160
+ requestContext
2174
3161
  }) {
2175
- return this.stores.workflows.updateWorkflowResults({ workflowName, runId, stepId, result, runtimeContext });
3162
+ return this.stores.workflows.updateWorkflowResults({ workflowName, runId, stepId, result, requestContext });
2176
3163
  }
2177
3164
  async updateWorkflowState({
2178
3165
  workflowName,
@@ -2214,6 +3201,60 @@ var MSSQLStore = class extends MastraStorage {
2214
3201
  async close() {
2215
3202
  await this.pool.close();
2216
3203
  }
3204
+ /**
3205
+ * Index Management
3206
+ */
3207
+ async createIndex(options) {
3208
+ return this.stores.operations.createIndex(options);
3209
+ }
3210
+ async listIndexes(tableName) {
3211
+ return this.stores.operations.listIndexes(tableName);
3212
+ }
3213
+ async describeIndex(indexName) {
3214
+ return this.stores.operations.describeIndex(indexName);
3215
+ }
3216
+ async dropIndex(indexName) {
3217
+ return this.stores.operations.dropIndex(indexName);
3218
+ }
3219
+ /**
3220
+ * AI Tracing / Observability
3221
+ */
3222
+ getObservabilityStore() {
3223
+ if (!this.stores.observability) {
3224
+ throw new MastraError({
3225
+ id: "MSSQL_STORE_OBSERVABILITY_NOT_INITIALIZED",
3226
+ domain: ErrorDomain.STORAGE,
3227
+ category: ErrorCategory.SYSTEM,
3228
+ text: "Observability storage is not initialized"
3229
+ });
3230
+ }
3231
+ return this.stores.observability;
3232
+ }
3233
+ async createAISpan(span) {
3234
+ return this.getObservabilityStore().createAISpan(span);
3235
+ }
3236
+ async updateAISpan({
3237
+ spanId,
3238
+ traceId,
3239
+ updates
3240
+ }) {
3241
+ return this.getObservabilityStore().updateAISpan({ spanId, traceId, updates });
3242
+ }
3243
+ async getAITrace(traceId) {
3244
+ return this.getObservabilityStore().getAITrace(traceId);
3245
+ }
3246
+ async getAITracesPaginated(args) {
3247
+ return this.getObservabilityStore().getAITracesPaginated(args);
3248
+ }
3249
+ async batchCreateAISpans(args) {
3250
+ return this.getObservabilityStore().batchCreateAISpans(args);
3251
+ }
3252
+ async batchUpdateAISpans(args) {
3253
+ return this.getObservabilityStore().batchUpdateAISpans(args);
3254
+ }
3255
+ async batchDeleteAITraces(args) {
3256
+ return this.getObservabilityStore().batchDeleteAITraces(args);
3257
+ }
2217
3258
  /**
2218
3259
  * Scorers
2219
3260
  */
@@ -2222,9 +3263,18 @@ var MSSQLStore = class extends MastraStorage {
2222
3263
  }
2223
3264
  async getScoresByScorerId({
2224
3265
  scorerId: _scorerId,
2225
- pagination: _pagination
3266
+ pagination: _pagination,
3267
+ entityId: _entityId,
3268
+ entityType: _entityType,
3269
+ source: _source
2226
3270
  }) {
2227
- return this.stores.scores.getScoresByScorerId({ scorerId: _scorerId, pagination: _pagination });
3271
+ return this.stores.scores.getScoresByScorerId({
3272
+ scorerId: _scorerId,
3273
+ pagination: _pagination,
3274
+ entityId: _entityId,
3275
+ entityType: _entityType,
3276
+ source: _source
3277
+ });
2228
3278
  }
2229
3279
  async saveScore(_score) {
2230
3280
  return this.stores.scores.saveScore(_score);