@mastra/mssql 0.0.0-rag-chunk-extract-llm-option-20250926183645 → 0.0.0-remove-unused-model-providers-api-20251030210744

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