@mastra/mssql 0.0.0-roamin-openaivoice-speak-options-passing-20250926163614 → 0.0.0-safe-stringify-telemetry-20251205024938

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