@mastra/mssql 0.4.6 → 0.5.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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, TracesStorage, TABLE_TRACES, WorkflowsStorage, MemoryStorage, resolveMessageLimit, TABLE_RESOURCES, TABLE_EVALS, TABLE_THREADS, TABLE_MESSAGES } from '@mastra/core/storage';
3
- import sql2 from 'mssql';
2
+ import { MastraStorage, LegacyEvalsStorage, StoreOperations, TABLE_WORKFLOW_SNAPSHOT, TABLE_SCHEMAS, TABLE_THREADS, TABLE_MESSAGES, TABLE_TRACES, TABLE_EVALS, TABLE_SCORERS, TABLE_AI_SPANS, ScoresStorage, TracesStorage, WorkflowsStorage, MemoryStorage, resolveMessageLimit, TABLE_RESOURCES, ObservabilityStorage, safelyParseJSON } from '@mastra/core/storage';
3
+ import sql3 from 'mssql';
4
4
  import { parseSqlIdentifier, parseFieldKey } from '@mastra/core/utils';
5
5
  import { MessageList } from '@mastra/core/agent';
6
+ import { randomUUID } from 'crypto';
6
7
  import { saveScorePayloadSchema } from '@mastra/core/scores';
7
8
 
8
9
  // src/storage/index.ts
@@ -15,6 +16,69 @@ function getTableName({ indexName, schemaName }) {
15
16
  const quotedSchemaName = schemaName;
16
17
  return quotedSchemaName ? `${quotedSchemaName}.${quotedIndexName}` : quotedIndexName;
17
18
  }
19
+ function buildDateRangeFilter(dateRange, fieldName) {
20
+ const filters = {};
21
+ if (dateRange?.start) {
22
+ filters[`${fieldName}_gte`] = dateRange.start;
23
+ }
24
+ if (dateRange?.end) {
25
+ filters[`${fieldName}_lte`] = dateRange.end;
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
+ });
51
+ return {
52
+ sql: conditions.length > 0 ? ` WHERE ${conditions.join(" AND ")}` : "",
53
+ params
54
+ };
55
+ }
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;
69
+ }
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;
78
+ }
79
+ });
80
+ return result;
81
+ }
18
82
 
19
83
  // src/storage/domains/legacy-evals/index.ts
20
84
  function transformEvalRow(row) {
@@ -25,7 +89,7 @@ function transformEvalRow(row) {
25
89
  } catch {
26
90
  }
27
91
  }
28
- if (row.test_info) {
92
+ if (row.result) {
29
93
  try {
30
94
  resultValue = typeof row.result === "string" ? JSON.parse(row.result) : row.result;
31
95
  } catch {
@@ -71,7 +135,7 @@ var LegacyEvalsMSSQL = class extends LegacyEvalsStorage {
71
135
  if (error && error.number === 208 && error.message && error.message.includes("Invalid object name")) {
72
136
  return [];
73
137
  }
74
- console.error("Failed to get evals for the specified agent: " + error?.message);
138
+ this.logger?.error?.("Failed to get evals for the specified agent:", error);
75
139
  throw error;
76
140
  }
77
141
  }
@@ -107,7 +171,7 @@ var LegacyEvalsMSSQL = class extends LegacyEvalsStorage {
107
171
  const countReq = this.pool.request();
108
172
  Object.entries(params).forEach(([key, value]) => {
109
173
  if (value instanceof Date) {
110
- countReq.input(key, sql2.DateTime, value);
174
+ countReq.input(key, sql3.DateTime, value);
111
175
  } else {
112
176
  countReq.input(key, value);
113
177
  }
@@ -126,7 +190,7 @@ var LegacyEvalsMSSQL = class extends LegacyEvalsStorage {
126
190
  const req = this.pool.request();
127
191
  Object.entries(params).forEach(([key, value]) => {
128
192
  if (value instanceof Date) {
129
- req.input(key, sql2.DateTime, value);
193
+ req.input(key, sql3.DateTime, value);
130
194
  } else {
131
195
  req.input(key, value);
132
196
  }
@@ -158,7 +222,7 @@ var LegacyEvalsMSSQL = class extends LegacyEvalsStorage {
158
222
  error
159
223
  );
160
224
  this.logger?.error?.(mastraError.toString());
161
- this.logger?.trackException(mastraError);
225
+ this.logger?.trackException?.(mastraError);
162
226
  throw mastraError;
163
227
  }
164
228
  }
@@ -251,7 +315,8 @@ var MemoryMSSQL = class extends MemoryStorage {
251
315
  };
252
316
  }
253
317
  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`;
318
+ const dir = (sortDirection || "DESC").toUpperCase() === "ASC" ? "ASC" : "DESC";
319
+ const dataQuery = `SELECT id, [resourceId], title, metadata, [createdAt], [updatedAt] ${baseQuery} ORDER BY ${orderByField} ${dir} OFFSET @offset ROWS FETCH NEXT @perPage ROWS ONLY`;
255
320
  const dataRequest = this.pool.request();
256
321
  dataRequest.input("resourceId", resourceId);
257
322
  dataRequest.input("perPage", perPage);
@@ -308,9 +373,14 @@ var MemoryMSSQL = class extends MemoryStorage {
308
373
  req.input("id", thread.id);
309
374
  req.input("resourceId", thread.resourceId);
310
375
  req.input("title", thread.title);
311
- req.input("metadata", thread.metadata ? JSON.stringify(thread.metadata) : null);
312
- req.input("createdAt", sql2.DateTime2, thread.createdAt);
313
- req.input("updatedAt", sql2.DateTime2, thread.updatedAt);
376
+ const metadata = thread.metadata ? JSON.stringify(thread.metadata) : null;
377
+ if (metadata === null) {
378
+ req.input("metadata", sql3.NVarChar, null);
379
+ } else {
380
+ req.input("metadata", metadata);
381
+ }
382
+ req.input("createdAt", sql3.DateTime2, thread.createdAt);
383
+ req.input("updatedAt", sql3.DateTime2, thread.updatedAt);
314
384
  await req.query(mergeSql);
315
385
  return thread;
316
386
  } catch (error) {
@@ -335,7 +405,8 @@ var MemoryMSSQL = class extends MemoryStorage {
335
405
  try {
336
406
  const baseQuery = `FROM ${getTableName({ indexName: TABLE_THREADS, schemaName: getSchemaName(this.schema) })} WHERE [resourceId] = @resourceId`;
337
407
  const orderByField = orderBy === "createdAt" ? "[createdAt]" : "[updatedAt]";
338
- const dataQuery = `SELECT id, [resourceId], title, metadata, [createdAt], [updatedAt] ${baseQuery} ORDER BY ${orderByField} ${sortDirection}`;
408
+ const dir = (sortDirection || "DESC").toUpperCase() === "ASC" ? "ASC" : "DESC";
409
+ const dataQuery = `SELECT id, [resourceId], title, metadata, [createdAt], [updatedAt] ${baseQuery} ORDER BY ${orderByField} ${dir}`;
339
410
  const request = this.pool.request();
340
411
  request.input("resourceId", resourceId);
341
412
  const resultSet = await request.query(dataQuery);
@@ -584,7 +655,7 @@ var MemoryMSSQL = class extends MemoryStorage {
584
655
  error
585
656
  );
586
657
  this.logger?.error?.(mastraError.toString());
587
- this.logger?.trackException(mastraError);
658
+ this.logger?.trackException?.(mastraError);
588
659
  return [];
589
660
  }
590
661
  }
@@ -624,7 +695,7 @@ var MemoryMSSQL = class extends MemoryStorage {
624
695
  error
625
696
  );
626
697
  this.logger?.error?.(mastraError.toString());
627
- this.logger?.trackException(mastraError);
698
+ this.logger?.trackException?.(mastraError);
628
699
  return [];
629
700
  }
630
701
  }
@@ -686,7 +757,7 @@ var MemoryMSSQL = class extends MemoryStorage {
686
757
  const parsed = this._parseAndFormatMessages(messages, format);
687
758
  return {
688
759
  messages: parsed,
689
- total: total + excludeIds.length,
760
+ total,
690
761
  page,
691
762
  perPage,
692
763
  hasMore: currentOffset + rows.length < total
@@ -706,7 +777,7 @@ var MemoryMSSQL = class extends MemoryStorage {
706
777
  error
707
778
  );
708
779
  this.logger?.error?.(mastraError.toString());
709
- this.logger?.trackException(mastraError);
780
+ this.logger?.trackException?.(mastraError);
710
781
  return { messages: [], total: 0, page, perPage: perPageInput || 40, hasMore: false };
711
782
  }
712
783
  }
@@ -758,7 +829,7 @@ var MemoryMSSQL = class extends MemoryStorage {
758
829
  "content",
759
830
  typeof message.content === "string" ? message.content : JSON.stringify(message.content)
760
831
  );
761
- request.input("createdAt", sql2.DateTime2, message.createdAt);
832
+ request.input("createdAt", sql3.DateTime2, message.createdAt);
762
833
  request.input("role", message.role);
763
834
  request.input("type", message.type || "v2");
764
835
  request.input("resourceId", message.resourceId);
@@ -777,7 +848,7 @@ var MemoryMSSQL = class extends MemoryStorage {
777
848
  await request.query(mergeSql);
778
849
  }
779
850
  const threadReq = transaction.request();
780
- threadReq.input("updatedAt", sql2.DateTime2, /* @__PURE__ */ new Date());
851
+ threadReq.input("updatedAt", sql3.DateTime2, /* @__PURE__ */ new Date());
781
852
  threadReq.input("id", threadId);
782
853
  await threadReq.query(`UPDATE ${tableThreads} SET [updatedAt] = @updatedAt WHERE id = @id`);
783
854
  await transaction.commit();
@@ -973,8 +1044,10 @@ var MemoryMSSQL = class extends MemoryStorage {
973
1044
  return null;
974
1045
  }
975
1046
  return {
976
- ...result,
977
- workingMemory: typeof result.workingMemory === "object" ? JSON.stringify(result.workingMemory) : result.workingMemory,
1047
+ id: result.id,
1048
+ createdAt: result.createdAt,
1049
+ updatedAt: result.updatedAt,
1050
+ workingMemory: result.workingMemory,
978
1051
  metadata: typeof result.metadata === "string" ? JSON.parse(result.metadata) : result.metadata
979
1052
  };
980
1053
  } catch (error) {
@@ -988,7 +1061,7 @@ var MemoryMSSQL = class extends MemoryStorage {
988
1061
  error
989
1062
  );
990
1063
  this.logger?.error?.(mastraError.toString());
991
- this.logger?.trackException(mastraError);
1064
+ this.logger?.trackException?.(mastraError);
992
1065
  throw mastraError;
993
1066
  }
994
1067
  }
@@ -997,7 +1070,7 @@ var MemoryMSSQL = class extends MemoryStorage {
997
1070
  tableName: TABLE_RESOURCES,
998
1071
  record: {
999
1072
  ...resource,
1000
- metadata: JSON.stringify(resource.metadata)
1073
+ metadata: resource.metadata
1001
1074
  }
1002
1075
  });
1003
1076
  return resource;
@@ -1055,20 +1128,337 @@ var MemoryMSSQL = class extends MemoryStorage {
1055
1128
  error
1056
1129
  );
1057
1130
  this.logger?.error?.(mastraError.toString());
1058
- this.logger?.trackException(mastraError);
1131
+ this.logger?.trackException?.(mastraError);
1059
1132
  throw mastraError;
1060
1133
  }
1061
1134
  }
1062
1135
  };
1136
+ var ObservabilityMSSQL = class extends ObservabilityStorage {
1137
+ pool;
1138
+ operations;
1139
+ schema;
1140
+ constructor({
1141
+ pool,
1142
+ operations,
1143
+ schema
1144
+ }) {
1145
+ super();
1146
+ this.pool = pool;
1147
+ this.operations = operations;
1148
+ this.schema = schema;
1149
+ }
1150
+ get aiTracingStrategy() {
1151
+ return {
1152
+ preferred: "batch-with-updates",
1153
+ supported: ["batch-with-updates", "insert-only"]
1154
+ };
1155
+ }
1156
+ async createAISpan(span) {
1157
+ try {
1158
+ const startedAt = span.startedAt instanceof Date ? span.startedAt.toISOString() : span.startedAt;
1159
+ const endedAt = span.endedAt instanceof Date ? span.endedAt.toISOString() : span.endedAt;
1160
+ const record = {
1161
+ ...span,
1162
+ startedAt,
1163
+ endedAt
1164
+ // Note: createdAt/updatedAt will be set by default values
1165
+ };
1166
+ return this.operations.insert({ tableName: TABLE_AI_SPANS, record });
1167
+ } catch (error) {
1168
+ throw new MastraError(
1169
+ {
1170
+ id: "MSSQL_STORE_CREATE_AI_SPAN_FAILED",
1171
+ domain: ErrorDomain.STORAGE,
1172
+ category: ErrorCategory.USER,
1173
+ details: {
1174
+ spanId: span.spanId,
1175
+ traceId: span.traceId,
1176
+ spanType: span.spanType,
1177
+ spanName: span.name
1178
+ }
1179
+ },
1180
+ error
1181
+ );
1182
+ }
1183
+ }
1184
+ async getAITrace(traceId) {
1185
+ try {
1186
+ const tableName = getTableName({
1187
+ indexName: TABLE_AI_SPANS,
1188
+ schemaName: getSchemaName(this.schema)
1189
+ });
1190
+ const request = this.pool.request();
1191
+ request.input("traceId", traceId);
1192
+ const result = await request.query(
1193
+ `SELECT
1194
+ [traceId], [spanId], [parentSpanId], [name], [scope], [spanType],
1195
+ [attributes], [metadata], [links], [input], [output], [error], [isEvent],
1196
+ [startedAt], [endedAt], [createdAt], [updatedAt]
1197
+ FROM ${tableName}
1198
+ WHERE [traceId] = @traceId
1199
+ ORDER BY [startedAt] DESC`
1200
+ );
1201
+ if (!result.recordset || result.recordset.length === 0) {
1202
+ return null;
1203
+ }
1204
+ return {
1205
+ traceId,
1206
+ spans: result.recordset.map(
1207
+ (span) => transformFromSqlRow({
1208
+ tableName: TABLE_AI_SPANS,
1209
+ sqlRow: span
1210
+ })
1211
+ )
1212
+ };
1213
+ } catch (error) {
1214
+ throw new MastraError(
1215
+ {
1216
+ id: "MSSQL_STORE_GET_AI_TRACE_FAILED",
1217
+ domain: ErrorDomain.STORAGE,
1218
+ category: ErrorCategory.USER,
1219
+ details: {
1220
+ traceId
1221
+ }
1222
+ },
1223
+ error
1224
+ );
1225
+ }
1226
+ }
1227
+ async updateAISpan({
1228
+ spanId,
1229
+ traceId,
1230
+ updates
1231
+ }) {
1232
+ try {
1233
+ const data = { ...updates };
1234
+ if (data.endedAt instanceof Date) {
1235
+ data.endedAt = data.endedAt.toISOString();
1236
+ }
1237
+ if (data.startedAt instanceof Date) {
1238
+ data.startedAt = data.startedAt.toISOString();
1239
+ }
1240
+ await this.operations.update({
1241
+ tableName: TABLE_AI_SPANS,
1242
+ keys: { spanId, traceId },
1243
+ data
1244
+ });
1245
+ } catch (error) {
1246
+ throw new MastraError(
1247
+ {
1248
+ id: "MSSQL_STORE_UPDATE_AI_SPAN_FAILED",
1249
+ domain: ErrorDomain.STORAGE,
1250
+ category: ErrorCategory.USER,
1251
+ details: {
1252
+ spanId,
1253
+ traceId
1254
+ }
1255
+ },
1256
+ error
1257
+ );
1258
+ }
1259
+ }
1260
+ async getAITracesPaginated({
1261
+ filters,
1262
+ pagination
1263
+ }) {
1264
+ const page = pagination?.page ?? 0;
1265
+ const perPage = pagination?.perPage ?? 10;
1266
+ const { entityId, entityType, ...actualFilters } = filters || {};
1267
+ const filtersWithDateRange = {
1268
+ ...actualFilters,
1269
+ ...buildDateRangeFilter(pagination?.dateRange, "startedAt"),
1270
+ parentSpanId: null
1271
+ // Only get root spans for traces
1272
+ };
1273
+ const whereClause = prepareWhereClause(filtersWithDateRange);
1274
+ let actualWhereClause = whereClause.sql;
1275
+ const params = { ...whereClause.params };
1276
+ let currentParamIndex = Object.keys(params).length + 1;
1277
+ if (entityId && entityType) {
1278
+ let name = "";
1279
+ if (entityType === "workflow") {
1280
+ name = `workflow run: '${entityId}'`;
1281
+ } else if (entityType === "agent") {
1282
+ name = `agent run: '${entityId}'`;
1283
+ } else {
1284
+ const error = new MastraError({
1285
+ id: "MSSQL_STORE_GET_AI_TRACES_PAGINATED_FAILED",
1286
+ domain: ErrorDomain.STORAGE,
1287
+ category: ErrorCategory.USER,
1288
+ details: {
1289
+ entityType
1290
+ },
1291
+ text: `Cannot filter by entity type: ${entityType}`
1292
+ });
1293
+ throw error;
1294
+ }
1295
+ const entityParam = `p${currentParamIndex++}`;
1296
+ if (actualWhereClause) {
1297
+ actualWhereClause += ` AND [name] = @${entityParam}`;
1298
+ } else {
1299
+ actualWhereClause = ` WHERE [name] = @${entityParam}`;
1300
+ }
1301
+ params[entityParam] = name;
1302
+ }
1303
+ const tableName = getTableName({
1304
+ indexName: TABLE_AI_SPANS,
1305
+ schemaName: getSchemaName(this.schema)
1306
+ });
1307
+ try {
1308
+ const countRequest = this.pool.request();
1309
+ Object.entries(params).forEach(([key, value]) => {
1310
+ countRequest.input(key, value);
1311
+ });
1312
+ const countResult = await countRequest.query(
1313
+ `SELECT COUNT(*) as count FROM ${tableName}${actualWhereClause}`
1314
+ );
1315
+ const total = countResult.recordset[0]?.count ?? 0;
1316
+ if (total === 0) {
1317
+ return {
1318
+ pagination: {
1319
+ total: 0,
1320
+ page,
1321
+ perPage,
1322
+ hasMore: false
1323
+ },
1324
+ spans: []
1325
+ };
1326
+ }
1327
+ const dataRequest = this.pool.request();
1328
+ Object.entries(params).forEach(([key, value]) => {
1329
+ dataRequest.input(key, value);
1330
+ });
1331
+ dataRequest.input("offset", page * perPage);
1332
+ dataRequest.input("limit", perPage);
1333
+ const dataResult = await dataRequest.query(
1334
+ `SELECT * FROM ${tableName}${actualWhereClause} ORDER BY [startedAt] DESC OFFSET @offset ROWS FETCH NEXT @limit ROWS ONLY`
1335
+ );
1336
+ const spans = dataResult.recordset.map(
1337
+ (row) => transformFromSqlRow({
1338
+ tableName: TABLE_AI_SPANS,
1339
+ sqlRow: row
1340
+ })
1341
+ );
1342
+ return {
1343
+ pagination: {
1344
+ total,
1345
+ page,
1346
+ perPage,
1347
+ hasMore: (page + 1) * perPage < total
1348
+ },
1349
+ spans
1350
+ };
1351
+ } catch (error) {
1352
+ throw new MastraError(
1353
+ {
1354
+ id: "MSSQL_STORE_GET_AI_TRACES_PAGINATED_FAILED",
1355
+ domain: ErrorDomain.STORAGE,
1356
+ category: ErrorCategory.USER
1357
+ },
1358
+ error
1359
+ );
1360
+ }
1361
+ }
1362
+ async batchCreateAISpans(args) {
1363
+ if (!args.records || args.records.length === 0) {
1364
+ return;
1365
+ }
1366
+ try {
1367
+ await this.operations.batchInsert({
1368
+ tableName: TABLE_AI_SPANS,
1369
+ records: args.records.map((span) => ({
1370
+ ...span,
1371
+ startedAt: span.startedAt instanceof Date ? span.startedAt.toISOString() : span.startedAt,
1372
+ endedAt: span.endedAt instanceof Date ? span.endedAt.toISOString() : span.endedAt
1373
+ }))
1374
+ });
1375
+ } catch (error) {
1376
+ throw new MastraError(
1377
+ {
1378
+ id: "MSSQL_STORE_BATCH_CREATE_AI_SPANS_FAILED",
1379
+ domain: ErrorDomain.STORAGE,
1380
+ category: ErrorCategory.USER,
1381
+ details: {
1382
+ count: args.records.length
1383
+ }
1384
+ },
1385
+ error
1386
+ );
1387
+ }
1388
+ }
1389
+ async batchUpdateAISpans(args) {
1390
+ if (!args.records || args.records.length === 0) {
1391
+ return;
1392
+ }
1393
+ try {
1394
+ const updates = args.records.map(({ traceId, spanId, updates: data }) => {
1395
+ const processedData = { ...data };
1396
+ if (processedData.endedAt instanceof Date) {
1397
+ processedData.endedAt = processedData.endedAt.toISOString();
1398
+ }
1399
+ if (processedData.startedAt instanceof Date) {
1400
+ processedData.startedAt = processedData.startedAt.toISOString();
1401
+ }
1402
+ return {
1403
+ keys: { spanId, traceId },
1404
+ data: processedData
1405
+ };
1406
+ });
1407
+ await this.operations.batchUpdate({
1408
+ tableName: TABLE_AI_SPANS,
1409
+ updates
1410
+ });
1411
+ } catch (error) {
1412
+ throw new MastraError(
1413
+ {
1414
+ id: "MSSQL_STORE_BATCH_UPDATE_AI_SPANS_FAILED",
1415
+ domain: ErrorDomain.STORAGE,
1416
+ category: ErrorCategory.USER,
1417
+ details: {
1418
+ count: args.records.length
1419
+ }
1420
+ },
1421
+ error
1422
+ );
1423
+ }
1424
+ }
1425
+ async batchDeleteAITraces(args) {
1426
+ if (!args.traceIds || args.traceIds.length === 0) {
1427
+ return;
1428
+ }
1429
+ try {
1430
+ const keys = args.traceIds.map((traceId) => ({ traceId }));
1431
+ await this.operations.batchDelete({
1432
+ tableName: TABLE_AI_SPANS,
1433
+ keys
1434
+ });
1435
+ } catch (error) {
1436
+ throw new MastraError(
1437
+ {
1438
+ id: "MSSQL_STORE_BATCH_DELETE_AI_TRACES_FAILED",
1439
+ domain: ErrorDomain.STORAGE,
1440
+ category: ErrorCategory.USER,
1441
+ details: {
1442
+ count: args.traceIds.length
1443
+ }
1444
+ },
1445
+ error
1446
+ );
1447
+ }
1448
+ }
1449
+ };
1063
1450
  var StoreOperationsMSSQL = class extends StoreOperations {
1064
1451
  pool;
1065
1452
  schemaName;
1066
1453
  setupSchemaPromise = null;
1067
1454
  schemaSetupComplete = void 0;
1068
- getSqlType(type, isPrimaryKey = false) {
1455
+ getSqlType(type, isPrimaryKey = false, useLargeStorage = false) {
1069
1456
  switch (type) {
1070
1457
  case "text":
1071
- return isPrimaryKey ? "NVARCHAR(255)" : "NVARCHAR(MAX)";
1458
+ if (useLargeStorage) {
1459
+ return "NVARCHAR(MAX)";
1460
+ }
1461
+ return isPrimaryKey ? "NVARCHAR(255)" : "NVARCHAR(400)";
1072
1462
  case "timestamp":
1073
1463
  return "DATETIME2(7)";
1074
1464
  case "uuid":
@@ -1081,6 +1471,8 @@ var StoreOperationsMSSQL = class extends StoreOperations {
1081
1471
  return "BIGINT";
1082
1472
  case "float":
1083
1473
  return "FLOAT";
1474
+ case "boolean":
1475
+ return "BIT";
1084
1476
  default:
1085
1477
  throw new MastraError({
1086
1478
  id: "MASTRA_STORAGE_MSSQL_STORE_TYPE_NOT_SUPPORTED",
@@ -1143,20 +1535,26 @@ var StoreOperationsMSSQL = class extends StoreOperations {
1143
1535
  }
1144
1536
  await this.setupSchemaPromise;
1145
1537
  }
1146
- async insert({ tableName, record }) {
1538
+ async insert({
1539
+ tableName,
1540
+ record,
1541
+ transaction
1542
+ }) {
1147
1543
  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));
1544
+ const columns = Object.keys(record);
1545
+ const parsedColumns = columns.map((col) => parseSqlIdentifier(col, "column name"));
1546
+ const paramNames = columns.map((_, i) => `@param${i}`);
1547
+ const insertSql = `INSERT INTO ${getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) })} (${parsedColumns.map((c) => `[${c}]`).join(", ")}) VALUES (${paramNames.join(", ")})`;
1548
+ const request = transaction ? transaction.request() : this.pool.request();
1549
+ columns.forEach((col, i) => {
1550
+ const value = record[col];
1551
+ const preparedValue = this.prepareValue(value, col, tableName);
1552
+ if (preparedValue instanceof Date) {
1553
+ request.input(`param${i}`, sql3.DateTime2, preparedValue);
1554
+ } else if (preparedValue === null || preparedValue === void 0) {
1555
+ request.input(`param${i}`, this.getMssqlType(tableName, col), null);
1158
1556
  } else {
1159
- request.input(`param${i}`, value);
1557
+ request.input(`param${i}`, preparedValue);
1160
1558
  }
1161
1559
  });
1162
1560
  await request.query(insertSql);
@@ -1180,7 +1578,7 @@ var StoreOperationsMSSQL = class extends StoreOperations {
1180
1578
  try {
1181
1579
  await this.pool.request().query(`TRUNCATE TABLE ${fullTableName}`);
1182
1580
  } catch (truncateError) {
1183
- if (truncateError.message && truncateError.message.includes("foreign key")) {
1581
+ if (truncateError?.number === 4712) {
1184
1582
  await this.pool.request().query(`DELETE FROM ${fullTableName}`);
1185
1583
  } else {
1186
1584
  throw truncateError;
@@ -1203,9 +1601,11 @@ var StoreOperationsMSSQL = class extends StoreOperations {
1203
1601
  getDefaultValue(type) {
1204
1602
  switch (type) {
1205
1603
  case "timestamp":
1206
- return "DEFAULT SYSDATETIMEOFFSET()";
1604
+ return "DEFAULT SYSUTCDATETIME()";
1207
1605
  case "jsonb":
1208
1606
  return "DEFAULT N'{}'";
1607
+ case "boolean":
1608
+ return "DEFAULT 0";
1209
1609
  default:
1210
1610
  return super.getDefaultValue(type);
1211
1611
  }
@@ -1216,13 +1616,29 @@ var StoreOperationsMSSQL = class extends StoreOperations {
1216
1616
  }) {
1217
1617
  try {
1218
1618
  const uniqueConstraintColumns = tableName === TABLE_WORKFLOW_SNAPSHOT ? ["workflow_name", "run_id"] : [];
1619
+ const largeDataColumns = [
1620
+ "workingMemory",
1621
+ "snapshot",
1622
+ "metadata",
1623
+ "content",
1624
+ // messages.content - can be very long conversation content
1625
+ "input",
1626
+ // evals.input - test input data
1627
+ "output",
1628
+ // evals.output - test output data
1629
+ "instructions",
1630
+ // evals.instructions - evaluation instructions
1631
+ "other"
1632
+ // traces.other - additional trace data
1633
+ ];
1219
1634
  const columns = Object.entries(schema).map(([name, def]) => {
1220
1635
  const parsedName = parseSqlIdentifier(name, "column name");
1221
1636
  const constraints = [];
1222
1637
  if (def.primaryKey) constraints.push("PRIMARY KEY");
1223
1638
  if (!def.nullable) constraints.push("NOT NULL");
1224
1639
  const isIndexed = !!def.primaryKey || uniqueConstraintColumns.includes(name);
1225
- return `[${parsedName}] ${this.getSqlType(def.type, isIndexed)} ${constraints.join(" ")}`.trim();
1640
+ const useLargeStorage = largeDataColumns.includes(name);
1641
+ return `[${parsedName}] ${this.getSqlType(def.type, isIndexed, useLargeStorage)} ${constraints.join(" ")}`.trim();
1226
1642
  }).join(",\n");
1227
1643
  if (this.schemaName) {
1228
1644
  await this.setupSchema();
@@ -1309,7 +1725,19 @@ ${columns}
1309
1725
  const columnExists = Array.isArray(checkResult.recordset) && checkResult.recordset.length > 0;
1310
1726
  if (!columnExists) {
1311
1727
  const columnDef = schema[columnName];
1312
- const sqlType = this.getSqlType(columnDef.type);
1728
+ const largeDataColumns = [
1729
+ "workingMemory",
1730
+ "snapshot",
1731
+ "metadata",
1732
+ "content",
1733
+ "input",
1734
+ "output",
1735
+ "instructions",
1736
+ "other"
1737
+ ];
1738
+ const useLargeStorage = largeDataColumns.includes(columnName);
1739
+ const isIndexed = !!columnDef.primaryKey;
1740
+ const sqlType = this.getSqlType(columnDef.type, isIndexed, useLargeStorage);
1313
1741
  const nullable = columnDef.nullable === false ? "NOT NULL" : "";
1314
1742
  const defaultValue = columnDef.nullable === false ? this.getDefaultValue(columnDef.type) : "";
1315
1743
  const parsedColumnName = parseSqlIdentifier(columnName, "column name");
@@ -1337,11 +1765,15 @@ ${columns}
1337
1765
  try {
1338
1766
  const keyEntries = Object.entries(keys).map(([key, value]) => [parseSqlIdentifier(key, "column name"), value]);
1339
1767
  const conditions = keyEntries.map(([key], i) => `[${key}] = @param${i}`).join(" AND ");
1340
- const values = keyEntries.map(([_, value]) => value);
1341
1768
  const sql7 = `SELECT * FROM ${getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) })} WHERE ${conditions}`;
1342
1769
  const request = this.pool.request();
1343
- values.forEach((value, i) => {
1344
- request.input(`param${i}`, value);
1770
+ keyEntries.forEach(([key, value], i) => {
1771
+ const preparedValue = this.prepareValue(value, key, tableName);
1772
+ if (preparedValue === null || preparedValue === void 0) {
1773
+ request.input(`param${i}`, this.getMssqlType(tableName, key), null);
1774
+ } else {
1775
+ request.input(`param${i}`, preparedValue);
1776
+ }
1345
1777
  });
1346
1778
  const resultSet = await request.query(sql7);
1347
1779
  const result = resultSet.recordset[0] || null;
@@ -1375,7 +1807,7 @@ ${columns}
1375
1807
  try {
1376
1808
  await transaction.begin();
1377
1809
  for (const record of records) {
1378
- await this.insert({ tableName, record });
1810
+ await this.insert({ tableName, record, transaction });
1379
1811
  }
1380
1812
  await transaction.commit();
1381
1813
  } catch (error) {
@@ -1412,50 +1844,591 @@ ${columns}
1412
1844
  );
1413
1845
  }
1414
1846
  }
1415
- };
1416
- function parseJSON(jsonString) {
1417
- try {
1418
- return JSON.parse(jsonString);
1419
- } catch {
1420
- return jsonString;
1847
+ /**
1848
+ * Prepares a value for database operations, handling Date objects and JSON serialization
1849
+ */
1850
+ prepareValue(value, columnName, tableName) {
1851
+ if (value === null || value === void 0) {
1852
+ return value;
1853
+ }
1854
+ if (value instanceof Date) {
1855
+ return value;
1856
+ }
1857
+ const schema = TABLE_SCHEMAS[tableName];
1858
+ const columnSchema = schema?.[columnName];
1859
+ if (columnSchema?.type === "boolean") {
1860
+ return value ? 1 : 0;
1861
+ }
1862
+ if (columnSchema?.type === "jsonb") {
1863
+ return JSON.stringify(value);
1864
+ }
1865
+ if (typeof value === "object") {
1866
+ return JSON.stringify(value);
1867
+ }
1868
+ return value;
1421
1869
  }
1422
- }
1423
- function transformScoreRow(row) {
1424
- return {
1425
- ...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),
1435
- createdAt: row.createdAt,
1436
- updatedAt: row.updatedAt
1437
- };
1438
- }
1439
- var ScoresMSSQL = class extends ScoresStorage {
1440
- pool;
1441
- operations;
1442
- schema;
1443
- constructor({
1444
- pool,
1445
- operations,
1446
- schema
1447
- }) {
1448
- super();
1449
- this.pool = pool;
1450
- this.operations = operations;
1451
- this.schema = schema;
1870
+ /**
1871
+ * Maps TABLE_SCHEMAS types to mssql param types (used when value is null)
1872
+ */
1873
+ getMssqlType(tableName, columnName) {
1874
+ const col = TABLE_SCHEMAS[tableName]?.[columnName];
1875
+ switch (col?.type) {
1876
+ case "text":
1877
+ return sql3.NVarChar;
1878
+ case "timestamp":
1879
+ return sql3.DateTime2;
1880
+ case "uuid":
1881
+ return sql3.UniqueIdentifier;
1882
+ case "jsonb":
1883
+ return sql3.NVarChar;
1884
+ case "integer":
1885
+ return sql3.Int;
1886
+ case "bigint":
1887
+ return sql3.BigInt;
1888
+ case "float":
1889
+ return sql3.Float;
1890
+ case "boolean":
1891
+ return sql3.Bit;
1892
+ default:
1893
+ return sql3.NVarChar;
1894
+ }
1452
1895
  }
1453
- async getScoreById({ id }) {
1896
+ /**
1897
+ * Update a single record in the database
1898
+ */
1899
+ async update({
1900
+ tableName,
1901
+ keys,
1902
+ data,
1903
+ transaction
1904
+ }) {
1454
1905
  try {
1455
- const request = this.pool.request();
1456
- request.input("p1", id);
1457
- const result = await request.query(
1458
- `SELECT * FROM ${getTableName({ indexName: TABLE_SCORERS, schemaName: getSchemaName(this.schema) })} WHERE id = @p1`
1906
+ if (!data || Object.keys(data).length === 0) {
1907
+ throw new MastraError({
1908
+ id: "MASTRA_STORAGE_MSSQL_UPDATE_EMPTY_DATA",
1909
+ domain: ErrorDomain.STORAGE,
1910
+ category: ErrorCategory.USER,
1911
+ text: "Cannot update with empty data payload"
1912
+ });
1913
+ }
1914
+ if (!keys || Object.keys(keys).length === 0) {
1915
+ throw new MastraError({
1916
+ id: "MASTRA_STORAGE_MSSQL_UPDATE_EMPTY_KEYS",
1917
+ domain: ErrorDomain.STORAGE,
1918
+ category: ErrorCategory.USER,
1919
+ text: "Cannot update without keys to identify records"
1920
+ });
1921
+ }
1922
+ const setClauses = [];
1923
+ const request = transaction ? transaction.request() : this.pool.request();
1924
+ let paramIndex = 0;
1925
+ Object.entries(data).forEach(([key, value]) => {
1926
+ const parsedKey = parseSqlIdentifier(key, "column name");
1927
+ const paramName = `set${paramIndex++}`;
1928
+ setClauses.push(`[${parsedKey}] = @${paramName}`);
1929
+ const preparedValue = this.prepareValue(value, key, tableName);
1930
+ if (preparedValue === null || preparedValue === void 0) {
1931
+ request.input(paramName, this.getMssqlType(tableName, key), null);
1932
+ } else {
1933
+ request.input(paramName, preparedValue);
1934
+ }
1935
+ });
1936
+ const whereConditions = [];
1937
+ Object.entries(keys).forEach(([key, value]) => {
1938
+ const parsedKey = parseSqlIdentifier(key, "column name");
1939
+ const paramName = `where${paramIndex++}`;
1940
+ whereConditions.push(`[${parsedKey}] = @${paramName}`);
1941
+ const preparedValue = this.prepareValue(value, key, tableName);
1942
+ if (preparedValue === null || preparedValue === void 0) {
1943
+ request.input(paramName, this.getMssqlType(tableName, key), null);
1944
+ } else {
1945
+ request.input(paramName, preparedValue);
1946
+ }
1947
+ });
1948
+ const tableName_ = getTableName({
1949
+ indexName: tableName,
1950
+ schemaName: getSchemaName(this.schemaName)
1951
+ });
1952
+ const updateSql = `UPDATE ${tableName_} SET ${setClauses.join(", ")} WHERE ${whereConditions.join(" AND ")}`;
1953
+ await request.query(updateSql);
1954
+ } catch (error) {
1955
+ throw new MastraError(
1956
+ {
1957
+ id: "MASTRA_STORAGE_MSSQL_STORE_UPDATE_FAILED",
1958
+ domain: ErrorDomain.STORAGE,
1959
+ category: ErrorCategory.THIRD_PARTY,
1960
+ details: {
1961
+ tableName
1962
+ }
1963
+ },
1964
+ error
1965
+ );
1966
+ }
1967
+ }
1968
+ /**
1969
+ * Update multiple records in a single batch transaction
1970
+ */
1971
+ async batchUpdate({
1972
+ tableName,
1973
+ updates
1974
+ }) {
1975
+ const transaction = this.pool.transaction();
1976
+ try {
1977
+ await transaction.begin();
1978
+ for (const { keys, data } of updates) {
1979
+ await this.update({ tableName, keys, data, transaction });
1980
+ }
1981
+ await transaction.commit();
1982
+ } catch (error) {
1983
+ await transaction.rollback();
1984
+ throw new MastraError(
1985
+ {
1986
+ id: "MASTRA_STORAGE_MSSQL_STORE_BATCH_UPDATE_FAILED",
1987
+ domain: ErrorDomain.STORAGE,
1988
+ category: ErrorCategory.THIRD_PARTY,
1989
+ details: {
1990
+ tableName,
1991
+ numberOfRecords: updates.length
1992
+ }
1993
+ },
1994
+ error
1995
+ );
1996
+ }
1997
+ }
1998
+ /**
1999
+ * Delete multiple records by keys
2000
+ */
2001
+ async batchDelete({ tableName, keys }) {
2002
+ if (keys.length === 0) {
2003
+ return;
2004
+ }
2005
+ const tableName_ = getTableName({
2006
+ indexName: tableName,
2007
+ schemaName: getSchemaName(this.schemaName)
2008
+ });
2009
+ const transaction = this.pool.transaction();
2010
+ try {
2011
+ await transaction.begin();
2012
+ for (const keySet of keys) {
2013
+ const conditions = [];
2014
+ const request = transaction.request();
2015
+ let paramIndex = 0;
2016
+ Object.entries(keySet).forEach(([key, value]) => {
2017
+ const parsedKey = parseSqlIdentifier(key, "column name");
2018
+ const paramName = `p${paramIndex++}`;
2019
+ conditions.push(`[${parsedKey}] = @${paramName}`);
2020
+ const preparedValue = this.prepareValue(value, key, tableName);
2021
+ if (preparedValue === null || preparedValue === void 0) {
2022
+ request.input(paramName, this.getMssqlType(tableName, key), null);
2023
+ } else {
2024
+ request.input(paramName, preparedValue);
2025
+ }
2026
+ });
2027
+ const deleteSql = `DELETE FROM ${tableName_} WHERE ${conditions.join(" AND ")}`;
2028
+ await request.query(deleteSql);
2029
+ }
2030
+ await transaction.commit();
2031
+ } catch (error) {
2032
+ await transaction.rollback();
2033
+ throw new MastraError(
2034
+ {
2035
+ id: "MASTRA_STORAGE_MSSQL_STORE_BATCH_DELETE_FAILED",
2036
+ domain: ErrorDomain.STORAGE,
2037
+ category: ErrorCategory.THIRD_PARTY,
2038
+ details: {
2039
+ tableName,
2040
+ numberOfRecords: keys.length
2041
+ }
2042
+ },
2043
+ error
2044
+ );
2045
+ }
2046
+ }
2047
+ /**
2048
+ * Create a new index on a table
2049
+ */
2050
+ async createIndex(options) {
2051
+ try {
2052
+ const { name, table, columns, unique = false, where } = options;
2053
+ const schemaName = this.schemaName || "dbo";
2054
+ const fullTableName = getTableName({
2055
+ indexName: table,
2056
+ schemaName: getSchemaName(this.schemaName)
2057
+ });
2058
+ const indexNameSafe = parseSqlIdentifier(name, "index name");
2059
+ const checkRequest = this.pool.request();
2060
+ checkRequest.input("indexName", indexNameSafe);
2061
+ checkRequest.input("schemaName", schemaName);
2062
+ checkRequest.input("tableName", table);
2063
+ const indexExists = await checkRequest.query(`
2064
+ SELECT 1 as found
2065
+ FROM sys.indexes i
2066
+ INNER JOIN sys.tables t ON i.object_id = t.object_id
2067
+ INNER JOIN sys.schemas s ON t.schema_id = s.schema_id
2068
+ WHERE i.name = @indexName
2069
+ AND s.name = @schemaName
2070
+ AND t.name = @tableName
2071
+ `);
2072
+ if (indexExists.recordset && indexExists.recordset.length > 0) {
2073
+ return;
2074
+ }
2075
+ const uniqueStr = unique ? "UNIQUE " : "";
2076
+ const columnsStr = columns.map((col) => {
2077
+ if (col.includes(" DESC") || col.includes(" ASC")) {
2078
+ const [colName, ...modifiers] = col.split(" ");
2079
+ if (!colName) {
2080
+ throw new Error(`Invalid column specification: ${col}`);
2081
+ }
2082
+ return `[${parseSqlIdentifier(colName, "column name")}] ${modifiers.join(" ")}`;
2083
+ }
2084
+ return `[${parseSqlIdentifier(col, "column name")}]`;
2085
+ }).join(", ");
2086
+ const whereStr = where ? ` WHERE ${where}` : "";
2087
+ const createIndexSql = `CREATE ${uniqueStr}INDEX [${indexNameSafe}] ON ${fullTableName} (${columnsStr})${whereStr}`;
2088
+ await this.pool.request().query(createIndexSql);
2089
+ } catch (error) {
2090
+ throw new MastraError(
2091
+ {
2092
+ id: "MASTRA_STORAGE_MSSQL_INDEX_CREATE_FAILED",
2093
+ domain: ErrorDomain.STORAGE,
2094
+ category: ErrorCategory.THIRD_PARTY,
2095
+ details: {
2096
+ indexName: options.name,
2097
+ tableName: options.table
2098
+ }
2099
+ },
2100
+ error
2101
+ );
2102
+ }
2103
+ }
2104
+ /**
2105
+ * Drop an existing index
2106
+ */
2107
+ async dropIndex(indexName) {
2108
+ try {
2109
+ const schemaName = this.schemaName || "dbo";
2110
+ const indexNameSafe = parseSqlIdentifier(indexName, "index name");
2111
+ const checkRequest = this.pool.request();
2112
+ checkRequest.input("indexName", indexNameSafe);
2113
+ checkRequest.input("schemaName", schemaName);
2114
+ const result = await checkRequest.query(`
2115
+ SELECT t.name as table_name
2116
+ FROM sys.indexes i
2117
+ INNER JOIN sys.tables t ON i.object_id = t.object_id
2118
+ INNER JOIN sys.schemas s ON t.schema_id = s.schema_id
2119
+ WHERE i.name = @indexName
2120
+ AND s.name = @schemaName
2121
+ `);
2122
+ if (!result.recordset || result.recordset.length === 0) {
2123
+ return;
2124
+ }
2125
+ if (result.recordset.length > 1) {
2126
+ const tables = result.recordset.map((r) => r.table_name).join(", ");
2127
+ throw new MastraError({
2128
+ id: "MASTRA_STORAGE_MSSQL_INDEX_AMBIGUOUS",
2129
+ domain: ErrorDomain.STORAGE,
2130
+ category: ErrorCategory.USER,
2131
+ text: `Index "${indexNameSafe}" exists on multiple tables (${tables}) in schema "${schemaName}". Please drop indexes manually or ensure unique index names.`
2132
+ });
2133
+ }
2134
+ const tableName = result.recordset[0].table_name;
2135
+ const fullTableName = getTableName({
2136
+ indexName: tableName,
2137
+ schemaName: getSchemaName(this.schemaName)
2138
+ });
2139
+ const dropSql = `DROP INDEX [${indexNameSafe}] ON ${fullTableName}`;
2140
+ await this.pool.request().query(dropSql);
2141
+ } catch (error) {
2142
+ throw new MastraError(
2143
+ {
2144
+ id: "MASTRA_STORAGE_MSSQL_INDEX_DROP_FAILED",
2145
+ domain: ErrorDomain.STORAGE,
2146
+ category: ErrorCategory.THIRD_PARTY,
2147
+ details: {
2148
+ indexName
2149
+ }
2150
+ },
2151
+ error
2152
+ );
2153
+ }
2154
+ }
2155
+ /**
2156
+ * List indexes for a specific table or all tables
2157
+ */
2158
+ async listIndexes(tableName) {
2159
+ try {
2160
+ const schemaName = this.schemaName || "dbo";
2161
+ let query;
2162
+ const request = this.pool.request();
2163
+ request.input("schemaName", schemaName);
2164
+ if (tableName) {
2165
+ query = `
2166
+ SELECT
2167
+ i.name as name,
2168
+ o.name as [table],
2169
+ i.is_unique as is_unique,
2170
+ CAST(SUM(s.used_page_count) * 8 / 1024.0 AS VARCHAR(50)) + ' MB' as size
2171
+ FROM sys.indexes i
2172
+ INNER JOIN sys.objects o ON i.object_id = o.object_id
2173
+ INNER JOIN sys.schemas sch ON o.schema_id = sch.schema_id
2174
+ LEFT JOIN sys.dm_db_partition_stats s ON i.object_id = s.object_id AND i.index_id = s.index_id
2175
+ WHERE sch.name = @schemaName
2176
+ AND o.name = @tableName
2177
+ AND i.name IS NOT NULL
2178
+ GROUP BY i.name, o.name, i.is_unique
2179
+ `;
2180
+ request.input("tableName", tableName);
2181
+ } else {
2182
+ query = `
2183
+ SELECT
2184
+ i.name as name,
2185
+ o.name as [table],
2186
+ i.is_unique as is_unique,
2187
+ CAST(SUM(s.used_page_count) * 8 / 1024.0 AS VARCHAR(50)) + ' MB' as size
2188
+ FROM sys.indexes i
2189
+ INNER JOIN sys.objects o ON i.object_id = o.object_id
2190
+ INNER JOIN sys.schemas sch ON o.schema_id = sch.schema_id
2191
+ LEFT JOIN sys.dm_db_partition_stats s ON i.object_id = s.object_id AND i.index_id = s.index_id
2192
+ WHERE sch.name = @schemaName
2193
+ AND i.name IS NOT NULL
2194
+ GROUP BY i.name, o.name, i.is_unique
2195
+ `;
2196
+ }
2197
+ const result = await request.query(query);
2198
+ const indexes = [];
2199
+ for (const row of result.recordset) {
2200
+ const colRequest = this.pool.request();
2201
+ colRequest.input("indexName", row.name);
2202
+ colRequest.input("schemaName", schemaName);
2203
+ const colResult = await colRequest.query(`
2204
+ SELECT c.name as column_name
2205
+ FROM sys.indexes i
2206
+ INNER JOIN sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id
2207
+ INNER JOIN sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id
2208
+ INNER JOIN sys.objects o ON i.object_id = o.object_id
2209
+ INNER JOIN sys.schemas s ON o.schema_id = s.schema_id
2210
+ WHERE i.name = @indexName
2211
+ AND s.name = @schemaName
2212
+ ORDER BY ic.key_ordinal
2213
+ `);
2214
+ indexes.push({
2215
+ name: row.name,
2216
+ table: row.table,
2217
+ columns: colResult.recordset.map((c) => c.column_name),
2218
+ unique: row.is_unique || false,
2219
+ size: row.size || "0 MB",
2220
+ definition: ""
2221
+ // MSSQL doesn't store definition like PG
2222
+ });
2223
+ }
2224
+ return indexes;
2225
+ } catch (error) {
2226
+ throw new MastraError(
2227
+ {
2228
+ id: "MASTRA_STORAGE_MSSQL_INDEX_LIST_FAILED",
2229
+ domain: ErrorDomain.STORAGE,
2230
+ category: ErrorCategory.THIRD_PARTY,
2231
+ details: tableName ? {
2232
+ tableName
2233
+ } : {}
2234
+ },
2235
+ error
2236
+ );
2237
+ }
2238
+ }
2239
+ /**
2240
+ * Get detailed statistics for a specific index
2241
+ */
2242
+ async describeIndex(indexName) {
2243
+ try {
2244
+ const schemaName = this.schemaName || "dbo";
2245
+ const request = this.pool.request();
2246
+ request.input("indexName", indexName);
2247
+ request.input("schemaName", schemaName);
2248
+ const query = `
2249
+ SELECT
2250
+ i.name as name,
2251
+ o.name as [table],
2252
+ i.is_unique as is_unique,
2253
+ CAST(SUM(s.used_page_count) * 8 / 1024.0 AS VARCHAR(50)) + ' MB' as size,
2254
+ i.type_desc as method,
2255
+ ISNULL(us.user_scans, 0) as scans,
2256
+ ISNULL(us.user_seeks + us.user_scans, 0) as tuples_read,
2257
+ ISNULL(us.user_lookups, 0) as tuples_fetched
2258
+ FROM sys.indexes i
2259
+ INNER JOIN sys.objects o ON i.object_id = o.object_id
2260
+ INNER JOIN sys.schemas sch ON o.schema_id = sch.schema_id
2261
+ LEFT JOIN sys.dm_db_partition_stats s ON i.object_id = s.object_id AND i.index_id = s.index_id
2262
+ LEFT JOIN sys.dm_db_index_usage_stats us ON i.object_id = us.object_id AND i.index_id = us.index_id
2263
+ WHERE i.name = @indexName
2264
+ AND sch.name = @schemaName
2265
+ GROUP BY i.name, o.name, i.is_unique, i.type_desc, us.user_seeks, us.user_scans, us.user_lookups
2266
+ `;
2267
+ const result = await request.query(query);
2268
+ if (!result.recordset || result.recordset.length === 0) {
2269
+ throw new Error(`Index "${indexName}" not found in schema "${schemaName}"`);
2270
+ }
2271
+ const row = result.recordset[0];
2272
+ const colRequest = this.pool.request();
2273
+ colRequest.input("indexName", indexName);
2274
+ colRequest.input("schemaName", schemaName);
2275
+ const colResult = await colRequest.query(`
2276
+ SELECT c.name as column_name
2277
+ FROM sys.indexes i
2278
+ INNER JOIN sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id
2279
+ INNER JOIN sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id
2280
+ INNER JOIN sys.objects o ON i.object_id = o.object_id
2281
+ INNER JOIN sys.schemas s ON o.schema_id = s.schema_id
2282
+ WHERE i.name = @indexName
2283
+ AND s.name = @schemaName
2284
+ ORDER BY ic.key_ordinal
2285
+ `);
2286
+ return {
2287
+ name: row.name,
2288
+ table: row.table,
2289
+ columns: colResult.recordset.map((c) => c.column_name),
2290
+ unique: row.is_unique || false,
2291
+ size: row.size || "0 MB",
2292
+ definition: "",
2293
+ method: row.method?.toLowerCase() || "nonclustered",
2294
+ scans: Number(row.scans) || 0,
2295
+ tuples_read: Number(row.tuples_read) || 0,
2296
+ tuples_fetched: Number(row.tuples_fetched) || 0
2297
+ };
2298
+ } catch (error) {
2299
+ throw new MastraError(
2300
+ {
2301
+ id: "MASTRA_STORAGE_MSSQL_INDEX_DESCRIBE_FAILED",
2302
+ domain: ErrorDomain.STORAGE,
2303
+ category: ErrorCategory.THIRD_PARTY,
2304
+ details: {
2305
+ indexName
2306
+ }
2307
+ },
2308
+ error
2309
+ );
2310
+ }
2311
+ }
2312
+ /**
2313
+ * Returns definitions for automatic performance indexes
2314
+ * IMPORTANT: Uses seq_id DESC instead of createdAt DESC for MSSQL due to millisecond accuracy limitations
2315
+ * NOTE: Using NVARCHAR(400) for text columns (800 bytes) leaves room for composite indexes
2316
+ */
2317
+ getAutomaticIndexDefinitions() {
2318
+ const schemaPrefix = this.schemaName ? `${this.schemaName}_` : "";
2319
+ return [
2320
+ // Composite indexes for optimal filtering + sorting performance
2321
+ // NVARCHAR(400) = 800 bytes, plus BIGINT (8 bytes) = 808 bytes total (under 900-byte limit)
2322
+ {
2323
+ name: `${schemaPrefix}mastra_threads_resourceid_seqid_idx`,
2324
+ table: TABLE_THREADS,
2325
+ columns: ["resourceId", "seq_id DESC"]
2326
+ },
2327
+ {
2328
+ name: `${schemaPrefix}mastra_messages_thread_id_seqid_idx`,
2329
+ table: TABLE_MESSAGES,
2330
+ columns: ["thread_id", "seq_id DESC"]
2331
+ },
2332
+ {
2333
+ name: `${schemaPrefix}mastra_traces_name_seqid_idx`,
2334
+ table: TABLE_TRACES,
2335
+ columns: ["name", "seq_id DESC"]
2336
+ },
2337
+ {
2338
+ name: `${schemaPrefix}mastra_evals_agent_name_seqid_idx`,
2339
+ table: TABLE_EVALS,
2340
+ columns: ["agent_name", "seq_id DESC"]
2341
+ },
2342
+ {
2343
+ name: `${schemaPrefix}mastra_scores_trace_id_span_id_seqid_idx`,
2344
+ table: TABLE_SCORERS,
2345
+ columns: ["traceId", "spanId", "seq_id DESC"]
2346
+ },
2347
+ // AI Spans indexes for optimal trace querying
2348
+ {
2349
+ name: `${schemaPrefix}mastra_ai_spans_traceid_startedat_idx`,
2350
+ table: TABLE_AI_SPANS,
2351
+ columns: ["traceId", "startedAt DESC"]
2352
+ },
2353
+ {
2354
+ name: `${schemaPrefix}mastra_ai_spans_parentspanid_startedat_idx`,
2355
+ table: TABLE_AI_SPANS,
2356
+ columns: ["parentSpanId", "startedAt DESC"]
2357
+ },
2358
+ {
2359
+ name: `${schemaPrefix}mastra_ai_spans_name_idx`,
2360
+ table: TABLE_AI_SPANS,
2361
+ columns: ["name"]
2362
+ },
2363
+ {
2364
+ name: `${schemaPrefix}mastra_ai_spans_spantype_startedat_idx`,
2365
+ table: TABLE_AI_SPANS,
2366
+ columns: ["spanType", "startedAt DESC"]
2367
+ }
2368
+ ];
2369
+ }
2370
+ /**
2371
+ * Creates automatic indexes for optimal query performance
2372
+ * Uses getAutomaticIndexDefinitions() to determine which indexes to create
2373
+ */
2374
+ async createAutomaticIndexes() {
2375
+ try {
2376
+ const indexes = this.getAutomaticIndexDefinitions();
2377
+ for (const indexOptions of indexes) {
2378
+ try {
2379
+ await this.createIndex(indexOptions);
2380
+ } catch (error) {
2381
+ this.logger?.warn?.(`Failed to create index ${indexOptions.name}:`, error);
2382
+ }
2383
+ }
2384
+ } catch (error) {
2385
+ throw new MastraError(
2386
+ {
2387
+ id: "MASTRA_STORAGE_MSSQL_STORE_CREATE_PERFORMANCE_INDEXES_FAILED",
2388
+ domain: ErrorDomain.STORAGE,
2389
+ category: ErrorCategory.THIRD_PARTY
2390
+ },
2391
+ error
2392
+ );
2393
+ }
2394
+ }
2395
+ };
2396
+ function transformScoreRow(row) {
2397
+ return {
2398
+ ...row,
2399
+ input: safelyParseJSON(row.input),
2400
+ scorer: safelyParseJSON(row.scorer),
2401
+ preprocessStepResult: safelyParseJSON(row.preprocessStepResult),
2402
+ analyzeStepResult: safelyParseJSON(row.analyzeStepResult),
2403
+ metadata: safelyParseJSON(row.metadata),
2404
+ output: safelyParseJSON(row.output),
2405
+ additionalContext: safelyParseJSON(row.additionalContext),
2406
+ runtimeContext: safelyParseJSON(row.runtimeContext),
2407
+ entity: safelyParseJSON(row.entity),
2408
+ createdAt: row.createdAt,
2409
+ updatedAt: row.updatedAt
2410
+ };
2411
+ }
2412
+ var ScoresMSSQL = class extends ScoresStorage {
2413
+ pool;
2414
+ operations;
2415
+ schema;
2416
+ constructor({
2417
+ pool,
2418
+ operations,
2419
+ schema
2420
+ }) {
2421
+ super();
2422
+ this.pool = pool;
2423
+ this.operations = operations;
2424
+ this.schema = schema;
2425
+ }
2426
+ async getScoreById({ id }) {
2427
+ try {
2428
+ const request = this.pool.request();
2429
+ request.input("p1", id);
2430
+ const result = await request.query(
2431
+ `SELECT * FROM ${getTableName({ indexName: TABLE_SCORERS, schemaName: getSchemaName(this.schema) })} WHERE id = @p1`
1459
2432
  );
1460
2433
  if (result.recordset.length === 0) {
1461
2434
  return null;
@@ -1488,7 +2461,7 @@ var ScoresMSSQL = class extends ScoresStorage {
1488
2461
  );
1489
2462
  }
1490
2463
  try {
1491
- const scoreId = crypto.randomUUID();
2464
+ const scoreId = randomUUID();
1492
2465
  const {
1493
2466
  scorer,
1494
2467
  preprocessStepResult,
@@ -1506,15 +2479,15 @@ var ScoresMSSQL = class extends ScoresStorage {
1506
2479
  record: {
1507
2480
  id: scoreId,
1508
2481
  ...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,
2482
+ input: input || "",
2483
+ output: output || "",
2484
+ preprocessStepResult: preprocessStepResult || null,
2485
+ analyzeStepResult: analyzeStepResult || null,
2486
+ metadata: metadata || null,
2487
+ additionalContext: additionalContext || null,
2488
+ runtimeContext: runtimeContext || null,
2489
+ entity: entity || null,
2490
+ scorer: scorer || null,
1518
2491
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1519
2492
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1520
2493
  }
@@ -1534,14 +2507,37 @@ var ScoresMSSQL = class extends ScoresStorage {
1534
2507
  }
1535
2508
  async getScoresByScorerId({
1536
2509
  scorerId,
1537
- pagination
2510
+ pagination,
2511
+ entityId,
2512
+ entityType,
2513
+ source
1538
2514
  }) {
1539
2515
  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
- );
2516
+ const conditions = ["[scorerId] = @p1"];
2517
+ const params = { p1: scorerId };
2518
+ let paramIndex = 2;
2519
+ if (entityId) {
2520
+ conditions.push(`[entityId] = @p${paramIndex}`);
2521
+ params[`p${paramIndex}`] = entityId;
2522
+ paramIndex++;
2523
+ }
2524
+ if (entityType) {
2525
+ conditions.push(`[entityType] = @p${paramIndex}`);
2526
+ params[`p${paramIndex}`] = entityType;
2527
+ paramIndex++;
2528
+ }
2529
+ if (source) {
2530
+ conditions.push(`[source] = @p${paramIndex}`);
2531
+ params[`p${paramIndex}`] = source;
2532
+ paramIndex++;
2533
+ }
2534
+ const whereClause = conditions.join(" AND ");
2535
+ const tableName = getTableName({ indexName: TABLE_SCORERS, schemaName: getSchemaName(this.schema) });
2536
+ const countRequest = this.pool.request();
2537
+ Object.entries(params).forEach(([key, value]) => {
2538
+ countRequest.input(key, value);
2539
+ });
2540
+ const totalResult = await countRequest.query(`SELECT COUNT(*) as count FROM ${tableName} WHERE ${whereClause}`);
1545
2541
  const total = totalResult.recordset[0]?.count || 0;
1546
2542
  if (total === 0) {
1547
2543
  return {
@@ -1555,12 +2551,13 @@ var ScoresMSSQL = class extends ScoresStorage {
1555
2551
  };
1556
2552
  }
1557
2553
  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
- );
2554
+ Object.entries(params).forEach(([key, value]) => {
2555
+ dataRequest.input(key, value);
2556
+ });
2557
+ dataRequest.input("perPage", pagination.perPage);
2558
+ dataRequest.input("offset", pagination.page * pagination.perPage);
2559
+ const dataQuery = `SELECT * FROM ${tableName} WHERE ${whereClause} ORDER BY [createdAt] DESC OFFSET @offset ROWS FETCH NEXT @perPage ROWS ONLY`;
2560
+ const result = await dataRequest.query(dataQuery);
1564
2561
  return {
1565
2562
  pagination: {
1566
2563
  total: Number(total),
@@ -1817,7 +2814,7 @@ var TracesMSSQL = class extends TracesStorage {
1817
2814
  const countRequest = this.pool.request();
1818
2815
  Object.entries(paramMap).forEach(([key, value]) => {
1819
2816
  if (value instanceof Date) {
1820
- countRequest.input(key, sql2.DateTime, value);
2817
+ countRequest.input(key, sql3.DateTime, value);
1821
2818
  } else {
1822
2819
  countRequest.input(key, value);
1823
2820
  }
@@ -1851,7 +2848,7 @@ var TracesMSSQL = class extends TracesStorage {
1851
2848
  const dataRequest = this.pool.request();
1852
2849
  Object.entries(paramMap).forEach(([key, value]) => {
1853
2850
  if (value instanceof Date) {
1854
- dataRequest.input(key, sql2.DateTime, value);
2851
+ dataRequest.input(key, sql3.DateTime, value);
1855
2852
  } else {
1856
2853
  dataRequest.input(key, value);
1857
2854
  }
@@ -1907,24 +2904,6 @@ var TracesMSSQL = class extends TracesStorage {
1907
2904
  });
1908
2905
  }
1909
2906
  };
1910
- function parseWorkflowRun(row) {
1911
- let parsedSnapshot = row.snapshot;
1912
- if (typeof parsedSnapshot === "string") {
1913
- try {
1914
- parsedSnapshot = JSON.parse(row.snapshot);
1915
- } catch (e) {
1916
- console.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
1917
- }
1918
- }
1919
- return {
1920
- workflowName: row.workflow_name,
1921
- runId: row.run_id,
1922
- snapshot: parsedSnapshot,
1923
- createdAt: row.createdAt,
1924
- updatedAt: row.updatedAt,
1925
- resourceId: row.resourceId
1926
- };
1927
- }
1928
2907
  var WorkflowsMSSQL = class extends WorkflowsStorage {
1929
2908
  pool;
1930
2909
  operations;
@@ -1939,21 +2918,163 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
1939
2918
  this.operations = operations;
1940
2919
  this.schema = schema;
1941
2920
  }
1942
- updateWorkflowResults({
1943
- // workflowName,
1944
- // runId,
1945
- // stepId,
1946
- // result,
1947
- // runtimeContext,
2921
+ parseWorkflowRun(row) {
2922
+ let parsedSnapshot = row.snapshot;
2923
+ if (typeof parsedSnapshot === "string") {
2924
+ try {
2925
+ parsedSnapshot = JSON.parse(row.snapshot);
2926
+ } catch (e) {
2927
+ this.logger?.warn?.(`Failed to parse snapshot for workflow ${row.workflow_name}:`, e);
2928
+ }
2929
+ }
2930
+ return {
2931
+ workflowName: row.workflow_name,
2932
+ runId: row.run_id,
2933
+ snapshot: parsedSnapshot,
2934
+ createdAt: row.createdAt,
2935
+ updatedAt: row.updatedAt,
2936
+ resourceId: row.resourceId
2937
+ };
2938
+ }
2939
+ async updateWorkflowResults({
2940
+ workflowName,
2941
+ runId,
2942
+ stepId,
2943
+ result,
2944
+ runtimeContext
1948
2945
  }) {
1949
- throw new Error("Method not implemented.");
2946
+ const table = getTableName({ indexName: TABLE_WORKFLOW_SNAPSHOT, schemaName: getSchemaName(this.schema) });
2947
+ const transaction = this.pool.transaction();
2948
+ try {
2949
+ await transaction.begin();
2950
+ const selectRequest = new sql3.Request(transaction);
2951
+ selectRequest.input("workflow_name", workflowName);
2952
+ selectRequest.input("run_id", runId);
2953
+ const existingSnapshotResult = await selectRequest.query(
2954
+ `SELECT snapshot FROM ${table} WITH (UPDLOCK, HOLDLOCK) WHERE workflow_name = @workflow_name AND run_id = @run_id`
2955
+ );
2956
+ let snapshot;
2957
+ if (!existingSnapshotResult.recordset || existingSnapshotResult.recordset.length === 0) {
2958
+ snapshot = {
2959
+ context: {},
2960
+ activePaths: [],
2961
+ timestamp: Date.now(),
2962
+ suspendedPaths: {},
2963
+ resumeLabels: {},
2964
+ serializedStepGraph: [],
2965
+ value: {},
2966
+ waitingPaths: {},
2967
+ status: "pending",
2968
+ runId,
2969
+ runtimeContext: {}
2970
+ };
2971
+ } else {
2972
+ const existingSnapshot = existingSnapshotResult.recordset[0].snapshot;
2973
+ snapshot = typeof existingSnapshot === "string" ? JSON.parse(existingSnapshot) : existingSnapshot;
2974
+ }
2975
+ snapshot.context[stepId] = result;
2976
+ snapshot.runtimeContext = { ...snapshot.runtimeContext, ...runtimeContext };
2977
+ const upsertReq = new sql3.Request(transaction);
2978
+ upsertReq.input("workflow_name", workflowName);
2979
+ upsertReq.input("run_id", runId);
2980
+ upsertReq.input("snapshot", JSON.stringify(snapshot));
2981
+ upsertReq.input("createdAt", sql3.DateTime2, /* @__PURE__ */ new Date());
2982
+ upsertReq.input("updatedAt", sql3.DateTime2, /* @__PURE__ */ new Date());
2983
+ await upsertReq.query(
2984
+ `MERGE ${table} AS target
2985
+ USING (SELECT @workflow_name AS workflow_name, @run_id AS run_id) AS src
2986
+ ON target.workflow_name = src.workflow_name AND target.run_id = src.run_id
2987
+ WHEN MATCHED THEN UPDATE SET snapshot = @snapshot, [updatedAt] = @updatedAt
2988
+ WHEN NOT MATCHED THEN INSERT (workflow_name, run_id, snapshot, [createdAt], [updatedAt])
2989
+ VALUES (@workflow_name, @run_id, @snapshot, @createdAt, @updatedAt);`
2990
+ );
2991
+ await transaction.commit();
2992
+ return snapshot.context;
2993
+ } catch (error) {
2994
+ try {
2995
+ await transaction.rollback();
2996
+ } catch {
2997
+ }
2998
+ throw new MastraError(
2999
+ {
3000
+ id: "MASTRA_STORAGE_MSSQL_STORE_UPDATE_WORKFLOW_RESULTS_FAILED",
3001
+ domain: ErrorDomain.STORAGE,
3002
+ category: ErrorCategory.THIRD_PARTY,
3003
+ details: {
3004
+ workflowName,
3005
+ runId,
3006
+ stepId
3007
+ }
3008
+ },
3009
+ error
3010
+ );
3011
+ }
1950
3012
  }
1951
- updateWorkflowState({
1952
- // workflowName,
1953
- // runId,
1954
- // opts,
3013
+ async updateWorkflowState({
3014
+ workflowName,
3015
+ runId,
3016
+ opts
1955
3017
  }) {
1956
- throw new Error("Method not implemented.");
3018
+ const table = getTableName({ indexName: TABLE_WORKFLOW_SNAPSHOT, schemaName: getSchemaName(this.schema) });
3019
+ const transaction = this.pool.transaction();
3020
+ try {
3021
+ await transaction.begin();
3022
+ const selectRequest = new sql3.Request(transaction);
3023
+ selectRequest.input("workflow_name", workflowName);
3024
+ selectRequest.input("run_id", runId);
3025
+ const existingSnapshotResult = await selectRequest.query(
3026
+ `SELECT snapshot FROM ${table} WITH (UPDLOCK, HOLDLOCK) WHERE workflow_name = @workflow_name AND run_id = @run_id`
3027
+ );
3028
+ if (!existingSnapshotResult.recordset || existingSnapshotResult.recordset.length === 0) {
3029
+ await transaction.rollback();
3030
+ return void 0;
3031
+ }
3032
+ const existingSnapshot = existingSnapshotResult.recordset[0].snapshot;
3033
+ const snapshot = typeof existingSnapshot === "string" ? JSON.parse(existingSnapshot) : existingSnapshot;
3034
+ if (!snapshot || !snapshot?.context) {
3035
+ await transaction.rollback();
3036
+ throw new MastraError(
3037
+ {
3038
+ id: "MASTRA_STORAGE_MSSQL_STORE_UPDATE_WORKFLOW_STATE_SNAPSHOT_NOT_FOUND",
3039
+ domain: ErrorDomain.STORAGE,
3040
+ category: ErrorCategory.SYSTEM,
3041
+ details: {
3042
+ workflowName,
3043
+ runId
3044
+ }
3045
+ },
3046
+ new Error(`Snapshot not found for runId ${runId}`)
3047
+ );
3048
+ }
3049
+ const updatedSnapshot = { ...snapshot, ...opts };
3050
+ const updateRequest = new sql3.Request(transaction);
3051
+ updateRequest.input("snapshot", JSON.stringify(updatedSnapshot));
3052
+ updateRequest.input("workflow_name", workflowName);
3053
+ updateRequest.input("run_id", runId);
3054
+ updateRequest.input("updatedAt", sql3.DateTime2, /* @__PURE__ */ new Date());
3055
+ await updateRequest.query(
3056
+ `UPDATE ${table} SET snapshot = @snapshot, [updatedAt] = @updatedAt WHERE workflow_name = @workflow_name AND run_id = @run_id`
3057
+ );
3058
+ await transaction.commit();
3059
+ return updatedSnapshot;
3060
+ } catch (error) {
3061
+ try {
3062
+ await transaction.rollback();
3063
+ } catch {
3064
+ }
3065
+ throw new MastraError(
3066
+ {
3067
+ id: "MASTRA_STORAGE_MSSQL_STORE_UPDATE_WORKFLOW_STATE_FAILED",
3068
+ domain: ErrorDomain.STORAGE,
3069
+ category: ErrorCategory.THIRD_PARTY,
3070
+ details: {
3071
+ workflowName,
3072
+ runId
3073
+ }
3074
+ },
3075
+ error
3076
+ );
3077
+ }
1957
3078
  }
1958
3079
  async persistWorkflowSnapshot({
1959
3080
  workflowName,
@@ -1969,8 +3090,8 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
1969
3090
  request.input("run_id", runId);
1970
3091
  request.input("resourceId", resourceId);
1971
3092
  request.input("snapshot", JSON.stringify(snapshot));
1972
- request.input("createdAt", sql2.DateTime2, new Date(now));
1973
- request.input("updatedAt", sql2.DateTime2, new Date(now));
3093
+ request.input("createdAt", sql3.DateTime2, new Date(now));
3094
+ request.input("updatedAt", sql3.DateTime2, new Date(now));
1974
3095
  const mergeSql = `MERGE INTO ${table} AS target
1975
3096
  USING (SELECT @workflow_name AS workflow_name, @run_id AS run_id) AS src
1976
3097
  ON target.workflow_name = src.workflow_name AND target.run_id = src.run_id
@@ -2051,7 +3172,7 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
2051
3172
  if (!result.recordset || result.recordset.length === 0) {
2052
3173
  return null;
2053
3174
  }
2054
- return parseWorkflowRun(result.recordset[0]);
3175
+ return this.parseWorkflowRun(result.recordset[0]);
2055
3176
  } catch (error) {
2056
3177
  throw new MastraError(
2057
3178
  {
@@ -2088,7 +3209,7 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
2088
3209
  conditions.push(`[resourceId] = @resourceId`);
2089
3210
  paramMap["resourceId"] = resourceId;
2090
3211
  } else {
2091
- console.warn(`[${TABLE_WORKFLOW_SNAPSHOT}] resourceId column not found. Skipping resourceId filter.`);
3212
+ this.logger?.warn?.(`[${TABLE_WORKFLOW_SNAPSHOT}] resourceId column not found. Skipping resourceId filter.`);
2092
3213
  }
2093
3214
  }
2094
3215
  if (fromDate instanceof Date && !isNaN(fromDate.getTime())) {
@@ -2105,7 +3226,7 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
2105
3226
  const request = this.pool.request();
2106
3227
  Object.entries(paramMap).forEach(([key, value]) => {
2107
3228
  if (value instanceof Date) {
2108
- request.input(key, sql2.DateTime, value);
3229
+ request.input(key, sql3.DateTime, value);
2109
3230
  } else {
2110
3231
  request.input(key, value);
2111
3232
  }
@@ -2122,7 +3243,7 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
2122
3243
  request.input("offset", offset);
2123
3244
  }
2124
3245
  const result = await request.query(query);
2125
- const runs = (result.recordset || []).map((row) => parseWorkflowRun(row));
3246
+ const runs = (result.recordset || []).map((row) => this.parseWorkflowRun(row));
2126
3247
  return { runs, total: total || runs.length };
2127
3248
  } catch (error) {
2128
3249
  throw new MastraError(
@@ -2162,7 +3283,7 @@ var MSSQLStore = class extends MastraStorage {
2162
3283
  }
2163
3284
  }
2164
3285
  this.schema = config.schemaName || "dbo";
2165
- this.pool = "connectionString" in config ? new sql2.ConnectionPool(config.connectionString) : new sql2.ConnectionPool({
3286
+ this.pool = "connectionString" in config ? new sql3.ConnectionPool(config.connectionString) : new sql3.ConnectionPool({
2166
3287
  server: config.server,
2167
3288
  database: config.database,
2168
3289
  user: config.user,
@@ -2176,13 +3297,15 @@ var MSSQLStore = class extends MastraStorage {
2176
3297
  const traces = new TracesMSSQL({ pool: this.pool, operations, schema: this.schema });
2177
3298
  const workflows = new WorkflowsMSSQL({ pool: this.pool, operations, schema: this.schema });
2178
3299
  const memory = new MemoryMSSQL({ pool: this.pool, schema: this.schema, operations });
3300
+ const observability = new ObservabilityMSSQL({ pool: this.pool, operations, schema: this.schema });
2179
3301
  this.stores = {
2180
3302
  operations,
2181
3303
  scores,
2182
3304
  traces,
2183
3305
  workflows,
2184
3306
  legacyEvals,
2185
- memory
3307
+ memory,
3308
+ observability
2186
3309
  };
2187
3310
  } catch (e) {
2188
3311
  throw new MastraError(
@@ -2202,6 +3325,11 @@ var MSSQLStore = class extends MastraStorage {
2202
3325
  try {
2203
3326
  await this.isConnected;
2204
3327
  await super.init();
3328
+ try {
3329
+ await this.stores.operations.createAutomaticIndexes();
3330
+ } catch (indexError) {
3331
+ this.logger?.warn?.("Failed to create indexes:", indexError);
3332
+ }
2205
3333
  } catch (error) {
2206
3334
  this.isConnected = null;
2207
3335
  throw new MastraError(
@@ -2229,7 +3357,9 @@ var MSSQLStore = class extends MastraStorage {
2229
3357
  hasColumn: true,
2230
3358
  createTable: true,
2231
3359
  deleteMessages: true,
2232
- getScoresBySpan: true
3360
+ getScoresBySpan: true,
3361
+ aiTracing: true,
3362
+ indexManagement: true
2233
3363
  };
2234
3364
  }
2235
3365
  /** @deprecated use getEvals instead */
@@ -2395,6 +3525,60 @@ var MSSQLStore = class extends MastraStorage {
2395
3525
  async close() {
2396
3526
  await this.pool.close();
2397
3527
  }
3528
+ /**
3529
+ * Index Management
3530
+ */
3531
+ async createIndex(options) {
3532
+ return this.stores.operations.createIndex(options);
3533
+ }
3534
+ async listIndexes(tableName) {
3535
+ return this.stores.operations.listIndexes(tableName);
3536
+ }
3537
+ async describeIndex(indexName) {
3538
+ return this.stores.operations.describeIndex(indexName);
3539
+ }
3540
+ async dropIndex(indexName) {
3541
+ return this.stores.operations.dropIndex(indexName);
3542
+ }
3543
+ /**
3544
+ * AI Tracing / Observability
3545
+ */
3546
+ getObservabilityStore() {
3547
+ if (!this.stores.observability) {
3548
+ throw new MastraError({
3549
+ id: "MSSQL_STORE_OBSERVABILITY_NOT_INITIALIZED",
3550
+ domain: ErrorDomain.STORAGE,
3551
+ category: ErrorCategory.SYSTEM,
3552
+ text: "Observability storage is not initialized"
3553
+ });
3554
+ }
3555
+ return this.stores.observability;
3556
+ }
3557
+ async createAISpan(span) {
3558
+ return this.getObservabilityStore().createAISpan(span);
3559
+ }
3560
+ async updateAISpan({
3561
+ spanId,
3562
+ traceId,
3563
+ updates
3564
+ }) {
3565
+ return this.getObservabilityStore().updateAISpan({ spanId, traceId, updates });
3566
+ }
3567
+ async getAITrace(traceId) {
3568
+ return this.getObservabilityStore().getAITrace(traceId);
3569
+ }
3570
+ async getAITracesPaginated(args) {
3571
+ return this.getObservabilityStore().getAITracesPaginated(args);
3572
+ }
3573
+ async batchCreateAISpans(args) {
3574
+ return this.getObservabilityStore().batchCreateAISpans(args);
3575
+ }
3576
+ async batchUpdateAISpans(args) {
3577
+ return this.getObservabilityStore().batchUpdateAISpans(args);
3578
+ }
3579
+ async batchDeleteAITraces(args) {
3580
+ return this.getObservabilityStore().batchDeleteAITraces(args);
3581
+ }
2398
3582
  /**
2399
3583
  * Scorers
2400
3584
  */
@@ -2403,9 +3587,18 @@ var MSSQLStore = class extends MastraStorage {
2403
3587
  }
2404
3588
  async getScoresByScorerId({
2405
3589
  scorerId: _scorerId,
2406
- pagination: _pagination
3590
+ pagination: _pagination,
3591
+ entityId: _entityId,
3592
+ entityType: _entityType,
3593
+ source: _source
2407
3594
  }) {
2408
- return this.stores.scores.getScoresByScorerId({ scorerId: _scorerId, pagination: _pagination });
3595
+ return this.stores.scores.getScoresByScorerId({
3596
+ scorerId: _scorerId,
3597
+ pagination: _pagination,
3598
+ entityId: _entityId,
3599
+ entityType: _entityType,
3600
+ source: _source
3601
+ });
2409
3602
  }
2410
3603
  async saveScore(_score) {
2411
3604
  return this.stores.scores.saveScore(_score);