@mastra/mssql 0.0.0-feat-support-ai-sdk-5-again-20250813225910 → 0.0.0-feat-add-query-option-to-playground-20251209160219

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.
Files changed (43) hide show
  1. package/CHANGELOG.md +1004 -3
  2. package/README.md +324 -37
  3. package/dist/index.cjs +1847 -756
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.d.ts +1 -0
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +1848 -757
  8. package/dist/index.js.map +1 -1
  9. package/dist/storage/domains/memory/index.d.ts +15 -36
  10. package/dist/storage/domains/memory/index.d.ts.map +1 -1
  11. package/dist/storage/domains/observability/index.d.ts +44 -0
  12. package/dist/storage/domains/observability/index.d.ts.map +1 -0
  13. package/dist/storage/domains/operations/index.d.ts +67 -4
  14. package/dist/storage/domains/operations/index.d.ts.map +1 -1
  15. package/dist/storage/domains/scores/index.d.ts +15 -6
  16. package/dist/storage/domains/scores/index.d.ts.map +1 -1
  17. package/dist/storage/domains/utils.d.ts +19 -0
  18. package/dist/storage/domains/utils.d.ts.map +1 -1
  19. package/dist/storage/domains/workflows/index.d.ts +28 -10
  20. package/dist/storage/domains/workflows/index.d.ts.map +1 -1
  21. package/dist/storage/index.d.ts +116 -74
  22. package/dist/storage/index.d.ts.map +1 -1
  23. package/package.json +30 -12
  24. package/dist/storage/domains/legacy-evals/index.d.ts +0 -20
  25. package/dist/storage/domains/legacy-evals/index.d.ts.map +0 -1
  26. package/dist/storage/domains/traces/index.d.ts +0 -37
  27. package/dist/storage/domains/traces/index.d.ts.map +0 -1
  28. package/docker-compose.yaml +0 -14
  29. package/eslint.config.js +0 -6
  30. package/src/index.ts +0 -2
  31. package/src/storage/domains/legacy-evals/index.ts +0 -175
  32. package/src/storage/domains/memory/index.ts +0 -1024
  33. package/src/storage/domains/operations/index.ts +0 -401
  34. package/src/storage/domains/scores/index.ts +0 -316
  35. package/src/storage/domains/traces/index.ts +0 -212
  36. package/src/storage/domains/utils.ts +0 -12
  37. package/src/storage/domains/workflows/index.ts +0 -259
  38. package/src/storage/index.test.ts +0 -2228
  39. package/src/storage/index.ts +0 -448
  40. package/tsconfig.build.json +0 -9
  41. package/tsconfig.json +0 -5
  42. package/tsup.config.ts +0 -17
  43. package/vitest.config.ts +0 -12
package/dist/index.js CHANGED
@@ -1,8 +1,10 @@
1
1
  import { MastraError, ErrorCategory, ErrorDomain } from '@mastra/core/error';
2
- import { MastraStorage, LegacyEvalsStorage, StoreOperations, TABLE_WORKFLOW_SNAPSHOT, ScoresStorage, TABLE_SCORERS, TracesStorage, TABLE_TRACES, WorkflowsStorage, MemoryStorage, resolveMessageLimit, TABLE_RESOURCES, TABLE_EVALS, TABLE_THREADS, TABLE_MESSAGES } from '@mastra/core/storage';
3
- import sql2 from 'mssql';
4
- import { parseSqlIdentifier, parseFieldKey } from '@mastra/core/utils';
2
+ import { MastraStorage, createStorageErrorId, StoreOperations, TABLE_WORKFLOW_SNAPSHOT, TABLE_SCHEMAS, TABLE_THREADS, TABLE_MESSAGES, TABLE_TRACES, TABLE_SCORERS, TABLE_SPANS, ScoresStorage, normalizePerPage, calculatePagination, WorkflowsStorage, MemoryStorage, TABLE_RESOURCES, ObservabilityStorage, transformScoreRow as transformScoreRow$1 } from '@mastra/core/storage';
3
+ import sql3 from 'mssql';
5
4
  import { MessageList } from '@mastra/core/agent';
5
+ import { parseSqlIdentifier } from '@mastra/core/utils';
6
+ import { randomUUID } from 'crypto';
7
+ import { saveScorePayloadSchema } from '@mastra/core/evals';
6
8
 
7
9
  // src/storage/index.ts
8
10
  function getSchemaName(schema) {
@@ -14,154 +16,109 @@ function getTableName({ indexName, schemaName }) {
14
16
  const quotedSchemaName = schemaName;
15
17
  return quotedSchemaName ? `${quotedSchemaName}.${quotedIndexName}` : quotedIndexName;
16
18
  }
17
-
18
- // src/storage/domains/legacy-evals/index.ts
19
- function transformEvalRow(row) {
20
- let testInfoValue = null, resultValue = null;
21
- if (row.test_info) {
22
- try {
23
- testInfoValue = typeof row.test_info === "string" ? JSON.parse(row.test_info) : row.test_info;
24
- } catch {
25
- }
19
+ function buildDateRangeFilter(dateRange, fieldName) {
20
+ const filters = {};
21
+ if (dateRange?.start) {
22
+ filters[`${fieldName}_gte`] = dateRange.start;
26
23
  }
27
- if (row.test_info) {
28
- try {
29
- resultValue = typeof row.result === "string" ? JSON.parse(row.result) : row.result;
30
- } catch {
31
- }
24
+ if (dateRange?.end) {
25
+ filters[`${fieldName}_lte`] = dateRange.end;
32
26
  }
33
- return {
34
- agentName: row.agent_name,
35
- input: row.input,
36
- output: row.output,
37
- result: resultValue,
38
- metricName: row.metric_name,
39
- instructions: row.instructions,
40
- testInfo: testInfoValue,
41
- globalRunId: row.global_run_id,
42
- runId: row.run_id,
43
- createdAt: row.created_at
44
- };
27
+ return filters;
45
28
  }
46
- var LegacyEvalsMSSQL = class extends LegacyEvalsStorage {
47
- pool;
48
- schema;
49
- constructor({ pool, schema }) {
50
- super();
51
- this.pool = pool;
52
- this.schema = schema;
53
- }
54
- /** @deprecated use getEvals instead */
55
- async getEvalsByAgentName(agentName, type) {
56
- try {
57
- let query = `SELECT * FROM ${getTableName({ indexName: TABLE_EVALS, schemaName: getSchemaName(this.schema) })} WHERE agent_name = @p1`;
58
- if (type === "test") {
59
- query += " AND test_info IS NOT NULL AND JSON_VALUE(test_info, '$.testPath') IS NOT NULL";
60
- } else if (type === "live") {
61
- query += " AND (test_info IS NULL OR JSON_VALUE(test_info, '$.testPath') IS NULL)";
62
- }
63
- query += " ORDER BY created_at DESC";
64
- const request = this.pool.request();
65
- request.input("p1", agentName);
66
- const result = await request.query(query);
67
- const rows = result.recordset;
68
- return typeof transformEvalRow === "function" ? rows?.map((row) => transformEvalRow(row)) ?? [] : rows ?? [];
69
- } catch (error) {
70
- if (error && error.number === 208 && error.message && error.message.includes("Invalid object name")) {
71
- return [];
72
- }
73
- console.error("Failed to get evals for the specified agent: " + error?.message);
74
- throw error;
75
- }
76
- }
77
- async getEvals(options = {}) {
78
- const { agentName, type, page = 0, perPage = 100, dateRange } = options;
79
- const fromDate = dateRange?.start;
80
- const toDate = dateRange?.end;
81
- const where = [];
82
- const params = {};
83
- if (agentName) {
84
- where.push("agent_name = @agentName");
85
- params["agentName"] = agentName;
86
- }
87
- if (type === "test") {
88
- where.push("test_info IS NOT NULL AND JSON_VALUE(test_info, '$.testPath') IS NOT NULL");
89
- } else if (type === "live") {
90
- where.push("(test_info IS NULL OR JSON_VALUE(test_info, '$.testPath') IS NULL)");
91
- }
92
- if (fromDate instanceof Date && !isNaN(fromDate.getTime())) {
93
- where.push(`[created_at] >= @fromDate`);
94
- params[`fromDate`] = fromDate.toISOString();
95
- }
96
- if (toDate instanceof Date && !isNaN(toDate.getTime())) {
97
- where.push(`[created_at] <= @toDate`);
98
- params[`toDate`] = toDate.toISOString();
99
- }
100
- const whereClause = where.length > 0 ? `WHERE ${where.join(" AND ")}` : "";
101
- const tableName = getTableName({ indexName: TABLE_EVALS, schemaName: getSchemaName(this.schema) });
102
- const offset = page * perPage;
103
- const countQuery = `SELECT COUNT(*) as total FROM ${tableName} ${whereClause}`;
104
- const dataQuery = `SELECT * FROM ${tableName} ${whereClause} ORDER BY seq_id DESC OFFSET @offset ROWS FETCH NEXT @perPage ROWS ONLY`;
105
- try {
106
- const countReq = this.pool.request();
107
- Object.entries(params).forEach(([key, value]) => {
108
- if (value instanceof Date) {
109
- countReq.input(key, sql2.DateTime, value);
110
- } else {
111
- countReq.input(key, value);
29
+ function isInOperator(value) {
30
+ return typeof value === "object" && value !== null && "$in" in value && Array.isArray(value.$in);
31
+ }
32
+ function prepareWhereClause(filters, _schema) {
33
+ const conditions = [];
34
+ const params = {};
35
+ let paramIndex = 1;
36
+ Object.entries(filters).forEach(([key, value]) => {
37
+ if (value === void 0) return;
38
+ if (key.endsWith("_gte")) {
39
+ const paramName = `p${paramIndex++}`;
40
+ const fieldName = key.slice(0, -4);
41
+ conditions.push(`[${parseSqlIdentifier(fieldName, "field name")}] >= @${paramName}`);
42
+ params[paramName] = value instanceof Date ? value.toISOString() : value;
43
+ } else if (key.endsWith("_lte")) {
44
+ const paramName = `p${paramIndex++}`;
45
+ const fieldName = key.slice(0, -4);
46
+ conditions.push(`[${parseSqlIdentifier(fieldName, "field name")}] <= @${paramName}`);
47
+ params[paramName] = value instanceof Date ? value.toISOString() : value;
48
+ } else if (value === null) {
49
+ conditions.push(`[${parseSqlIdentifier(key, "field name")}] IS NULL`);
50
+ } else if (isInOperator(value)) {
51
+ const inValues = value.$in;
52
+ if (inValues.length === 0) {
53
+ conditions.push("1 = 0");
54
+ } else if (inValues.length === 1) {
55
+ const paramName = `p${paramIndex++}`;
56
+ conditions.push(`[${parseSqlIdentifier(key, "field name")}] = @${paramName}`);
57
+ params[paramName] = inValues[0] instanceof Date ? inValues[0].toISOString() : inValues[0];
58
+ } else {
59
+ const inParamNames = [];
60
+ for (const item of inValues) {
61
+ const paramName = `p${paramIndex++}`;
62
+ inParamNames.push(`@${paramName}`);
63
+ params[paramName] = item instanceof Date ? item.toISOString() : item;
112
64
  }
113
- });
114
- const countResult = await countReq.query(countQuery);
115
- const total = countResult.recordset[0]?.total || 0;
116
- if (total === 0) {
117
- return {
118
- evals: [],
119
- total: 0,
120
- page,
121
- perPage,
122
- hasMore: false
123
- };
65
+ conditions.push(`[${parseSqlIdentifier(key, "field name")}] IN (${inParamNames.join(", ")})`);
124
66
  }
125
- const req = this.pool.request();
126
- Object.entries(params).forEach(([key, value]) => {
127
- if (value instanceof Date) {
128
- req.input(key, sql2.DateTime, value);
129
- } else {
130
- req.input(key, value);
67
+ } else if (Array.isArray(value)) {
68
+ if (value.length === 0) {
69
+ conditions.push("1 = 0");
70
+ } else if (value.length === 1) {
71
+ const paramName = `p${paramIndex++}`;
72
+ conditions.push(`[${parseSqlIdentifier(key, "field name")}] = @${paramName}`);
73
+ params[paramName] = value[0] instanceof Date ? value[0].toISOString() : value[0];
74
+ } else {
75
+ const inParamNames = [];
76
+ for (const item of value) {
77
+ const paramName = `p${paramIndex++}`;
78
+ inParamNames.push(`@${paramName}`);
79
+ params[paramName] = item instanceof Date ? item.toISOString() : item;
131
80
  }
132
- });
133
- req.input("offset", offset);
134
- req.input("perPage", perPage);
135
- const result = await req.query(dataQuery);
136
- const rows = result.recordset;
137
- return {
138
- evals: rows?.map((row) => transformEvalRow(row)) ?? [],
139
- total,
140
- page,
141
- perPage,
142
- hasMore: offset + (rows?.length ?? 0) < total
143
- };
144
- } catch (error) {
145
- const mastraError = new MastraError(
146
- {
147
- id: "MASTRA_STORAGE_MSSQL_STORE_GET_EVALS_FAILED",
148
- domain: ErrorDomain.STORAGE,
149
- category: ErrorCategory.THIRD_PARTY,
150
- details: {
151
- agentName: agentName || "all",
152
- type: type || "all",
153
- page,
154
- perPage
155
- }
156
- },
157
- error
158
- );
159
- this.logger?.error?.(mastraError.toString());
160
- this.logger?.trackException(mastraError);
161
- throw mastraError;
81
+ conditions.push(`[${parseSqlIdentifier(key, "field name")}] IN (${inParamNames.join(", ")})`);
82
+ }
83
+ } else {
84
+ const paramName = `p${paramIndex++}`;
85
+ conditions.push(`[${parseSqlIdentifier(key, "field name")}] = @${paramName}`);
86
+ params[paramName] = value instanceof Date ? value.toISOString() : value;
162
87
  }
163
- }
164
- };
88
+ });
89
+ return {
90
+ sql: conditions.length > 0 ? ` WHERE ${conditions.join(" AND ")}` : "",
91
+ params
92
+ };
93
+ }
94
+ function transformFromSqlRow({
95
+ tableName,
96
+ sqlRow
97
+ }) {
98
+ const schema = TABLE_SCHEMAS[tableName];
99
+ const result = {};
100
+ Object.entries(sqlRow).forEach(([key, value]) => {
101
+ const columnSchema = schema?.[key];
102
+ if (columnSchema?.type === "jsonb" && typeof value === "string") {
103
+ try {
104
+ result[key] = JSON.parse(value);
105
+ } catch {
106
+ result[key] = value;
107
+ }
108
+ } else if (columnSchema?.type === "timestamp" && value && typeof value === "string") {
109
+ result[key] = new Date(value);
110
+ } else if (columnSchema?.type === "timestamp" && value instanceof Date) {
111
+ result[key] = value;
112
+ } else if (columnSchema?.type === "boolean") {
113
+ result[key] = Boolean(value);
114
+ } else {
115
+ result[key] = value;
116
+ }
117
+ });
118
+ return result;
119
+ }
120
+
121
+ // src/storage/domains/memory/index.ts
165
122
  var MemoryMSSQL = class extends MemoryStorage {
166
123
  pool;
167
124
  schema;
@@ -179,7 +136,7 @@ var MemoryMSSQL = class extends MemoryStorage {
179
136
  });
180
137
  const cleanMessages = messagesWithParsedContent.map(({ seq_id, ...rest }) => rest);
181
138
  const list = new MessageList().add(cleanMessages, "memory");
182
- return format === "v2" ? list.get.all.v2() : list.get.all.v1();
139
+ return format === "v2" ? list.get.all.db() : list.get.all.v1();
183
140
  }
184
141
  constructor({
185
142
  pool,
@@ -193,7 +150,7 @@ var MemoryMSSQL = class extends MemoryStorage {
193
150
  }
194
151
  async getThreadById({ threadId }) {
195
152
  try {
196
- const sql7 = `SELECT
153
+ const sql5 = `SELECT
197
154
  id,
198
155
  [resourceId],
199
156
  title,
@@ -204,7 +161,7 @@ var MemoryMSSQL = class extends MemoryStorage {
204
161
  WHERE id = @threadId`;
205
162
  const request = this.pool.request();
206
163
  request.input("threadId", threadId);
207
- const resultSet = await request.query(sql7);
164
+ const resultSet = await request.query(sql5);
208
165
  const thread = resultSet.recordset[0] || null;
209
166
  if (!thread) {
210
167
  return null;
@@ -218,7 +175,7 @@ var MemoryMSSQL = class extends MemoryStorage {
218
175
  } catch (error) {
219
176
  throw new MastraError(
220
177
  {
221
- id: "MASTRA_STORAGE_MSSQL_STORE_GET_THREAD_BY_ID_FAILED",
178
+ id: createStorageErrorId("MSSQL", "GET_THREAD_BY_ID", "FAILED"),
222
179
  domain: ErrorDomain.STORAGE,
223
180
  category: ErrorCategory.THIRD_PARTY,
224
181
  details: {
@@ -229,11 +186,24 @@ var MemoryMSSQL = class extends MemoryStorage {
229
186
  );
230
187
  }
231
188
  }
232
- async getThreadsByResourceIdPaginated(args) {
233
- const { resourceId, page = 0, perPage: perPageInput, orderBy = "createdAt", sortDirection = "DESC" } = args;
189
+ async listThreadsByResourceId(args) {
190
+ const { resourceId, page = 0, perPage: perPageInput, orderBy } = args;
191
+ if (page < 0) {
192
+ throw new MastraError({
193
+ id: createStorageErrorId("MSSQL", "LIST_THREADS_BY_RESOURCE_ID", "INVALID_PAGE"),
194
+ domain: ErrorDomain.STORAGE,
195
+ category: ErrorCategory.USER,
196
+ text: "Page number must be non-negative",
197
+ details: {
198
+ resourceId,
199
+ page
200
+ }
201
+ });
202
+ }
203
+ const perPage = normalizePerPage(perPageInput, 100);
204
+ const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
205
+ const { field, direction } = this.parseOrderBy(orderBy);
234
206
  try {
235
- const perPage = perPageInput !== void 0 ? perPageInput : 100;
236
- const currentOffset = page * perPage;
237
207
  const baseQuery = `FROM ${getTableName({ indexName: TABLE_THREADS, schemaName: getSchemaName(this.schema) })} WHERE [resourceId] = @resourceId`;
238
208
  const countQuery = `SELECT COUNT(*) as count ${baseQuery}`;
239
209
  const countRequest = this.pool.request();
@@ -245,16 +215,22 @@ var MemoryMSSQL = class extends MemoryStorage {
245
215
  threads: [],
246
216
  total: 0,
247
217
  page,
248
- perPage,
218
+ perPage: perPageForResponse,
249
219
  hasMore: false
250
220
  };
251
221
  }
252
- const orderByField = orderBy === "createdAt" ? "[createdAt]" : "[updatedAt]";
253
- const dataQuery = `SELECT id, [resourceId], title, metadata, [createdAt], [updatedAt] ${baseQuery} ORDER BY ${orderByField} ${sortDirection} OFFSET @offset ROWS FETCH NEXT @perPage ROWS ONLY`;
222
+ const orderByField = field === "createdAt" ? "[createdAt]" : "[updatedAt]";
223
+ const dir = (direction || "DESC").toUpperCase() === "ASC" ? "ASC" : "DESC";
224
+ const limitValue = perPageInput === false ? total : perPage;
225
+ const dataQuery = `SELECT id, [resourceId], title, metadata, [createdAt], [updatedAt] ${baseQuery} ORDER BY ${orderByField} ${dir} OFFSET @offset ROWS FETCH NEXT @perPage ROWS ONLY`;
254
226
  const dataRequest = this.pool.request();
255
227
  dataRequest.input("resourceId", resourceId);
256
- dataRequest.input("perPage", perPage);
257
- dataRequest.input("offset", currentOffset);
228
+ dataRequest.input("offset", offset);
229
+ if (limitValue > 2147483647) {
230
+ dataRequest.input("perPage", sql3.BigInt, limitValue);
231
+ } else {
232
+ dataRequest.input("perPage", limitValue);
233
+ }
258
234
  const rowsResult = await dataRequest.query(dataQuery);
259
235
  const rows = rowsResult.recordset || [];
260
236
  const threads = rows.map((thread) => ({
@@ -267,13 +243,13 @@ var MemoryMSSQL = class extends MemoryStorage {
267
243
  threads,
268
244
  total,
269
245
  page,
270
- perPage,
271
- hasMore: currentOffset + threads.length < total
246
+ perPage: perPageForResponse,
247
+ hasMore: perPageInput === false ? false : offset + perPage < total
272
248
  };
273
249
  } catch (error) {
274
250
  const mastraError = new MastraError(
275
251
  {
276
- id: "MASTRA_STORAGE_MSSQL_STORE_GET_THREADS_BY_RESOURCE_ID_PAGINATED_FAILED",
252
+ id: createStorageErrorId("MSSQL", "LIST_THREADS_BY_RESOURCE_ID", "FAILED"),
277
253
  domain: ErrorDomain.STORAGE,
278
254
  category: ErrorCategory.THIRD_PARTY,
279
255
  details: {
@@ -285,7 +261,13 @@ var MemoryMSSQL = class extends MemoryStorage {
285
261
  );
286
262
  this.logger?.error?.(mastraError.toString());
287
263
  this.logger?.trackException?.(mastraError);
288
- return { threads: [], total: 0, page, perPage: perPageInput || 100, hasMore: false };
264
+ return {
265
+ threads: [],
266
+ total: 0,
267
+ page,
268
+ perPage: perPageForResponse,
269
+ hasMore: false
270
+ };
289
271
  }
290
272
  }
291
273
  async saveThread({ thread }) {
@@ -307,15 +289,20 @@ var MemoryMSSQL = class extends MemoryStorage {
307
289
  req.input("id", thread.id);
308
290
  req.input("resourceId", thread.resourceId);
309
291
  req.input("title", thread.title);
310
- req.input("metadata", thread.metadata ? JSON.stringify(thread.metadata) : null);
311
- req.input("createdAt", sql2.DateTime2, thread.createdAt);
312
- req.input("updatedAt", sql2.DateTime2, thread.updatedAt);
292
+ const metadata = thread.metadata ? JSON.stringify(thread.metadata) : null;
293
+ if (metadata === null) {
294
+ req.input("metadata", sql3.NVarChar, null);
295
+ } else {
296
+ req.input("metadata", metadata);
297
+ }
298
+ req.input("createdAt", sql3.DateTime2, thread.createdAt);
299
+ req.input("updatedAt", sql3.DateTime2, thread.updatedAt);
313
300
  await req.query(mergeSql);
314
301
  return thread;
315
302
  } catch (error) {
316
303
  throw new MastraError(
317
304
  {
318
- id: "MASTRA_STORAGE_MSSQL_STORE_SAVE_THREAD_FAILED",
305
+ id: createStorageErrorId("MSSQL", "SAVE_THREAD", "FAILED"),
319
306
  domain: ErrorDomain.STORAGE,
320
307
  category: ErrorCategory.THIRD_PARTY,
321
308
  details: {
@@ -326,30 +313,6 @@ var MemoryMSSQL = class extends MemoryStorage {
326
313
  );
327
314
  }
328
315
  }
329
- /**
330
- * @deprecated use getThreadsByResourceIdPaginated instead
331
- */
332
- async getThreadsByResourceId(args) {
333
- const { resourceId, orderBy = "createdAt", sortDirection = "DESC" } = args;
334
- try {
335
- const baseQuery = `FROM ${getTableName({ indexName: TABLE_THREADS, schemaName: getSchemaName(this.schema) })} WHERE [resourceId] = @resourceId`;
336
- const orderByField = orderBy === "createdAt" ? "[createdAt]" : "[updatedAt]";
337
- const dataQuery = `SELECT id, [resourceId], title, metadata, [createdAt], [updatedAt] ${baseQuery} ORDER BY ${orderByField} ${sortDirection}`;
338
- const request = this.pool.request();
339
- request.input("resourceId", resourceId);
340
- const resultSet = await request.query(dataQuery);
341
- const rows = resultSet.recordset || [];
342
- return rows.map((thread) => ({
343
- ...thread,
344
- metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
345
- createdAt: thread.createdAt,
346
- updatedAt: thread.updatedAt
347
- }));
348
- } catch (error) {
349
- this.logger?.error?.(`Error getting threads for resource ${resourceId}:`, error);
350
- return [];
351
- }
352
- }
353
316
  /**
354
317
  * Updates a thread's title and metadata, merging with existing metadata. Returns the updated thread.
355
318
  */
@@ -361,7 +324,7 @@ var MemoryMSSQL = class extends MemoryStorage {
361
324
  const existingThread = await this.getThreadById({ threadId: id });
362
325
  if (!existingThread) {
363
326
  throw new MastraError({
364
- id: "MASTRA_STORAGE_MSSQL_STORE_UPDATE_THREAD_FAILED",
327
+ id: createStorageErrorId("MSSQL", "UPDATE_THREAD", "NOT_FOUND"),
365
328
  domain: ErrorDomain.STORAGE,
366
329
  category: ErrorCategory.USER,
367
330
  text: `Thread ${id} not found`,
@@ -377,7 +340,7 @@ var MemoryMSSQL = class extends MemoryStorage {
377
340
  };
378
341
  try {
379
342
  const table = getTableName({ indexName: TABLE_THREADS, schemaName: getSchemaName(this.schema) });
380
- const sql7 = `UPDATE ${table}
343
+ const sql5 = `UPDATE ${table}
381
344
  SET title = @title,
382
345
  metadata = @metadata,
383
346
  [updatedAt] = @updatedAt
@@ -388,7 +351,7 @@ var MemoryMSSQL = class extends MemoryStorage {
388
351
  req.input("title", title);
389
352
  req.input("metadata", JSON.stringify(mergedMetadata));
390
353
  req.input("updatedAt", /* @__PURE__ */ new Date());
391
- const result = await req.query(sql7);
354
+ const result = await req.query(sql5);
392
355
  let thread = result.recordset && result.recordset[0];
393
356
  if (thread && "seq_id" in thread) {
394
357
  const { seq_id, ...rest } = thread;
@@ -396,7 +359,7 @@ var MemoryMSSQL = class extends MemoryStorage {
396
359
  }
397
360
  if (!thread) {
398
361
  throw new MastraError({
399
- id: "MASTRA_STORAGE_MSSQL_STORE_UPDATE_THREAD_FAILED",
362
+ id: createStorageErrorId("MSSQL", "UPDATE_THREAD", "NOT_FOUND"),
400
363
  domain: ErrorDomain.STORAGE,
401
364
  category: ErrorCategory.USER,
402
365
  text: `Thread ${id} not found after update`,
@@ -415,7 +378,7 @@ var MemoryMSSQL = class extends MemoryStorage {
415
378
  } catch (error) {
416
379
  throw new MastraError(
417
380
  {
418
- id: "MASTRA_STORAGE_MSSQL_STORE_UPDATE_THREAD_FAILED",
381
+ id: createStorageErrorId("MSSQL", "UPDATE_THREAD", "FAILED"),
419
382
  domain: ErrorDomain.STORAGE,
420
383
  category: ErrorCategory.THIRD_PARTY,
421
384
  details: {
@@ -445,7 +408,7 @@ var MemoryMSSQL = class extends MemoryStorage {
445
408
  });
446
409
  throw new MastraError(
447
410
  {
448
- id: "MASTRA_STORAGE_MSSQL_STORE_DELETE_THREAD_FAILED",
411
+ id: createStorageErrorId("MSSQL", "DELETE_THREAD", "FAILED"),
449
412
  domain: ErrorDomain.STORAGE,
450
413
  category: ErrorCategory.THIRD_PARTY,
451
414
  details: {
@@ -456,24 +419,18 @@ var MemoryMSSQL = class extends MemoryStorage {
456
419
  );
457
420
  }
458
421
  }
459
- async _getIncludedMessages({
460
- threadId,
461
- selectBy,
462
- orderByStatement
463
- }) {
464
- const include = selectBy?.include;
465
- if (!include) return null;
422
+ async _getIncludedMessages({ include }) {
423
+ if (!include || include.length === 0) return null;
466
424
  const unionQueries = [];
467
425
  const paramValues = [];
468
426
  let paramIdx = 1;
469
427
  const paramNames = [];
428
+ const tableName = getTableName({ indexName: TABLE_MESSAGES, schemaName: getSchemaName(this.schema) });
470
429
  for (const inc of include) {
471
430
  const { id, withPreviousMessages = 0, withNextMessages = 0 } = inc;
472
- const searchId = inc.threadId || threadId;
473
- const pThreadId = `@p${paramIdx}`;
474
- const pId = `@p${paramIdx + 1}`;
475
- const pPrev = `@p${paramIdx + 2}`;
476
- const pNext = `@p${paramIdx + 3}`;
431
+ const pId = `@p${paramIdx}`;
432
+ const pPrev = `@p${paramIdx + 1}`;
433
+ const pNext = `@p${paramIdx + 2}`;
477
434
  unionQueries.push(
478
435
  `
479
436
  SELECT
@@ -486,30 +443,32 @@ var MemoryMSSQL = class extends MemoryStorage {
486
443
  m.[resourceId],
487
444
  m.seq_id
488
445
  FROM (
489
- SELECT *, ROW_NUMBER() OVER (${orderByStatement}) as row_num
490
- FROM ${getTableName({ indexName: TABLE_MESSAGES, schemaName: getSchemaName(this.schema) })}
491
- WHERE [thread_id] = ${pThreadId}
446
+ SELECT *, ROW_NUMBER() OVER (ORDER BY [createdAt] ASC) as row_num
447
+ FROM ${tableName}
448
+ WHERE [thread_id] = (SELECT thread_id FROM ${tableName} WHERE id = ${pId})
492
449
  ) AS m
493
450
  WHERE m.id = ${pId}
494
451
  OR EXISTS (
495
452
  SELECT 1
496
453
  FROM (
497
- SELECT *, ROW_NUMBER() OVER (${orderByStatement}) as row_num
498
- FROM ${getTableName({ indexName: TABLE_MESSAGES, schemaName: getSchemaName(this.schema) })}
499
- WHERE [thread_id] = ${pThreadId}
454
+ SELECT *, ROW_NUMBER() OVER (ORDER BY [createdAt] ASC) as row_num
455
+ FROM ${tableName}
456
+ WHERE [thread_id] = (SELECT thread_id FROM ${tableName} WHERE id = ${pId})
500
457
  ) AS target
501
458
  WHERE target.id = ${pId}
502
459
  AND (
503
- (m.row_num <= target.row_num + ${pPrev} AND m.row_num > target.row_num)
460
+ -- Get previous messages (messages that come BEFORE the target)
461
+ (m.row_num < target.row_num AND m.row_num >= target.row_num - ${pPrev})
504
462
  OR
505
- (m.row_num >= target.row_num - ${pNext} AND m.row_num < target.row_num)
463
+ -- Get next messages (messages that come AFTER the target)
464
+ (m.row_num > target.row_num AND m.row_num <= target.row_num + ${pNext})
506
465
  )
507
466
  )
508
467
  `
509
468
  );
510
- paramValues.push(searchId, id, withPreviousMessages, withNextMessages);
511
- paramNames.push(`p${paramIdx}`, `p${paramIdx + 1}`, `p${paramIdx + 2}`, `p${paramIdx + 3}`);
512
- paramIdx += 4;
469
+ paramValues.push(id, withPreviousMessages, withNextMessages);
470
+ paramNames.push(`p${paramIdx}`, `p${paramIdx + 1}`, `p${paramIdx + 2}`);
471
+ paramIdx += 3;
513
472
  }
514
473
  const finalQuery = `
515
474
  SELECT * FROM (
@@ -531,33 +490,16 @@ var MemoryMSSQL = class extends MemoryStorage {
531
490
  });
532
491
  return dedupedRows;
533
492
  }
534
- async getMessages(args) {
535
- const { threadId, format, selectBy } = args;
493
+ async listMessagesById({ messageIds }) {
494
+ if (messageIds.length === 0) return { messages: [] };
536
495
  const selectStatement = `SELECT seq_id, id, content, role, type, [createdAt], thread_id AS threadId, resourceId`;
537
496
  const orderByStatement = `ORDER BY [seq_id] DESC`;
538
- const limit = resolveMessageLimit({ last: selectBy?.last, defaultLimit: 40 });
539
497
  try {
540
498
  let rows = [];
541
- const include = selectBy?.include || [];
542
- if (include?.length) {
543
- const includeMessages = await this._getIncludedMessages({ threadId, selectBy, orderByStatement });
544
- if (includeMessages) {
545
- rows.push(...includeMessages);
546
- }
547
- }
548
- const excludeIds = rows.map((m) => m.id).filter(Boolean);
549
- let query = `${selectStatement} FROM ${getTableName({ indexName: TABLE_MESSAGES, schemaName: getSchemaName(this.schema) })} WHERE [thread_id] = @threadId`;
499
+ let query = `${selectStatement} FROM ${getTableName({ indexName: TABLE_MESSAGES, schemaName: getSchemaName(this.schema) })} WHERE [id] IN (${messageIds.map((_, i) => `@id${i}`).join(", ")})`;
550
500
  const request = this.pool.request();
551
- request.input("threadId", threadId);
552
- if (excludeIds.length > 0) {
553
- const excludeParams = excludeIds.map((_, idx) => `@id${idx}`);
554
- query += ` AND id NOT IN (${excludeParams.join(", ")})`;
555
- excludeIds.forEach((id, idx) => {
556
- request.input(`id${idx}`, id);
557
- });
558
- }
559
- query += ` ${orderByStatement} OFFSET 0 ROWS FETCH NEXT @limit ROWS ONLY`;
560
- request.input("limit", limit);
501
+ messageIds.forEach((id, i) => request.input(`id${i}`, id));
502
+ query += ` ${orderByStatement}`;
561
503
  const result = await request.query(query);
562
504
  const remainingRows = result.recordset || [];
563
505
  rows.push(...remainingRows);
@@ -565,120 +507,177 @@ var MemoryMSSQL = class extends MemoryStorage {
565
507
  const timeDiff = a.seq_id - b.seq_id;
566
508
  return timeDiff;
567
509
  });
568
- rows = rows.map(({ seq_id, ...rest }) => rest);
569
- return this._parseAndFormatMessages(rows, format);
510
+ const messagesWithParsedContent = rows.map((row) => {
511
+ if (typeof row.content === "string") {
512
+ try {
513
+ return { ...row, content: JSON.parse(row.content) };
514
+ } catch {
515
+ return row;
516
+ }
517
+ }
518
+ return row;
519
+ });
520
+ const cleanMessages = messagesWithParsedContent.map(({ seq_id, ...rest }) => rest);
521
+ const list = new MessageList().add(cleanMessages, "memory");
522
+ return { messages: list.get.all.db() };
570
523
  } catch (error) {
571
524
  const mastraError = new MastraError(
572
525
  {
573
- id: "MASTRA_STORAGE_MSSQL_STORE_GET_MESSAGES_FAILED",
526
+ id: createStorageErrorId("MSSQL", "LIST_MESSAGES_BY_ID", "FAILED"),
574
527
  domain: ErrorDomain.STORAGE,
575
528
  category: ErrorCategory.THIRD_PARTY,
576
529
  details: {
577
- threadId
530
+ messageIds: JSON.stringify(messageIds)
578
531
  }
579
532
  },
580
533
  error
581
534
  );
582
535
  this.logger?.error?.(mastraError.toString());
583
- this.logger?.trackException(mastraError);
584
- return [];
536
+ this.logger?.trackException?.(mastraError);
537
+ return { messages: [] };
585
538
  }
586
539
  }
587
- async getMessagesPaginated(args) {
588
- const { threadId, selectBy } = args;
589
- const { page = 0, perPage: perPageInput } = selectBy?.pagination || {};
590
- const orderByStatement = `ORDER BY [seq_id] DESC`;
591
- if (selectBy?.include?.length) {
592
- await this._getIncludedMessages({ threadId, selectBy, orderByStatement });
540
+ async listMessages(args) {
541
+ const { threadId, resourceId, include, filter, perPage: perPageInput, page = 0, orderBy } = args;
542
+ const threadIds = Array.isArray(threadId) ? threadId : [threadId];
543
+ if (threadIds.length === 0 || threadIds.some((id) => !id.trim())) {
544
+ throw new MastraError(
545
+ {
546
+ id: createStorageErrorId("MSSQL", "LIST_MESSAGES", "INVALID_THREAD_ID"),
547
+ domain: ErrorDomain.STORAGE,
548
+ category: ErrorCategory.THIRD_PARTY,
549
+ details: { threadId: Array.isArray(threadId) ? threadId.join(",") : threadId }
550
+ },
551
+ new Error("threadId must be a non-empty string or array of non-empty strings")
552
+ );
553
+ }
554
+ if (page < 0) {
555
+ throw new MastraError({
556
+ id: createStorageErrorId("MSSQL", "LIST_MESSAGES", "INVALID_PAGE"),
557
+ domain: ErrorDomain.STORAGE,
558
+ category: ErrorCategory.USER,
559
+ text: "Page number must be non-negative",
560
+ details: {
561
+ threadId: Array.isArray(threadId) ? threadId.join(",") : threadId,
562
+ page
563
+ }
564
+ });
593
565
  }
566
+ const perPage = normalizePerPage(perPageInput, 40);
567
+ const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
594
568
  try {
595
- const { threadId: threadId2, format, selectBy: selectBy2 } = args;
596
- const { page: page2 = 0, perPage: perPageInput2, dateRange } = selectBy2?.pagination || {};
597
- const fromDate = dateRange?.start;
598
- const toDate = dateRange?.end;
599
- const selectStatement = `SELECT seq_id, id, content, role, type, [createdAt], thread_id AS threadId, resourceId`;
600
- const orderByStatement2 = `ORDER BY [seq_id] DESC`;
601
- let messages2 = [];
602
- if (selectBy2?.include?.length) {
603
- const includeMessages = await this._getIncludedMessages({ threadId: threadId2, selectBy: selectBy2, orderByStatement: orderByStatement2 });
604
- if (includeMessages) messages2.push(...includeMessages);
605
- }
606
- const perPage = perPageInput2 !== void 0 ? perPageInput2 : resolveMessageLimit({ last: selectBy2?.last, defaultLimit: 40 });
607
- const currentOffset = page2 * perPage;
608
- const conditions = ["[thread_id] = @threadId"];
609
- const request = this.pool.request();
610
- request.input("threadId", threadId2);
611
- if (fromDate instanceof Date && !isNaN(fromDate.getTime())) {
612
- conditions.push("[createdAt] >= @fromDate");
613
- request.input("fromDate", fromDate.toISOString());
614
- }
615
- if (toDate instanceof Date && !isNaN(toDate.getTime())) {
616
- conditions.push("[createdAt] <= @toDate");
617
- request.input("toDate", toDate.toISOString());
618
- }
619
- const whereClause = `WHERE ${conditions.join(" AND ")}`;
620
- const countQuery = `SELECT COUNT(*) as total FROM ${getTableName({ indexName: TABLE_MESSAGES, schemaName: getSchemaName(this.schema) })} ${whereClause}`;
621
- const countResult = await request.query(countQuery);
569
+ const { field, direction } = this.parseOrderBy(orderBy, "ASC");
570
+ const orderByStatement = `ORDER BY [${field}] ${direction}, [seq_id] ${direction}`;
571
+ const tableName = getTableName({ indexName: TABLE_MESSAGES, schemaName: getSchemaName(this.schema) });
572
+ const baseQuery = `SELECT seq_id, id, content, role, type, [createdAt], thread_id AS threadId, resourceId FROM ${tableName}`;
573
+ const filters = {
574
+ thread_id: threadIds.length === 1 ? threadIds[0] : { $in: threadIds },
575
+ ...resourceId ? { resourceId } : {},
576
+ ...buildDateRangeFilter(filter?.dateRange, "createdAt")
577
+ };
578
+ const { sql: actualWhereClause = "", params: whereParams } = prepareWhereClause(
579
+ filters);
580
+ const bindWhereParams = (req) => {
581
+ Object.entries(whereParams).forEach(([paramName, paramValue]) => req.input(paramName, paramValue));
582
+ };
583
+ const countRequest = this.pool.request();
584
+ bindWhereParams(countRequest);
585
+ const countResult = await countRequest.query(`SELECT COUNT(*) as total FROM ${tableName}${actualWhereClause}`);
622
586
  const total = parseInt(countResult.recordset[0]?.total, 10) || 0;
623
- if (total === 0 && messages2.length > 0) {
624
- const parsedIncluded = this._parseAndFormatMessages(messages2, format);
587
+ const fetchBaseMessages = async () => {
588
+ const request = this.pool.request();
589
+ bindWhereParams(request);
590
+ if (perPageInput === false) {
591
+ const result2 = await request.query(`${baseQuery}${actualWhereClause} ${orderByStatement}`);
592
+ return result2.recordset || [];
593
+ }
594
+ request.input("offset", offset);
595
+ request.input("limit", perPage > 2147483647 ? sql3.BigInt : sql3.Int, perPage);
596
+ const result = await request.query(
597
+ `${baseQuery}${actualWhereClause} ${orderByStatement} OFFSET @offset ROWS FETCH NEXT @limit ROWS ONLY`
598
+ );
599
+ return result.recordset || [];
600
+ };
601
+ const baseRows = perPage === 0 ? [] : await fetchBaseMessages();
602
+ const messages = [...baseRows];
603
+ const seqById = /* @__PURE__ */ new Map();
604
+ messages.forEach((msg) => {
605
+ if (typeof msg.seq_id === "number") seqById.set(msg.id, msg.seq_id);
606
+ });
607
+ if (total === 0 && messages.length === 0 && (!include || include.length === 0)) {
625
608
  return {
626
- messages: parsedIncluded,
627
- total: parsedIncluded.length,
628
- page: page2,
629
- perPage,
609
+ messages: [],
610
+ total: 0,
611
+ page,
612
+ perPage: perPageForResponse,
630
613
  hasMore: false
631
614
  };
632
615
  }
633
- const excludeIds = messages2.map((m) => m.id);
634
- if (excludeIds.length > 0) {
635
- const excludeParams = excludeIds.map((_, idx) => `@id${idx}`);
636
- conditions.push(`id NOT IN (${excludeParams.join(", ")})`);
637
- excludeIds.forEach((id, idx) => request.input(`id${idx}`, id));
638
- }
639
- const finalWhereClause = `WHERE ${conditions.join(" AND ")}`;
640
- const dataQuery = `${selectStatement} FROM ${getTableName({ indexName: TABLE_MESSAGES, schemaName: getSchemaName(this.schema) })} ${finalWhereClause} ${orderByStatement2} OFFSET @offset ROWS FETCH NEXT @limit ROWS ONLY`;
641
- request.input("offset", currentOffset);
642
- request.input("limit", perPage);
643
- const rowsResult = await request.query(dataQuery);
644
- const rows = rowsResult.recordset || [];
645
- rows.sort((a, b) => a.seq_id - b.seq_id);
646
- messages2.push(...rows);
647
- const parsed = this._parseAndFormatMessages(messages2, format);
616
+ if (include?.length) {
617
+ const messageIds = new Set(messages.map((m) => m.id));
618
+ const includeMessages = await this._getIncludedMessages({ include });
619
+ includeMessages?.forEach((msg) => {
620
+ if (!messageIds.has(msg.id)) {
621
+ messages.push(msg);
622
+ messageIds.add(msg.id);
623
+ if (typeof msg.seq_id === "number") seqById.set(msg.id, msg.seq_id);
624
+ }
625
+ });
626
+ }
627
+ const parsed = this._parseAndFormatMessages(messages, "v2");
628
+ const mult = direction === "ASC" ? 1 : -1;
629
+ const finalMessages = parsed.sort((a, b) => {
630
+ const aVal = field === "createdAt" ? new Date(a.createdAt).getTime() : a[field];
631
+ const bVal = field === "createdAt" ? new Date(b.createdAt).getTime() : b[field];
632
+ if (aVal == null || bVal == null) {
633
+ return aVal == null && bVal == null ? a.id.localeCompare(b.id) : aVal == null ? 1 : -1;
634
+ }
635
+ const diff = (typeof aVal === "number" && typeof bVal === "number" ? aVal - bVal : String(aVal).localeCompare(String(bVal))) * mult;
636
+ if (diff !== 0) return diff;
637
+ const seqA = seqById.get(a.id);
638
+ const seqB = seqById.get(b.id);
639
+ return seqA != null && seqB != null ? (seqA - seqB) * mult : a.id.localeCompare(b.id);
640
+ });
641
+ const threadIdSet = new Set(threadIds);
642
+ const returnedThreadMessageCount = finalMessages.filter((m) => m.threadId && threadIdSet.has(m.threadId)).length;
643
+ const hasMore = perPageInput !== false && returnedThreadMessageCount < total && offset + perPage < total;
648
644
  return {
649
- messages: parsed,
650
- total: total + excludeIds.length,
651
- page: page2,
652
- perPage,
653
- hasMore: currentOffset + rows.length < total
645
+ messages: finalMessages,
646
+ total,
647
+ page,
648
+ perPage: perPageForResponse,
649
+ hasMore
654
650
  };
655
651
  } catch (error) {
656
652
  const mastraError = new MastraError(
657
653
  {
658
- id: "MASTRA_STORAGE_MSSQL_STORE_GET_MESSAGES_PAGINATED_FAILED",
654
+ id: createStorageErrorId("MSSQL", "LIST_MESSAGES", "FAILED"),
659
655
  domain: ErrorDomain.STORAGE,
660
656
  category: ErrorCategory.THIRD_PARTY,
661
657
  details: {
662
- threadId,
663
- page
658
+ threadId: Array.isArray(threadId) ? threadId.join(",") : threadId,
659
+ resourceId: resourceId ?? ""
664
660
  }
665
661
  },
666
662
  error
667
663
  );
668
664
  this.logger?.error?.(mastraError.toString());
669
- this.logger?.trackException(mastraError);
670
- return { messages: [], total: 0, page, perPage: perPageInput || 40, hasMore: false };
665
+ this.logger?.trackException?.(mastraError);
666
+ return {
667
+ messages: [],
668
+ total: 0,
669
+ page,
670
+ perPage: perPageForResponse,
671
+ hasMore: false
672
+ };
671
673
  }
672
674
  }
673
- async saveMessages({
674
- messages,
675
- format
676
- }) {
677
- if (messages.length === 0) return messages;
675
+ async saveMessages({ messages }) {
676
+ if (messages.length === 0) return { messages: [] };
678
677
  const threadId = messages[0]?.threadId;
679
678
  if (!threadId) {
680
679
  throw new MastraError({
681
- id: "MASTRA_STORAGE_MSSQL_STORE_SAVE_MESSAGES_FAILED",
680
+ id: createStorageErrorId("MSSQL", "SAVE_MESSAGES", "INVALID_THREAD_ID"),
682
681
  domain: ErrorDomain.STORAGE,
683
682
  category: ErrorCategory.THIRD_PARTY,
684
683
  text: `Thread ID is required`
@@ -687,7 +686,7 @@ var MemoryMSSQL = class extends MemoryStorage {
687
686
  const thread = await this.getThreadById({ threadId });
688
687
  if (!thread) {
689
688
  throw new MastraError({
690
- id: "MASTRA_STORAGE_MSSQL_STORE_SAVE_MESSAGES_FAILED",
689
+ id: createStorageErrorId("MSSQL", "SAVE_MESSAGES", "THREAD_NOT_FOUND"),
691
690
  domain: ErrorDomain.STORAGE,
692
691
  category: ErrorCategory.THIRD_PARTY,
693
692
  text: `Thread ${threadId} not found`,
@@ -718,7 +717,7 @@ var MemoryMSSQL = class extends MemoryStorage {
718
717
  "content",
719
718
  typeof message.content === "string" ? message.content : JSON.stringify(message.content)
720
719
  );
721
- request.input("createdAt", sql2.DateTime2, message.createdAt);
720
+ request.input("createdAt", sql3.DateTime2, message.createdAt);
722
721
  request.input("role", message.role);
723
722
  request.input("type", message.type || "v2");
724
723
  request.input("resourceId", message.resourceId);
@@ -737,7 +736,7 @@ var MemoryMSSQL = class extends MemoryStorage {
737
736
  await request.query(mergeSql);
738
737
  }
739
738
  const threadReq = transaction.request();
740
- threadReq.input("updatedAt", sql2.DateTime2, /* @__PURE__ */ new Date());
739
+ threadReq.input("updatedAt", sql3.DateTime2, /* @__PURE__ */ new Date());
741
740
  threadReq.input("id", threadId);
742
741
  await threadReq.query(`UPDATE ${tableThreads} SET [updatedAt] = @updatedAt WHERE id = @id`);
743
742
  await transaction.commit();
@@ -756,12 +755,11 @@ var MemoryMSSQL = class extends MemoryStorage {
756
755
  return message;
757
756
  });
758
757
  const list = new MessageList().add(messagesWithParsedContent, "memory");
759
- if (format === "v2") return list.get.all.v2();
760
- return list.get.all.v1();
758
+ return { messages: list.get.all.db() };
761
759
  } catch (error) {
762
760
  throw new MastraError(
763
761
  {
764
- id: "MASTRA_STORAGE_MSSQL_STORE_SAVE_MESSAGES_FAILED",
762
+ id: createStorageErrorId("MSSQL", "SAVE_MESSAGES", "FAILED"),
765
763
  domain: ErrorDomain.STORAGE,
766
764
  category: ErrorCategory.THIRD_PARTY,
767
765
  details: { threadId }
@@ -852,7 +850,7 @@ var MemoryMSSQL = class extends MemoryStorage {
852
850
  await transaction.rollback();
853
851
  throw new MastraError(
854
852
  {
855
- id: "MASTRA_STORAGE_MSSQL_UPDATE_MESSAGES_FAILED",
853
+ id: createStorageErrorId("MSSQL", "UPDATE_MESSAGES", "FAILED"),
856
854
  domain: ErrorDomain.STORAGE,
857
855
  category: ErrorCategory.THIRD_PARTY
858
856
  },
@@ -914,7 +912,7 @@ var MemoryMSSQL = class extends MemoryStorage {
914
912
  } catch (error) {
915
913
  throw new MastraError(
916
914
  {
917
- id: "MASTRA_STORAGE_MSSQL_STORE_DELETE_MESSAGES_FAILED",
915
+ id: createStorageErrorId("MSSQL", "DELETE_MESSAGES", "FAILED"),
918
916
  domain: ErrorDomain.STORAGE,
919
917
  category: ErrorCategory.THIRD_PARTY,
920
918
  details: { messageIds: messageIds.join(", ") }
@@ -933,14 +931,16 @@ var MemoryMSSQL = class extends MemoryStorage {
933
931
  return null;
934
932
  }
935
933
  return {
936
- ...result,
937
- workingMemory: typeof result.workingMemory === "object" ? JSON.stringify(result.workingMemory) : result.workingMemory,
934
+ id: result.id,
935
+ createdAt: result.createdAt,
936
+ updatedAt: result.updatedAt,
937
+ workingMemory: result.workingMemory,
938
938
  metadata: typeof result.metadata === "string" ? JSON.parse(result.metadata) : result.metadata
939
939
  };
940
940
  } catch (error) {
941
941
  const mastraError = new MastraError(
942
942
  {
943
- id: "MASTRA_STORAGE_MSSQL_GET_RESOURCE_BY_ID_FAILED",
943
+ id: createStorageErrorId("MSSQL", "GET_RESOURCE_BY_ID", "FAILED"),
944
944
  domain: ErrorDomain.STORAGE,
945
945
  category: ErrorCategory.THIRD_PARTY,
946
946
  details: { resourceId }
@@ -948,7 +948,7 @@ var MemoryMSSQL = class extends MemoryStorage {
948
948
  error
949
949
  );
950
950
  this.logger?.error?.(mastraError.toString());
951
- this.logger?.trackException(mastraError);
951
+ this.logger?.trackException?.(mastraError);
952
952
  throw mastraError;
953
953
  }
954
954
  }
@@ -957,7 +957,7 @@ var MemoryMSSQL = class extends MemoryStorage {
957
957
  tableName: TABLE_RESOURCES,
958
958
  record: {
959
959
  ...resource,
960
- metadata: JSON.stringify(resource.metadata)
960
+ metadata: resource.metadata
961
961
  }
962
962
  });
963
963
  return resource;
@@ -1007,7 +1007,7 @@ var MemoryMSSQL = class extends MemoryStorage {
1007
1007
  } catch (error) {
1008
1008
  const mastraError = new MastraError(
1009
1009
  {
1010
- id: "MASTRA_STORAGE_MSSQL_UPDATE_RESOURCE_FAILED",
1010
+ id: createStorageErrorId("MSSQL", "UPDATE_RESOURCE", "FAILED"),
1011
1011
  domain: ErrorDomain.STORAGE,
1012
1012
  category: ErrorCategory.THIRD_PARTY,
1013
1013
  details: { resourceId }
@@ -1015,115 +1015,440 @@ var MemoryMSSQL = class extends MemoryStorage {
1015
1015
  error
1016
1016
  );
1017
1017
  this.logger?.error?.(mastraError.toString());
1018
- this.logger?.trackException(mastraError);
1018
+ this.logger?.trackException?.(mastraError);
1019
1019
  throw mastraError;
1020
1020
  }
1021
1021
  }
1022
1022
  };
1023
- var StoreOperationsMSSQL = class extends StoreOperations {
1023
+ var ObservabilityMSSQL = class extends ObservabilityStorage {
1024
1024
  pool;
1025
- schemaName;
1026
- setupSchemaPromise = null;
1027
- schemaSetupComplete = void 0;
1028
- getSqlType(type, isPrimaryKey = false) {
1029
- switch (type) {
1030
- case "text":
1031
- return isPrimaryKey ? "NVARCHAR(255)" : "NVARCHAR(MAX)";
1032
- case "timestamp":
1033
- return "DATETIME2(7)";
1034
- case "uuid":
1035
- return "UNIQUEIDENTIFIER";
1036
- case "jsonb":
1037
- return "NVARCHAR(MAX)";
1038
- case "integer":
1039
- return "INT";
1040
- case "bigint":
1041
- return "BIGINT";
1042
- case "float":
1043
- return "FLOAT";
1044
- default:
1045
- throw new MastraError({
1046
- id: "MASTRA_STORAGE_MSSQL_STORE_TYPE_NOT_SUPPORTED",
1047
- domain: ErrorDomain.STORAGE,
1048
- category: ErrorCategory.THIRD_PARTY
1049
- });
1050
- }
1051
- }
1052
- constructor({ pool, schemaName }) {
1025
+ operations;
1026
+ schema;
1027
+ constructor({
1028
+ pool,
1029
+ operations,
1030
+ schema
1031
+ }) {
1053
1032
  super();
1054
1033
  this.pool = pool;
1055
- this.schemaName = schemaName;
1056
- }
1057
- async hasColumn(table, column) {
1058
- const schema = this.schemaName || "dbo";
1059
- const request = this.pool.request();
1060
- request.input("schema", schema);
1061
- request.input("table", table);
1062
- request.input("column", column);
1063
- request.input("columnLower", column.toLowerCase());
1064
- const result = await request.query(
1065
- `SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = @schema AND TABLE_NAME = @table AND (COLUMN_NAME = @column OR COLUMN_NAME = @columnLower)`
1066
- );
1067
- return result.recordset.length > 0;
1034
+ this.operations = operations;
1035
+ this.schema = schema;
1068
1036
  }
1069
- async setupSchema() {
1070
- if (!this.schemaName || this.schemaSetupComplete) {
1071
- return;
1072
- }
1073
- if (!this.setupSchemaPromise) {
1074
- this.setupSchemaPromise = (async () => {
1075
- try {
1076
- const checkRequest = this.pool.request();
1077
- checkRequest.input("schemaName", this.schemaName);
1078
- const checkResult = await checkRequest.query(`
1079
- SELECT 1 AS found FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = @schemaName
1080
- `);
1081
- const schemaExists = Array.isArray(checkResult.recordset) && checkResult.recordset.length > 0;
1082
- if (!schemaExists) {
1083
- try {
1084
- await this.pool.request().query(`CREATE SCHEMA [${this.schemaName}]`);
1085
- this.logger?.info?.(`Schema "${this.schemaName}" created successfully`);
1086
- } catch (error) {
1087
- this.logger?.error?.(`Failed to create schema "${this.schemaName}"`, { error });
1088
- throw new Error(
1089
- `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.`
1090
- );
1091
- }
1092
- }
1093
- this.schemaSetupComplete = true;
1094
- this.logger?.debug?.(`Schema "${this.schemaName}" is ready for use`);
1095
- } catch (error) {
1096
- this.schemaSetupComplete = void 0;
1097
- this.setupSchemaPromise = null;
1098
- throw error;
1099
- } finally {
1100
- this.setupSchemaPromise = null;
1101
- }
1102
- })();
1103
- }
1104
- await this.setupSchemaPromise;
1037
+ get tracingStrategy() {
1038
+ return {
1039
+ preferred: "batch-with-updates",
1040
+ supported: ["batch-with-updates", "insert-only"]
1041
+ };
1105
1042
  }
1106
- async insert({ tableName, record }) {
1043
+ async createSpan(span) {
1107
1044
  try {
1108
- const columns = Object.keys(record).map((col) => parseSqlIdentifier(col, "column name"));
1109
- const values = Object.values(record);
1110
- const paramNames = values.map((_, i) => `@param${i}`);
1111
- const insertSql = `INSERT INTO ${getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) })} (${columns.map((c) => `[${c}]`).join(", ")}) VALUES (${paramNames.join(", ")})`;
1112
- const request = this.pool.request();
1113
- values.forEach((value, i) => {
1114
- if (value instanceof Date) {
1115
- request.input(`param${i}`, sql2.DateTime2, value);
1116
- } else if (typeof value === "object" && value !== null) {
1117
- request.input(`param${i}`, JSON.stringify(value));
1045
+ const startedAt = span.startedAt instanceof Date ? span.startedAt.toISOString() : span.startedAt;
1046
+ const endedAt = span.endedAt instanceof Date ? span.endedAt.toISOString() : span.endedAt;
1047
+ const record = {
1048
+ ...span,
1049
+ startedAt,
1050
+ endedAt
1051
+ // Note: createdAt/updatedAt will be set by default values
1052
+ };
1053
+ return this.operations.insert({ tableName: TABLE_SPANS, record });
1054
+ } catch (error) {
1055
+ throw new MastraError(
1056
+ {
1057
+ id: createStorageErrorId("MSSQL", "CREATE_SPAN", "FAILED"),
1058
+ domain: ErrorDomain.STORAGE,
1059
+ category: ErrorCategory.USER,
1060
+ details: {
1061
+ spanId: span.spanId,
1062
+ traceId: span.traceId,
1063
+ spanType: span.spanType,
1064
+ spanName: span.name
1065
+ }
1066
+ },
1067
+ error
1068
+ );
1069
+ }
1070
+ }
1071
+ async getTrace(traceId) {
1072
+ try {
1073
+ const tableName = getTableName({
1074
+ indexName: TABLE_SPANS,
1075
+ schemaName: getSchemaName(this.schema)
1076
+ });
1077
+ const request = this.pool.request();
1078
+ request.input("traceId", traceId);
1079
+ const result = await request.query(
1080
+ `SELECT
1081
+ [traceId], [spanId], [parentSpanId], [name], [scope], [spanType],
1082
+ [attributes], [metadata], [links], [input], [output], [error], [isEvent],
1083
+ [startedAt], [endedAt], [createdAt], [updatedAt]
1084
+ FROM ${tableName}
1085
+ WHERE [traceId] = @traceId
1086
+ ORDER BY [startedAt] DESC`
1087
+ );
1088
+ if (!result.recordset || result.recordset.length === 0) {
1089
+ return null;
1090
+ }
1091
+ return {
1092
+ traceId,
1093
+ spans: result.recordset.map(
1094
+ (span) => transformFromSqlRow({
1095
+ tableName: TABLE_SPANS,
1096
+ sqlRow: span
1097
+ })
1098
+ )
1099
+ };
1100
+ } catch (error) {
1101
+ throw new MastraError(
1102
+ {
1103
+ id: createStorageErrorId("MSSQL", "GET_TRACE", "FAILED"),
1104
+ domain: ErrorDomain.STORAGE,
1105
+ category: ErrorCategory.USER,
1106
+ details: {
1107
+ traceId
1108
+ }
1109
+ },
1110
+ error
1111
+ );
1112
+ }
1113
+ }
1114
+ async updateSpan({
1115
+ spanId,
1116
+ traceId,
1117
+ updates
1118
+ }) {
1119
+ try {
1120
+ const data = { ...updates };
1121
+ if (data.endedAt instanceof Date) {
1122
+ data.endedAt = data.endedAt.toISOString();
1123
+ }
1124
+ if (data.startedAt instanceof Date) {
1125
+ data.startedAt = data.startedAt.toISOString();
1126
+ }
1127
+ await this.operations.update({
1128
+ tableName: TABLE_SPANS,
1129
+ keys: { spanId, traceId },
1130
+ data
1131
+ });
1132
+ } catch (error) {
1133
+ throw new MastraError(
1134
+ {
1135
+ id: createStorageErrorId("MSSQL", "UPDATE_SPAN", "FAILED"),
1136
+ domain: ErrorDomain.STORAGE,
1137
+ category: ErrorCategory.USER,
1138
+ details: {
1139
+ spanId,
1140
+ traceId
1141
+ }
1142
+ },
1143
+ error
1144
+ );
1145
+ }
1146
+ }
1147
+ async getTracesPaginated({
1148
+ filters,
1149
+ pagination
1150
+ }) {
1151
+ const page = pagination?.page ?? 0;
1152
+ const perPage = pagination?.perPage ?? 10;
1153
+ const { entityId, entityType, ...actualFilters } = filters || {};
1154
+ const filtersWithDateRange = {
1155
+ ...actualFilters,
1156
+ ...buildDateRangeFilter(pagination?.dateRange, "startedAt"),
1157
+ parentSpanId: null
1158
+ // Only get root spans for traces
1159
+ };
1160
+ const whereClause = prepareWhereClause(filtersWithDateRange);
1161
+ let actualWhereClause = whereClause.sql;
1162
+ const params = { ...whereClause.params };
1163
+ let currentParamIndex = Object.keys(params).length + 1;
1164
+ if (entityId && entityType) {
1165
+ let name = "";
1166
+ if (entityType === "workflow") {
1167
+ name = `workflow run: '${entityId}'`;
1168
+ } else if (entityType === "agent") {
1169
+ name = `agent run: '${entityId}'`;
1170
+ } else {
1171
+ const error = new MastraError({
1172
+ id: createStorageErrorId("MSSQL", "GET_TRACES_PAGINATED", "INVALID_ENTITY_TYPE"),
1173
+ domain: ErrorDomain.STORAGE,
1174
+ category: ErrorCategory.USER,
1175
+ details: {
1176
+ entityType
1177
+ },
1178
+ text: `Cannot filter by entity type: ${entityType}`
1179
+ });
1180
+ throw error;
1181
+ }
1182
+ const entityParam = `p${currentParamIndex++}`;
1183
+ if (actualWhereClause) {
1184
+ actualWhereClause += ` AND [name] = @${entityParam}`;
1185
+ } else {
1186
+ actualWhereClause = ` WHERE [name] = @${entityParam}`;
1187
+ }
1188
+ params[entityParam] = name;
1189
+ }
1190
+ const tableName = getTableName({
1191
+ indexName: TABLE_SPANS,
1192
+ schemaName: getSchemaName(this.schema)
1193
+ });
1194
+ try {
1195
+ const countRequest = this.pool.request();
1196
+ Object.entries(params).forEach(([key, value]) => {
1197
+ countRequest.input(key, value);
1198
+ });
1199
+ const countResult = await countRequest.query(
1200
+ `SELECT COUNT(*) as count FROM ${tableName}${actualWhereClause}`
1201
+ );
1202
+ const total = countResult.recordset[0]?.count ?? 0;
1203
+ if (total === 0) {
1204
+ return {
1205
+ pagination: {
1206
+ total: 0,
1207
+ page,
1208
+ perPage,
1209
+ hasMore: false
1210
+ },
1211
+ spans: []
1212
+ };
1213
+ }
1214
+ const dataRequest = this.pool.request();
1215
+ Object.entries(params).forEach(([key, value]) => {
1216
+ dataRequest.input(key, value);
1217
+ });
1218
+ dataRequest.input("offset", page * perPage);
1219
+ dataRequest.input("limit", perPage);
1220
+ const dataResult = await dataRequest.query(
1221
+ `SELECT * FROM ${tableName}${actualWhereClause} ORDER BY [startedAt] DESC OFFSET @offset ROWS FETCH NEXT @limit ROWS ONLY`
1222
+ );
1223
+ const spans = dataResult.recordset.map(
1224
+ (row) => transformFromSqlRow({
1225
+ tableName: TABLE_SPANS,
1226
+ sqlRow: row
1227
+ })
1228
+ );
1229
+ return {
1230
+ pagination: {
1231
+ total,
1232
+ page,
1233
+ perPage,
1234
+ hasMore: (page + 1) * perPage < total
1235
+ },
1236
+ spans
1237
+ };
1238
+ } catch (error) {
1239
+ throw new MastraError(
1240
+ {
1241
+ id: createStorageErrorId("MSSQL", "GET_TRACES_PAGINATED", "FAILED"),
1242
+ domain: ErrorDomain.STORAGE,
1243
+ category: ErrorCategory.USER
1244
+ },
1245
+ error
1246
+ );
1247
+ }
1248
+ }
1249
+ async batchCreateSpans(args) {
1250
+ if (!args.records || args.records.length === 0) {
1251
+ return;
1252
+ }
1253
+ try {
1254
+ await this.operations.batchInsert({
1255
+ tableName: TABLE_SPANS,
1256
+ records: args.records.map((span) => ({
1257
+ ...span,
1258
+ startedAt: span.startedAt instanceof Date ? span.startedAt.toISOString() : span.startedAt,
1259
+ endedAt: span.endedAt instanceof Date ? span.endedAt.toISOString() : span.endedAt
1260
+ }))
1261
+ });
1262
+ } catch (error) {
1263
+ throw new MastraError(
1264
+ {
1265
+ id: createStorageErrorId("MSSQL", "BATCH_CREATE_SPANS", "FAILED"),
1266
+ domain: ErrorDomain.STORAGE,
1267
+ category: ErrorCategory.USER,
1268
+ details: {
1269
+ count: args.records.length
1270
+ }
1271
+ },
1272
+ error
1273
+ );
1274
+ }
1275
+ }
1276
+ async batchUpdateSpans(args) {
1277
+ if (!args.records || args.records.length === 0) {
1278
+ return;
1279
+ }
1280
+ try {
1281
+ const updates = args.records.map(({ traceId, spanId, updates: data }) => {
1282
+ const processedData = { ...data };
1283
+ if (processedData.endedAt instanceof Date) {
1284
+ processedData.endedAt = processedData.endedAt.toISOString();
1285
+ }
1286
+ if (processedData.startedAt instanceof Date) {
1287
+ processedData.startedAt = processedData.startedAt.toISOString();
1288
+ }
1289
+ return {
1290
+ keys: { spanId, traceId },
1291
+ data: processedData
1292
+ };
1293
+ });
1294
+ await this.operations.batchUpdate({
1295
+ tableName: TABLE_SPANS,
1296
+ updates
1297
+ });
1298
+ } catch (error) {
1299
+ throw new MastraError(
1300
+ {
1301
+ id: createStorageErrorId("MSSQL", "BATCH_UPDATE_SPANS", "FAILED"),
1302
+ domain: ErrorDomain.STORAGE,
1303
+ category: ErrorCategory.USER,
1304
+ details: {
1305
+ count: args.records.length
1306
+ }
1307
+ },
1308
+ error
1309
+ );
1310
+ }
1311
+ }
1312
+ async batchDeleteTraces(args) {
1313
+ if (!args.traceIds || args.traceIds.length === 0) {
1314
+ return;
1315
+ }
1316
+ try {
1317
+ const keys = args.traceIds.map((traceId) => ({ traceId }));
1318
+ await this.operations.batchDelete({
1319
+ tableName: TABLE_SPANS,
1320
+ keys
1321
+ });
1322
+ } catch (error) {
1323
+ throw new MastraError(
1324
+ {
1325
+ id: createStorageErrorId("MSSQL", "BATCH_DELETE_TRACES", "FAILED"),
1326
+ domain: ErrorDomain.STORAGE,
1327
+ category: ErrorCategory.USER,
1328
+ details: {
1329
+ count: args.traceIds.length
1330
+ }
1331
+ },
1332
+ error
1333
+ );
1334
+ }
1335
+ }
1336
+ };
1337
+ var StoreOperationsMSSQL = class extends StoreOperations {
1338
+ pool;
1339
+ schemaName;
1340
+ setupSchemaPromise = null;
1341
+ schemaSetupComplete = void 0;
1342
+ getSqlType(type, isPrimaryKey = false, useLargeStorage = false) {
1343
+ switch (type) {
1344
+ case "text":
1345
+ if (useLargeStorage) {
1346
+ return "NVARCHAR(MAX)";
1347
+ }
1348
+ return isPrimaryKey ? "NVARCHAR(255)" : "NVARCHAR(400)";
1349
+ case "timestamp":
1350
+ return "DATETIME2(7)";
1351
+ case "uuid":
1352
+ return "UNIQUEIDENTIFIER";
1353
+ case "jsonb":
1354
+ return "NVARCHAR(MAX)";
1355
+ case "integer":
1356
+ return "INT";
1357
+ case "bigint":
1358
+ return "BIGINT";
1359
+ case "float":
1360
+ return "FLOAT";
1361
+ case "boolean":
1362
+ return "BIT";
1363
+ default:
1364
+ throw new MastraError({
1365
+ id: createStorageErrorId("MSSQL", "TYPE", "NOT_SUPPORTED"),
1366
+ domain: ErrorDomain.STORAGE,
1367
+ category: ErrorCategory.THIRD_PARTY
1368
+ });
1369
+ }
1370
+ }
1371
+ constructor({ pool, schemaName }) {
1372
+ super();
1373
+ this.pool = pool;
1374
+ this.schemaName = schemaName;
1375
+ }
1376
+ async hasColumn(table, column) {
1377
+ const schema = this.schemaName || "dbo";
1378
+ const request = this.pool.request();
1379
+ request.input("schema", schema);
1380
+ request.input("table", table);
1381
+ request.input("column", column);
1382
+ request.input("columnLower", column.toLowerCase());
1383
+ const result = await request.query(
1384
+ `SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = @schema AND TABLE_NAME = @table AND (COLUMN_NAME = @column OR COLUMN_NAME = @columnLower)`
1385
+ );
1386
+ return result.recordset.length > 0;
1387
+ }
1388
+ async setupSchema() {
1389
+ if (!this.schemaName || this.schemaSetupComplete) {
1390
+ return;
1391
+ }
1392
+ if (!this.setupSchemaPromise) {
1393
+ this.setupSchemaPromise = (async () => {
1394
+ try {
1395
+ const checkRequest = this.pool.request();
1396
+ checkRequest.input("schemaName", this.schemaName);
1397
+ const checkResult = await checkRequest.query(`
1398
+ SELECT 1 AS found FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = @schemaName
1399
+ `);
1400
+ const schemaExists = Array.isArray(checkResult.recordset) && checkResult.recordset.length > 0;
1401
+ if (!schemaExists) {
1402
+ try {
1403
+ await this.pool.request().query(`CREATE SCHEMA [${this.schemaName}]`);
1404
+ this.logger?.info?.(`Schema "${this.schemaName}" created successfully`);
1405
+ } catch (error) {
1406
+ this.logger?.error?.(`Failed to create schema "${this.schemaName}"`, { error });
1407
+ throw new Error(
1408
+ `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.`
1409
+ );
1410
+ }
1411
+ }
1412
+ this.schemaSetupComplete = true;
1413
+ this.logger?.debug?.(`Schema "${this.schemaName}" is ready for use`);
1414
+ } catch (error) {
1415
+ this.schemaSetupComplete = void 0;
1416
+ this.setupSchemaPromise = null;
1417
+ throw error;
1418
+ } finally {
1419
+ this.setupSchemaPromise = null;
1420
+ }
1421
+ })();
1422
+ }
1423
+ await this.setupSchemaPromise;
1424
+ }
1425
+ async insert({
1426
+ tableName,
1427
+ record,
1428
+ transaction
1429
+ }) {
1430
+ try {
1431
+ const columns = Object.keys(record);
1432
+ const parsedColumns = columns.map((col) => parseSqlIdentifier(col, "column name"));
1433
+ const paramNames = columns.map((_, i) => `@param${i}`);
1434
+ const insertSql = `INSERT INTO ${getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) })} (${parsedColumns.map((c) => `[${c}]`).join(", ")}) VALUES (${paramNames.join(", ")})`;
1435
+ const request = transaction ? transaction.request() : this.pool.request();
1436
+ columns.forEach((col, i) => {
1437
+ const value = record[col];
1438
+ const preparedValue = this.prepareValue(value, col, tableName);
1439
+ if (preparedValue instanceof Date) {
1440
+ request.input(`param${i}`, sql3.DateTime2, preparedValue);
1441
+ } else if (preparedValue === null || preparedValue === void 0) {
1442
+ request.input(`param${i}`, this.getMssqlType(tableName, col), null);
1118
1443
  } else {
1119
- request.input(`param${i}`, value);
1444
+ request.input(`param${i}`, preparedValue);
1120
1445
  }
1121
1446
  });
1122
1447
  await request.query(insertSql);
1123
1448
  } catch (error) {
1124
1449
  throw new MastraError(
1125
1450
  {
1126
- id: "MASTRA_STORAGE_MSSQL_STORE_INSERT_FAILED",
1451
+ id: createStorageErrorId("MSSQL", "INSERT", "FAILED"),
1127
1452
  domain: ErrorDomain.STORAGE,
1128
1453
  category: ErrorCategory.THIRD_PARTY,
1129
1454
  details: {
@@ -1140,7 +1465,7 @@ var StoreOperationsMSSQL = class extends StoreOperations {
1140
1465
  try {
1141
1466
  await this.pool.request().query(`TRUNCATE TABLE ${fullTableName}`);
1142
1467
  } catch (truncateError) {
1143
- if (truncateError.message && truncateError.message.includes("foreign key")) {
1468
+ if (truncateError?.number === 4712) {
1144
1469
  await this.pool.request().query(`DELETE FROM ${fullTableName}`);
1145
1470
  } else {
1146
1471
  throw truncateError;
@@ -1149,7 +1474,7 @@ var StoreOperationsMSSQL = class extends StoreOperations {
1149
1474
  } catch (error) {
1150
1475
  throw new MastraError(
1151
1476
  {
1152
- id: "MASTRA_STORAGE_MSSQL_STORE_CLEAR_TABLE_FAILED",
1477
+ id: createStorageErrorId("MSSQL", "CLEAR_TABLE", "FAILED"),
1153
1478
  domain: ErrorDomain.STORAGE,
1154
1479
  category: ErrorCategory.THIRD_PARTY,
1155
1480
  details: {
@@ -1163,9 +1488,11 @@ var StoreOperationsMSSQL = class extends StoreOperations {
1163
1488
  getDefaultValue(type) {
1164
1489
  switch (type) {
1165
1490
  case "timestamp":
1166
- return "DEFAULT SYSDATETIMEOFFSET()";
1491
+ return "DEFAULT SYSUTCDATETIME()";
1167
1492
  case "jsonb":
1168
1493
  return "DEFAULT N'{}'";
1494
+ case "boolean":
1495
+ return "DEFAULT 0";
1169
1496
  default:
1170
1497
  return super.getDefaultValue(type);
1171
1498
  }
@@ -1176,13 +1503,29 @@ var StoreOperationsMSSQL = class extends StoreOperations {
1176
1503
  }) {
1177
1504
  try {
1178
1505
  const uniqueConstraintColumns = tableName === TABLE_WORKFLOW_SNAPSHOT ? ["workflow_name", "run_id"] : [];
1506
+ const largeDataColumns = [
1507
+ "workingMemory",
1508
+ "snapshot",
1509
+ "metadata",
1510
+ "content",
1511
+ // messages.content - can be very long conversation content
1512
+ "input",
1513
+ // evals.input - test input data
1514
+ "output",
1515
+ // evals.output - test output data
1516
+ "instructions",
1517
+ // evals.instructions - evaluation instructions
1518
+ "other"
1519
+ // traces.other - additional trace data
1520
+ ];
1179
1521
  const columns = Object.entries(schema).map(([name, def]) => {
1180
1522
  const parsedName = parseSqlIdentifier(name, "column name");
1181
1523
  const constraints = [];
1182
1524
  if (def.primaryKey) constraints.push("PRIMARY KEY");
1183
1525
  if (!def.nullable) constraints.push("NOT NULL");
1184
1526
  const isIndexed = !!def.primaryKey || uniqueConstraintColumns.includes(name);
1185
- return `[${parsedName}] ${this.getSqlType(def.type, isIndexed)} ${constraints.join(" ")}`.trim();
1527
+ const useLargeStorage = largeDataColumns.includes(name);
1528
+ return `[${parsedName}] ${this.getSqlType(def.type, isIndexed, useLargeStorage)} ${constraints.join(" ")}`.trim();
1186
1529
  }).join(",\n");
1187
1530
  if (this.schemaName) {
1188
1531
  await this.setupSchema();
@@ -1234,7 +1577,7 @@ ${columns}
1234
1577
  } catch (error) {
1235
1578
  throw new MastraError(
1236
1579
  {
1237
- id: "MASTRA_STORAGE_MSSQL_STORE_CREATE_TABLE_FAILED",
1580
+ id: createStorageErrorId("MSSQL", "CREATE_TABLE", "FAILED"),
1238
1581
  domain: ErrorDomain.STORAGE,
1239
1582
  category: ErrorCategory.THIRD_PARTY,
1240
1583
  details: {
@@ -1269,7 +1612,19 @@ ${columns}
1269
1612
  const columnExists = Array.isArray(checkResult.recordset) && checkResult.recordset.length > 0;
1270
1613
  if (!columnExists) {
1271
1614
  const columnDef = schema[columnName];
1272
- const sqlType = this.getSqlType(columnDef.type);
1615
+ const largeDataColumns = [
1616
+ "workingMemory",
1617
+ "snapshot",
1618
+ "metadata",
1619
+ "content",
1620
+ "input",
1621
+ "output",
1622
+ "instructions",
1623
+ "other"
1624
+ ];
1625
+ const useLargeStorage = largeDataColumns.includes(columnName);
1626
+ const isIndexed = !!columnDef.primaryKey;
1627
+ const sqlType = this.getSqlType(columnDef.type, isIndexed, useLargeStorage);
1273
1628
  const nullable = columnDef.nullable === false ? "NOT NULL" : "";
1274
1629
  const defaultValue = columnDef.nullable === false ? this.getDefaultValue(columnDef.type) : "";
1275
1630
  const parsedColumnName = parseSqlIdentifier(columnName, "column name");
@@ -1282,7 +1637,7 @@ ${columns}
1282
1637
  } catch (error) {
1283
1638
  throw new MastraError(
1284
1639
  {
1285
- id: "MASTRA_STORAGE_MSSQL_STORE_ALTER_TABLE_FAILED",
1640
+ id: createStorageErrorId("MSSQL", "ALTER_TABLE", "FAILED"),
1286
1641
  domain: ErrorDomain.STORAGE,
1287
1642
  category: ErrorCategory.THIRD_PARTY,
1288
1643
  details: {
@@ -1297,13 +1652,17 @@ ${columns}
1297
1652
  try {
1298
1653
  const keyEntries = Object.entries(keys).map(([key, value]) => [parseSqlIdentifier(key, "column name"), value]);
1299
1654
  const conditions = keyEntries.map(([key], i) => `[${key}] = @param${i}`).join(" AND ");
1300
- const values = keyEntries.map(([_, value]) => value);
1301
- const sql7 = `SELECT * FROM ${getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) })} WHERE ${conditions}`;
1655
+ const sql5 = `SELECT * FROM ${getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) })} WHERE ${conditions}`;
1302
1656
  const request = this.pool.request();
1303
- values.forEach((value, i) => {
1304
- request.input(`param${i}`, value);
1657
+ keyEntries.forEach(([key, value], i) => {
1658
+ const preparedValue = this.prepareValue(value, key, tableName);
1659
+ if (preparedValue === null || preparedValue === void 0) {
1660
+ request.input(`param${i}`, this.getMssqlType(tableName, key), null);
1661
+ } else {
1662
+ request.input(`param${i}`, preparedValue);
1663
+ }
1305
1664
  });
1306
- const resultSet = await request.query(sql7);
1665
+ const resultSet = await request.query(sql5);
1307
1666
  const result = resultSet.recordset[0] || null;
1308
1667
  if (!result) {
1309
1668
  return null;
@@ -1319,7 +1678,7 @@ ${columns}
1319
1678
  } catch (error) {
1320
1679
  throw new MastraError(
1321
1680
  {
1322
- id: "MASTRA_STORAGE_MSSQL_STORE_LOAD_FAILED",
1681
+ id: createStorageErrorId("MSSQL", "LOAD", "FAILED"),
1323
1682
  domain: ErrorDomain.STORAGE,
1324
1683
  category: ErrorCategory.THIRD_PARTY,
1325
1684
  details: {
@@ -1335,14 +1694,14 @@ ${columns}
1335
1694
  try {
1336
1695
  await transaction.begin();
1337
1696
  for (const record of records) {
1338
- await this.insert({ tableName, record });
1697
+ await this.insert({ tableName, record, transaction });
1339
1698
  }
1340
1699
  await transaction.commit();
1341
1700
  } catch (error) {
1342
1701
  await transaction.rollback();
1343
1702
  throw new MastraError(
1344
1703
  {
1345
- id: "MASTRA_STORAGE_MSSQL_STORE_BATCH_INSERT_FAILED",
1704
+ id: createStorageErrorId("MSSQL", "BATCH_INSERT", "FAILED"),
1346
1705
  domain: ErrorDomain.STORAGE,
1347
1706
  category: ErrorCategory.THIRD_PARTY,
1348
1707
  details: {
@@ -1361,7 +1720,7 @@ ${columns}
1361
1720
  } catch (error) {
1362
1721
  throw new MastraError(
1363
1722
  {
1364
- id: "MASTRA_STORAGE_MSSQL_STORE_DROP_TABLE_FAILED",
1723
+ id: createStorageErrorId("MSSQL", "DROP_TABLE", "FAILED"),
1365
1724
  domain: ErrorDomain.STORAGE,
1366
1725
  category: ErrorCategory.THIRD_PARTY,
1367
1726
  details: {
@@ -1372,29 +1731,568 @@ ${columns}
1372
1731
  );
1373
1732
  }
1374
1733
  }
1375
- };
1376
- function parseJSON(jsonString) {
1377
- try {
1378
- return JSON.parse(jsonString);
1379
- } catch {
1380
- return jsonString;
1734
+ /**
1735
+ * Prepares a value for database operations, handling Date objects and JSON serialization
1736
+ */
1737
+ prepareValue(value, columnName, tableName) {
1738
+ if (value === null || value === void 0) {
1739
+ return value;
1740
+ }
1741
+ if (value instanceof Date) {
1742
+ return value;
1743
+ }
1744
+ const schema = TABLE_SCHEMAS[tableName];
1745
+ const columnSchema = schema?.[columnName];
1746
+ if (columnSchema?.type === "boolean") {
1747
+ return value ? 1 : 0;
1748
+ }
1749
+ if (columnSchema?.type === "jsonb") {
1750
+ if (typeof value === "string") {
1751
+ const trimmed = value.trim();
1752
+ if (trimmed.length > 0) {
1753
+ try {
1754
+ JSON.parse(trimmed);
1755
+ return trimmed;
1756
+ } catch {
1757
+ }
1758
+ }
1759
+ return JSON.stringify(value);
1760
+ }
1761
+ if (typeof value === "bigint") {
1762
+ return value.toString();
1763
+ }
1764
+ return JSON.stringify(value);
1765
+ }
1766
+ if (typeof value === "object") {
1767
+ return JSON.stringify(value);
1768
+ }
1769
+ return value;
1381
1770
  }
1382
- }
1771
+ /**
1772
+ * Maps TABLE_SCHEMAS types to mssql param types (used when value is null)
1773
+ */
1774
+ getMssqlType(tableName, columnName) {
1775
+ const col = TABLE_SCHEMAS[tableName]?.[columnName];
1776
+ switch (col?.type) {
1777
+ case "text":
1778
+ return sql3.NVarChar;
1779
+ case "timestamp":
1780
+ return sql3.DateTime2;
1781
+ case "uuid":
1782
+ return sql3.UniqueIdentifier;
1783
+ case "jsonb":
1784
+ return sql3.NVarChar;
1785
+ case "integer":
1786
+ return sql3.Int;
1787
+ case "bigint":
1788
+ return sql3.BigInt;
1789
+ case "float":
1790
+ return sql3.Float;
1791
+ case "boolean":
1792
+ return sql3.Bit;
1793
+ default:
1794
+ return sql3.NVarChar;
1795
+ }
1796
+ }
1797
+ /**
1798
+ * Update a single record in the database
1799
+ */
1800
+ async update({
1801
+ tableName,
1802
+ keys,
1803
+ data,
1804
+ transaction
1805
+ }) {
1806
+ try {
1807
+ if (!data || Object.keys(data).length === 0) {
1808
+ throw new MastraError({
1809
+ id: createStorageErrorId("MSSQL", "UPDATE", "EMPTY_DATA"),
1810
+ domain: ErrorDomain.STORAGE,
1811
+ category: ErrorCategory.USER,
1812
+ text: "Cannot update with empty data payload"
1813
+ });
1814
+ }
1815
+ if (!keys || Object.keys(keys).length === 0) {
1816
+ throw new MastraError({
1817
+ id: createStorageErrorId("MSSQL", "UPDATE", "EMPTY_KEYS"),
1818
+ domain: ErrorDomain.STORAGE,
1819
+ category: ErrorCategory.USER,
1820
+ text: "Cannot update without keys to identify records"
1821
+ });
1822
+ }
1823
+ const setClauses = [];
1824
+ const request = transaction ? transaction.request() : this.pool.request();
1825
+ let paramIndex = 0;
1826
+ Object.entries(data).forEach(([key, value]) => {
1827
+ const parsedKey = parseSqlIdentifier(key, "column name");
1828
+ const paramName = `set${paramIndex++}`;
1829
+ setClauses.push(`[${parsedKey}] = @${paramName}`);
1830
+ const preparedValue = this.prepareValue(value, key, tableName);
1831
+ if (preparedValue === null || preparedValue === void 0) {
1832
+ request.input(paramName, this.getMssqlType(tableName, key), null);
1833
+ } else {
1834
+ request.input(paramName, preparedValue);
1835
+ }
1836
+ });
1837
+ const whereConditions = [];
1838
+ Object.entries(keys).forEach(([key, value]) => {
1839
+ const parsedKey = parseSqlIdentifier(key, "column name");
1840
+ const paramName = `where${paramIndex++}`;
1841
+ whereConditions.push(`[${parsedKey}] = @${paramName}`);
1842
+ const preparedValue = this.prepareValue(value, key, tableName);
1843
+ if (preparedValue === null || preparedValue === void 0) {
1844
+ request.input(paramName, this.getMssqlType(tableName, key), null);
1845
+ } else {
1846
+ request.input(paramName, preparedValue);
1847
+ }
1848
+ });
1849
+ const tableName_ = getTableName({
1850
+ indexName: tableName,
1851
+ schemaName: getSchemaName(this.schemaName)
1852
+ });
1853
+ const updateSql = `UPDATE ${tableName_} SET ${setClauses.join(", ")} WHERE ${whereConditions.join(" AND ")}`;
1854
+ await request.query(updateSql);
1855
+ } catch (error) {
1856
+ throw new MastraError(
1857
+ {
1858
+ id: createStorageErrorId("MSSQL", "UPDATE", "FAILED"),
1859
+ domain: ErrorDomain.STORAGE,
1860
+ category: ErrorCategory.THIRD_PARTY,
1861
+ details: {
1862
+ tableName
1863
+ }
1864
+ },
1865
+ error
1866
+ );
1867
+ }
1868
+ }
1869
+ /**
1870
+ * Update multiple records in a single batch transaction
1871
+ */
1872
+ async batchUpdate({
1873
+ tableName,
1874
+ updates
1875
+ }) {
1876
+ const transaction = this.pool.transaction();
1877
+ try {
1878
+ await transaction.begin();
1879
+ for (const { keys, data } of updates) {
1880
+ await this.update({ tableName, keys, data, transaction });
1881
+ }
1882
+ await transaction.commit();
1883
+ } catch (error) {
1884
+ await transaction.rollback();
1885
+ throw new MastraError(
1886
+ {
1887
+ id: createStorageErrorId("MSSQL", "BATCH_UPDATE", "FAILED"),
1888
+ domain: ErrorDomain.STORAGE,
1889
+ category: ErrorCategory.THIRD_PARTY,
1890
+ details: {
1891
+ tableName,
1892
+ numberOfRecords: updates.length
1893
+ }
1894
+ },
1895
+ error
1896
+ );
1897
+ }
1898
+ }
1899
+ /**
1900
+ * Delete multiple records by keys
1901
+ */
1902
+ async batchDelete({ tableName, keys }) {
1903
+ if (keys.length === 0) {
1904
+ return;
1905
+ }
1906
+ const tableName_ = getTableName({
1907
+ indexName: tableName,
1908
+ schemaName: getSchemaName(this.schemaName)
1909
+ });
1910
+ const transaction = this.pool.transaction();
1911
+ try {
1912
+ await transaction.begin();
1913
+ for (const keySet of keys) {
1914
+ const conditions = [];
1915
+ const request = transaction.request();
1916
+ let paramIndex = 0;
1917
+ Object.entries(keySet).forEach(([key, value]) => {
1918
+ const parsedKey = parseSqlIdentifier(key, "column name");
1919
+ const paramName = `p${paramIndex++}`;
1920
+ conditions.push(`[${parsedKey}] = @${paramName}`);
1921
+ const preparedValue = this.prepareValue(value, key, tableName);
1922
+ if (preparedValue === null || preparedValue === void 0) {
1923
+ request.input(paramName, this.getMssqlType(tableName, key), null);
1924
+ } else {
1925
+ request.input(paramName, preparedValue);
1926
+ }
1927
+ });
1928
+ const deleteSql = `DELETE FROM ${tableName_} WHERE ${conditions.join(" AND ")}`;
1929
+ await request.query(deleteSql);
1930
+ }
1931
+ await transaction.commit();
1932
+ } catch (error) {
1933
+ await transaction.rollback();
1934
+ throw new MastraError(
1935
+ {
1936
+ id: createStorageErrorId("MSSQL", "BATCH_DELETE", "FAILED"),
1937
+ domain: ErrorDomain.STORAGE,
1938
+ category: ErrorCategory.THIRD_PARTY,
1939
+ details: {
1940
+ tableName,
1941
+ numberOfRecords: keys.length
1942
+ }
1943
+ },
1944
+ error
1945
+ );
1946
+ }
1947
+ }
1948
+ /**
1949
+ * Create a new index on a table
1950
+ */
1951
+ async createIndex(options) {
1952
+ try {
1953
+ const { name, table, columns, unique = false, where } = options;
1954
+ const schemaName = this.schemaName || "dbo";
1955
+ const fullTableName = getTableName({
1956
+ indexName: table,
1957
+ schemaName: getSchemaName(this.schemaName)
1958
+ });
1959
+ const indexNameSafe = parseSqlIdentifier(name, "index name");
1960
+ const checkRequest = this.pool.request();
1961
+ checkRequest.input("indexName", indexNameSafe);
1962
+ checkRequest.input("schemaName", schemaName);
1963
+ checkRequest.input("tableName", table);
1964
+ const indexExists = await checkRequest.query(`
1965
+ SELECT 1 as found
1966
+ FROM sys.indexes i
1967
+ INNER JOIN sys.tables t ON i.object_id = t.object_id
1968
+ INNER JOIN sys.schemas s ON t.schema_id = s.schema_id
1969
+ WHERE i.name = @indexName
1970
+ AND s.name = @schemaName
1971
+ AND t.name = @tableName
1972
+ `);
1973
+ if (indexExists.recordset && indexExists.recordset.length > 0) {
1974
+ return;
1975
+ }
1976
+ const uniqueStr = unique ? "UNIQUE " : "";
1977
+ const columnsStr = columns.map((col) => {
1978
+ if (col.includes(" DESC") || col.includes(" ASC")) {
1979
+ const [colName, ...modifiers] = col.split(" ");
1980
+ if (!colName) {
1981
+ throw new Error(`Invalid column specification: ${col}`);
1982
+ }
1983
+ return `[${parseSqlIdentifier(colName, "column name")}] ${modifiers.join(" ")}`;
1984
+ }
1985
+ return `[${parseSqlIdentifier(col, "column name")}]`;
1986
+ }).join(", ");
1987
+ const whereStr = where ? ` WHERE ${where}` : "";
1988
+ const createIndexSql = `CREATE ${uniqueStr}INDEX [${indexNameSafe}] ON ${fullTableName} (${columnsStr})${whereStr}`;
1989
+ await this.pool.request().query(createIndexSql);
1990
+ } catch (error) {
1991
+ throw new MastraError(
1992
+ {
1993
+ id: createStorageErrorId("MSSQL", "INDEX_CREATE", "FAILED"),
1994
+ domain: ErrorDomain.STORAGE,
1995
+ category: ErrorCategory.THIRD_PARTY,
1996
+ details: {
1997
+ indexName: options.name,
1998
+ tableName: options.table
1999
+ }
2000
+ },
2001
+ error
2002
+ );
2003
+ }
2004
+ }
2005
+ /**
2006
+ * Drop an existing index
2007
+ */
2008
+ async dropIndex(indexName) {
2009
+ try {
2010
+ const schemaName = this.schemaName || "dbo";
2011
+ const indexNameSafe = parseSqlIdentifier(indexName, "index name");
2012
+ const checkRequest = this.pool.request();
2013
+ checkRequest.input("indexName", indexNameSafe);
2014
+ checkRequest.input("schemaName", schemaName);
2015
+ const result = await checkRequest.query(`
2016
+ SELECT t.name as table_name
2017
+ FROM sys.indexes i
2018
+ INNER JOIN sys.tables t ON i.object_id = t.object_id
2019
+ INNER JOIN sys.schemas s ON t.schema_id = s.schema_id
2020
+ WHERE i.name = @indexName
2021
+ AND s.name = @schemaName
2022
+ `);
2023
+ if (!result.recordset || result.recordset.length === 0) {
2024
+ return;
2025
+ }
2026
+ if (result.recordset.length > 1) {
2027
+ const tables = result.recordset.map((r) => r.table_name).join(", ");
2028
+ throw new MastraError({
2029
+ id: createStorageErrorId("MSSQL", "INDEX", "AMBIGUOUS"),
2030
+ domain: ErrorDomain.STORAGE,
2031
+ category: ErrorCategory.USER,
2032
+ text: `Index "${indexNameSafe}" exists on multiple tables (${tables}) in schema "${schemaName}". Please drop indexes manually or ensure unique index names.`
2033
+ });
2034
+ }
2035
+ const tableName = result.recordset[0].table_name;
2036
+ const fullTableName = getTableName({
2037
+ indexName: tableName,
2038
+ schemaName: getSchemaName(this.schemaName)
2039
+ });
2040
+ const dropSql = `DROP INDEX [${indexNameSafe}] ON ${fullTableName}`;
2041
+ await this.pool.request().query(dropSql);
2042
+ } catch (error) {
2043
+ throw new MastraError(
2044
+ {
2045
+ id: createStorageErrorId("MSSQL", "INDEX_DROP", "FAILED"),
2046
+ domain: ErrorDomain.STORAGE,
2047
+ category: ErrorCategory.THIRD_PARTY,
2048
+ details: {
2049
+ indexName
2050
+ }
2051
+ },
2052
+ error
2053
+ );
2054
+ }
2055
+ }
2056
+ /**
2057
+ * List indexes for a specific table or all tables
2058
+ */
2059
+ async listIndexes(tableName) {
2060
+ try {
2061
+ const schemaName = this.schemaName || "dbo";
2062
+ let query;
2063
+ const request = this.pool.request();
2064
+ request.input("schemaName", schemaName);
2065
+ if (tableName) {
2066
+ query = `
2067
+ SELECT
2068
+ i.name as name,
2069
+ o.name as [table],
2070
+ i.is_unique as is_unique,
2071
+ CAST(SUM(s.used_page_count) * 8 / 1024.0 AS VARCHAR(50)) + ' MB' as size
2072
+ FROM sys.indexes i
2073
+ INNER JOIN sys.objects o ON i.object_id = o.object_id
2074
+ INNER JOIN sys.schemas sch ON o.schema_id = sch.schema_id
2075
+ LEFT JOIN sys.dm_db_partition_stats s ON i.object_id = s.object_id AND i.index_id = s.index_id
2076
+ WHERE sch.name = @schemaName
2077
+ AND o.name = @tableName
2078
+ AND i.name IS NOT NULL
2079
+ GROUP BY i.name, o.name, i.is_unique
2080
+ `;
2081
+ request.input("tableName", tableName);
2082
+ } else {
2083
+ query = `
2084
+ SELECT
2085
+ i.name as name,
2086
+ o.name as [table],
2087
+ i.is_unique as is_unique,
2088
+ CAST(SUM(s.used_page_count) * 8 / 1024.0 AS VARCHAR(50)) + ' MB' as size
2089
+ FROM sys.indexes i
2090
+ INNER JOIN sys.objects o ON i.object_id = o.object_id
2091
+ INNER JOIN sys.schemas sch ON o.schema_id = sch.schema_id
2092
+ LEFT JOIN sys.dm_db_partition_stats s ON i.object_id = s.object_id AND i.index_id = s.index_id
2093
+ WHERE sch.name = @schemaName
2094
+ AND i.name IS NOT NULL
2095
+ GROUP BY i.name, o.name, i.is_unique
2096
+ `;
2097
+ }
2098
+ const result = await request.query(query);
2099
+ const indexes = [];
2100
+ for (const row of result.recordset) {
2101
+ const colRequest = this.pool.request();
2102
+ colRequest.input("indexName", row.name);
2103
+ colRequest.input("schemaName", schemaName);
2104
+ const colResult = await colRequest.query(`
2105
+ SELECT c.name as column_name
2106
+ FROM sys.indexes i
2107
+ INNER JOIN sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id
2108
+ INNER JOIN sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id
2109
+ INNER JOIN sys.objects o ON i.object_id = o.object_id
2110
+ INNER JOIN sys.schemas s ON o.schema_id = s.schema_id
2111
+ WHERE i.name = @indexName
2112
+ AND s.name = @schemaName
2113
+ ORDER BY ic.key_ordinal
2114
+ `);
2115
+ indexes.push({
2116
+ name: row.name,
2117
+ table: row.table,
2118
+ columns: colResult.recordset.map((c) => c.column_name),
2119
+ unique: row.is_unique || false,
2120
+ size: row.size || "0 MB",
2121
+ definition: ""
2122
+ // MSSQL doesn't store definition like PG
2123
+ });
2124
+ }
2125
+ return indexes;
2126
+ } catch (error) {
2127
+ throw new MastraError(
2128
+ {
2129
+ id: createStorageErrorId("MSSQL", "INDEX_LIST", "FAILED"),
2130
+ domain: ErrorDomain.STORAGE,
2131
+ category: ErrorCategory.THIRD_PARTY,
2132
+ details: tableName ? {
2133
+ tableName
2134
+ } : {}
2135
+ },
2136
+ error
2137
+ );
2138
+ }
2139
+ }
2140
+ /**
2141
+ * Get detailed statistics for a specific index
2142
+ */
2143
+ async describeIndex(indexName) {
2144
+ try {
2145
+ const schemaName = this.schemaName || "dbo";
2146
+ const request = this.pool.request();
2147
+ request.input("indexName", indexName);
2148
+ request.input("schemaName", schemaName);
2149
+ const query = `
2150
+ SELECT
2151
+ i.name as name,
2152
+ o.name as [table],
2153
+ i.is_unique as is_unique,
2154
+ CAST(SUM(s.used_page_count) * 8 / 1024.0 AS VARCHAR(50)) + ' MB' as size,
2155
+ i.type_desc as method,
2156
+ ISNULL(us.user_scans, 0) as scans,
2157
+ ISNULL(us.user_seeks + us.user_scans, 0) as tuples_read,
2158
+ ISNULL(us.user_lookups, 0) as tuples_fetched
2159
+ FROM sys.indexes i
2160
+ INNER JOIN sys.objects o ON i.object_id = o.object_id
2161
+ INNER JOIN sys.schemas sch ON o.schema_id = sch.schema_id
2162
+ LEFT JOIN sys.dm_db_partition_stats s ON i.object_id = s.object_id AND i.index_id = s.index_id
2163
+ LEFT JOIN sys.dm_db_index_usage_stats us ON i.object_id = us.object_id AND i.index_id = us.index_id
2164
+ WHERE i.name = @indexName
2165
+ AND sch.name = @schemaName
2166
+ GROUP BY i.name, o.name, i.is_unique, i.type_desc, us.user_seeks, us.user_scans, us.user_lookups
2167
+ `;
2168
+ const result = await request.query(query);
2169
+ if (!result.recordset || result.recordset.length === 0) {
2170
+ throw new Error(`Index "${indexName}" not found in schema "${schemaName}"`);
2171
+ }
2172
+ const row = result.recordset[0];
2173
+ const colRequest = this.pool.request();
2174
+ colRequest.input("indexName", indexName);
2175
+ colRequest.input("schemaName", schemaName);
2176
+ const colResult = await colRequest.query(`
2177
+ SELECT c.name as column_name
2178
+ FROM sys.indexes i
2179
+ INNER JOIN sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id
2180
+ INNER JOIN sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id
2181
+ INNER JOIN sys.objects o ON i.object_id = o.object_id
2182
+ INNER JOIN sys.schemas s ON o.schema_id = s.schema_id
2183
+ WHERE i.name = @indexName
2184
+ AND s.name = @schemaName
2185
+ ORDER BY ic.key_ordinal
2186
+ `);
2187
+ return {
2188
+ name: row.name,
2189
+ table: row.table,
2190
+ columns: colResult.recordset.map((c) => c.column_name),
2191
+ unique: row.is_unique || false,
2192
+ size: row.size || "0 MB",
2193
+ definition: "",
2194
+ method: row.method?.toLowerCase() || "nonclustered",
2195
+ scans: Number(row.scans) || 0,
2196
+ tuples_read: Number(row.tuples_read) || 0,
2197
+ tuples_fetched: Number(row.tuples_fetched) || 0
2198
+ };
2199
+ } catch (error) {
2200
+ throw new MastraError(
2201
+ {
2202
+ id: createStorageErrorId("MSSQL", "INDEX_DESCRIBE", "FAILED"),
2203
+ domain: ErrorDomain.STORAGE,
2204
+ category: ErrorCategory.THIRD_PARTY,
2205
+ details: {
2206
+ indexName
2207
+ }
2208
+ },
2209
+ error
2210
+ );
2211
+ }
2212
+ }
2213
+ /**
2214
+ * Returns definitions for automatic performance indexes
2215
+ * IMPORTANT: Uses seq_id DESC instead of createdAt DESC for MSSQL due to millisecond accuracy limitations
2216
+ * NOTE: Using NVARCHAR(400) for text columns (800 bytes) leaves room for composite indexes
2217
+ */
2218
+ getAutomaticIndexDefinitions() {
2219
+ const schemaPrefix = this.schemaName ? `${this.schemaName}_` : "";
2220
+ return [
2221
+ // Composite indexes for optimal filtering + sorting performance
2222
+ // NVARCHAR(400) = 800 bytes, plus BIGINT (8 bytes) = 808 bytes total (under 900-byte limit)
2223
+ {
2224
+ name: `${schemaPrefix}mastra_threads_resourceid_seqid_idx`,
2225
+ table: TABLE_THREADS,
2226
+ columns: ["resourceId", "seq_id DESC"]
2227
+ },
2228
+ {
2229
+ name: `${schemaPrefix}mastra_messages_thread_id_seqid_idx`,
2230
+ table: TABLE_MESSAGES,
2231
+ columns: ["thread_id", "seq_id DESC"]
2232
+ },
2233
+ {
2234
+ name: `${schemaPrefix}mastra_traces_name_seqid_idx`,
2235
+ table: TABLE_TRACES,
2236
+ columns: ["name", "seq_id DESC"]
2237
+ },
2238
+ {
2239
+ name: `${schemaPrefix}mastra_scores_trace_id_span_id_seqid_idx`,
2240
+ table: TABLE_SCORERS,
2241
+ columns: ["traceId", "spanId", "seq_id DESC"]
2242
+ },
2243
+ // Spans indexes for optimal trace querying
2244
+ {
2245
+ name: `${schemaPrefix}mastra_ai_spans_traceid_startedat_idx`,
2246
+ table: TABLE_SPANS,
2247
+ columns: ["traceId", "startedAt DESC"]
2248
+ },
2249
+ {
2250
+ name: `${schemaPrefix}mastra_ai_spans_parentspanid_startedat_idx`,
2251
+ table: TABLE_SPANS,
2252
+ columns: ["parentSpanId", "startedAt DESC"]
2253
+ },
2254
+ {
2255
+ name: `${schemaPrefix}mastra_ai_spans_name_idx`,
2256
+ table: TABLE_SPANS,
2257
+ columns: ["name"]
2258
+ },
2259
+ {
2260
+ name: `${schemaPrefix}mastra_ai_spans_spantype_startedat_idx`,
2261
+ table: TABLE_SPANS,
2262
+ columns: ["spanType", "startedAt DESC"]
2263
+ }
2264
+ ];
2265
+ }
2266
+ /**
2267
+ * Creates automatic indexes for optimal query performance
2268
+ * Uses getAutomaticIndexDefinitions() to determine which indexes to create
2269
+ */
2270
+ async createAutomaticIndexes() {
2271
+ try {
2272
+ const indexes = this.getAutomaticIndexDefinitions();
2273
+ for (const indexOptions of indexes) {
2274
+ try {
2275
+ await this.createIndex(indexOptions);
2276
+ } catch (error) {
2277
+ this.logger?.warn?.(`Failed to create index ${indexOptions.name}:`, error);
2278
+ }
2279
+ }
2280
+ } catch (error) {
2281
+ throw new MastraError(
2282
+ {
2283
+ id: createStorageErrorId("MSSQL", "CREATE_PERFORMANCE_INDEXES", "FAILED"),
2284
+ domain: ErrorDomain.STORAGE,
2285
+ category: ErrorCategory.THIRD_PARTY
2286
+ },
2287
+ error
2288
+ );
2289
+ }
2290
+ }
2291
+ };
1383
2292
  function transformScoreRow(row) {
1384
- return {
1385
- ...row,
1386
- input: parseJSON(row.input),
1387
- scorer: parseJSON(row.scorer),
1388
- preprocessStepResult: parseJSON(row.preprocessStepResult),
1389
- analyzeStepResult: parseJSON(row.analyzeStepResult),
1390
- metadata: parseJSON(row.metadata),
1391
- output: parseJSON(row.output),
1392
- additionalContext: parseJSON(row.additionalContext),
1393
- runtimeContext: parseJSON(row.runtimeContext),
1394
- entity: parseJSON(row.entity),
1395
- createdAt: row.createdAt,
1396
- updatedAt: row.updatedAt
1397
- };
2293
+ return transformScoreRow$1(row, {
2294
+ convertTimestamps: true
2295
+ });
1398
2296
  }
1399
2297
  var ScoresMSSQL = class extends ScoresStorage {
1400
2298
  pool;
@@ -1424,7 +2322,7 @@ var ScoresMSSQL = class extends ScoresStorage {
1424
2322
  } catch (error) {
1425
2323
  throw new MastraError(
1426
2324
  {
1427
- id: "MASTRA_STORAGE_MSSQL_STORE_GET_SCORE_BY_ID_FAILED",
2325
+ id: createStorageErrorId("MSSQL", "GET_SCORE_BY_ID", "FAILED"),
1428
2326
  domain: ErrorDomain.STORAGE,
1429
2327
  category: ErrorCategory.THIRD_PARTY,
1430
2328
  details: { id }
@@ -1432,10 +2330,31 @@ var ScoresMSSQL = class extends ScoresStorage {
1432
2330
  error
1433
2331
  );
1434
2332
  }
1435
- }
1436
- async saveScore(score) {
2333
+ }
2334
+ async saveScore(score) {
2335
+ let validatedScore;
2336
+ try {
2337
+ validatedScore = saveScorePayloadSchema.parse(score);
2338
+ } catch (error) {
2339
+ throw new MastraError(
2340
+ {
2341
+ id: createStorageErrorId("MSSQL", "SAVE_SCORE", "VALIDATION_FAILED"),
2342
+ domain: ErrorDomain.STORAGE,
2343
+ category: ErrorCategory.USER,
2344
+ details: {
2345
+ scorer: score.scorer?.id ?? "unknown",
2346
+ entityId: score.entityId ?? "unknown",
2347
+ entityType: score.entityType ?? "unknown",
2348
+ traceId: score.traceId ?? "",
2349
+ spanId: score.spanId ?? ""
2350
+ }
2351
+ },
2352
+ error
2353
+ );
2354
+ }
1437
2355
  try {
1438
- const scoreId = crypto.randomUUID();
2356
+ const scoreId = randomUUID();
2357
+ const now = /* @__PURE__ */ new Date();
1439
2358
  const {
1440
2359
  scorer,
1441
2360
  preprocessStepResult,
@@ -1444,34 +2363,33 @@ var ScoresMSSQL = class extends ScoresStorage {
1444
2363
  input,
1445
2364
  output,
1446
2365
  additionalContext,
1447
- runtimeContext,
2366
+ requestContext,
1448
2367
  entity,
1449
2368
  ...rest
1450
- } = score;
2369
+ } = validatedScore;
1451
2370
  await this.operations.insert({
1452
2371
  tableName: TABLE_SCORERS,
1453
2372
  record: {
1454
2373
  id: scoreId,
1455
2374
  ...rest,
1456
- input: JSON.stringify(input) || "",
1457
- output: JSON.stringify(output) || "",
1458
- preprocessStepResult: preprocessStepResult ? JSON.stringify(preprocessStepResult) : null,
1459
- analyzeStepResult: analyzeStepResult ? JSON.stringify(analyzeStepResult) : null,
1460
- metadata: metadata ? JSON.stringify(metadata) : null,
1461
- additionalContext: additionalContext ? JSON.stringify(additionalContext) : null,
1462
- runtimeContext: runtimeContext ? JSON.stringify(runtimeContext) : null,
1463
- entity: entity ? JSON.stringify(entity) : null,
1464
- scorer: scorer ? JSON.stringify(scorer) : null,
1465
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1466
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
2375
+ input: input || "",
2376
+ output: output || "",
2377
+ preprocessStepResult: preprocessStepResult || null,
2378
+ analyzeStepResult: analyzeStepResult || null,
2379
+ metadata: metadata || null,
2380
+ additionalContext: additionalContext || null,
2381
+ requestContext: requestContext || null,
2382
+ entity: entity || null,
2383
+ scorer: scorer || null,
2384
+ createdAt: now.toISOString(),
2385
+ updatedAt: now.toISOString()
1467
2386
  }
1468
2387
  });
1469
- const scoreFromDb = await this.getScoreById({ id: scoreId });
1470
- return { score: scoreFromDb };
2388
+ return { score: { ...validatedScore, id: scoreId, createdAt: now, updatedAt: now } };
1471
2389
  } catch (error) {
1472
2390
  throw new MastraError(
1473
2391
  {
1474
- id: "MASTRA_STORAGE_MSSQL_STORE_SAVE_SCORE_FAILED",
2392
+ id: createStorageErrorId("MSSQL", "SAVE_SCORE", "FAILED"),
1475
2393
  domain: ErrorDomain.STORAGE,
1476
2394
  category: ErrorCategory.THIRD_PARTY
1477
2395
  },
@@ -1479,48 +2397,77 @@ var ScoresMSSQL = class extends ScoresStorage {
1479
2397
  );
1480
2398
  }
1481
2399
  }
1482
- async getScoresByScorerId({
2400
+ async listScoresByScorerId({
1483
2401
  scorerId,
1484
- pagination
2402
+ pagination,
2403
+ entityId,
2404
+ entityType,
2405
+ source
1485
2406
  }) {
1486
2407
  try {
1487
- const request = this.pool.request();
1488
- request.input("p1", scorerId);
1489
- const totalResult = await request.query(
1490
- `SELECT COUNT(*) as count FROM ${getTableName({ indexName: TABLE_SCORERS, schemaName: getSchemaName(this.schema) })} WHERE [scorerId] = @p1`
1491
- );
2408
+ const conditions = ["[scorerId] = @p1"];
2409
+ const params = { p1: scorerId };
2410
+ let paramIndex = 2;
2411
+ if (entityId) {
2412
+ conditions.push(`[entityId] = @p${paramIndex}`);
2413
+ params[`p${paramIndex}`] = entityId;
2414
+ paramIndex++;
2415
+ }
2416
+ if (entityType) {
2417
+ conditions.push(`[entityType] = @p${paramIndex}`);
2418
+ params[`p${paramIndex}`] = entityType;
2419
+ paramIndex++;
2420
+ }
2421
+ if (source) {
2422
+ conditions.push(`[source] = @p${paramIndex}`);
2423
+ params[`p${paramIndex}`] = source;
2424
+ paramIndex++;
2425
+ }
2426
+ const whereClause = conditions.join(" AND ");
2427
+ const tableName = getTableName({ indexName: TABLE_SCORERS, schemaName: getSchemaName(this.schema) });
2428
+ const countRequest = this.pool.request();
2429
+ Object.entries(params).forEach(([key, value]) => {
2430
+ countRequest.input(key, value);
2431
+ });
2432
+ const totalResult = await countRequest.query(`SELECT COUNT(*) as count FROM ${tableName} WHERE ${whereClause}`);
1492
2433
  const total = totalResult.recordset[0]?.count || 0;
2434
+ const { page, perPage: perPageInput } = pagination;
1493
2435
  if (total === 0) {
1494
2436
  return {
1495
2437
  pagination: {
1496
2438
  total: 0,
1497
- page: pagination.page,
1498
- perPage: pagination.perPage,
2439
+ page,
2440
+ perPage: perPageInput,
1499
2441
  hasMore: false
1500
2442
  },
1501
2443
  scores: []
1502
2444
  };
1503
2445
  }
2446
+ const perPage = normalizePerPage(perPageInput, 100);
2447
+ const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
2448
+ const limitValue = perPageInput === false ? total : perPage;
2449
+ const end = perPageInput === false ? total : start + perPage;
1504
2450
  const dataRequest = this.pool.request();
1505
- dataRequest.input("p1", scorerId);
1506
- dataRequest.input("p2", pagination.perPage);
1507
- dataRequest.input("p3", pagination.page * pagination.perPage);
1508
- const result = await dataRequest.query(
1509
- `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`
1510
- );
2451
+ Object.entries(params).forEach(([key, value]) => {
2452
+ dataRequest.input(key, value);
2453
+ });
2454
+ dataRequest.input("perPage", limitValue);
2455
+ dataRequest.input("offset", start);
2456
+ const dataQuery = `SELECT * FROM ${tableName} WHERE ${whereClause} ORDER BY [createdAt] DESC OFFSET @offset ROWS FETCH NEXT @perPage ROWS ONLY`;
2457
+ const result = await dataRequest.query(dataQuery);
1511
2458
  return {
1512
2459
  pagination: {
1513
2460
  total: Number(total),
1514
- page: pagination.page,
1515
- perPage: pagination.perPage,
1516
- hasMore: Number(total) > (pagination.page + 1) * pagination.perPage
2461
+ page,
2462
+ perPage: perPageForResponse,
2463
+ hasMore: end < total
1517
2464
  },
1518
2465
  scores: result.recordset.map((row) => transformScoreRow(row))
1519
2466
  };
1520
2467
  } catch (error) {
1521
2468
  throw new MastraError(
1522
2469
  {
1523
- id: "MASTRA_STORAGE_MSSQL_STORE_GET_SCORES_BY_SCORER_ID_FAILED",
2470
+ id: createStorageErrorId("MSSQL", "LIST_SCORES_BY_SCORER_ID", "FAILED"),
1524
2471
  domain: ErrorDomain.STORAGE,
1525
2472
  category: ErrorCategory.THIRD_PARTY,
1526
2473
  details: { scorerId }
@@ -1529,7 +2476,7 @@ var ScoresMSSQL = class extends ScoresStorage {
1529
2476
  );
1530
2477
  }
1531
2478
  }
1532
- async getScoresByRunId({
2479
+ async listScoresByRunId({
1533
2480
  runId,
1534
2481
  pagination
1535
2482
  }) {
@@ -1540,37 +2487,42 @@ var ScoresMSSQL = class extends ScoresStorage {
1540
2487
  `SELECT COUNT(*) as count FROM ${getTableName({ indexName: TABLE_SCORERS, schemaName: getSchemaName(this.schema) })} WHERE [runId] = @p1`
1541
2488
  );
1542
2489
  const total = totalResult.recordset[0]?.count || 0;
2490
+ const { page, perPage: perPageInput } = pagination;
1543
2491
  if (total === 0) {
1544
2492
  return {
1545
2493
  pagination: {
1546
2494
  total: 0,
1547
- page: pagination.page,
1548
- perPage: pagination.perPage,
2495
+ page,
2496
+ perPage: perPageInput,
1549
2497
  hasMore: false
1550
2498
  },
1551
2499
  scores: []
1552
2500
  };
1553
2501
  }
2502
+ const perPage = normalizePerPage(perPageInput, 100);
2503
+ const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
2504
+ const limitValue = perPageInput === false ? total : perPage;
2505
+ const end = perPageInput === false ? total : start + perPage;
1554
2506
  const dataRequest = this.pool.request();
1555
2507
  dataRequest.input("p1", runId);
1556
- dataRequest.input("p2", pagination.perPage);
1557
- dataRequest.input("p3", pagination.page * pagination.perPage);
2508
+ dataRequest.input("p2", limitValue);
2509
+ dataRequest.input("p3", start);
1558
2510
  const result = await dataRequest.query(
1559
2511
  `SELECT * FROM ${getTableName({ indexName: TABLE_SCORERS, schemaName: getSchemaName(this.schema) })} WHERE [runId] = @p1 ORDER BY [createdAt] DESC OFFSET @p3 ROWS FETCH NEXT @p2 ROWS ONLY`
1560
2512
  );
1561
2513
  return {
1562
2514
  pagination: {
1563
2515
  total: Number(total),
1564
- page: pagination.page,
1565
- perPage: pagination.perPage,
1566
- hasMore: Number(total) > (pagination.page + 1) * pagination.perPage
2516
+ page,
2517
+ perPage: perPageForResponse,
2518
+ hasMore: end < total
1567
2519
  },
1568
2520
  scores: result.recordset.map((row) => transformScoreRow(row))
1569
2521
  };
1570
2522
  } catch (error) {
1571
2523
  throw new MastraError(
1572
2524
  {
1573
- id: "MASTRA_STORAGE_MSSQL_STORE_GET_SCORES_BY_RUN_ID_FAILED",
2525
+ id: createStorageErrorId("MSSQL", "LIST_SCORES_BY_RUN_ID", "FAILED"),
1574
2526
  domain: ErrorDomain.STORAGE,
1575
2527
  category: ErrorCategory.THIRD_PARTY,
1576
2528
  details: { runId }
@@ -1579,7 +2531,7 @@ var ScoresMSSQL = class extends ScoresStorage {
1579
2531
  );
1580
2532
  }
1581
2533
  }
1582
- async getScoresByEntityId({
2534
+ async listScoresByEntityId({
1583
2535
  entityId,
1584
2536
  entityType,
1585
2537
  pagination
@@ -1592,38 +2544,43 @@ var ScoresMSSQL = class extends ScoresStorage {
1592
2544
  `SELECT COUNT(*) as count FROM ${getTableName({ indexName: TABLE_SCORERS, schemaName: getSchemaName(this.schema) })} WHERE [entityId] = @p1 AND [entityType] = @p2`
1593
2545
  );
1594
2546
  const total = totalResult.recordset[0]?.count || 0;
2547
+ const { page, perPage: perPageInput } = pagination;
2548
+ const perPage = normalizePerPage(perPageInput, 100);
2549
+ const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
1595
2550
  if (total === 0) {
1596
2551
  return {
1597
2552
  pagination: {
1598
2553
  total: 0,
1599
- page: pagination.page,
1600
- perPage: pagination.perPage,
2554
+ page,
2555
+ perPage: perPageForResponse,
1601
2556
  hasMore: false
1602
2557
  },
1603
2558
  scores: []
1604
2559
  };
1605
2560
  }
2561
+ const limitValue = perPageInput === false ? total : perPage;
2562
+ const end = perPageInput === false ? total : start + perPage;
1606
2563
  const dataRequest = this.pool.request();
1607
2564
  dataRequest.input("p1", entityId);
1608
2565
  dataRequest.input("p2", entityType);
1609
- dataRequest.input("p3", pagination.perPage);
1610
- dataRequest.input("p4", pagination.page * pagination.perPage);
2566
+ dataRequest.input("p3", limitValue);
2567
+ dataRequest.input("p4", start);
1611
2568
  const result = await dataRequest.query(
1612
2569
  `SELECT * FROM ${getTableName({ indexName: TABLE_SCORERS, schemaName: getSchemaName(this.schema) })} WHERE [entityId] = @p1 AND [entityType] = @p2 ORDER BY [createdAt] DESC OFFSET @p4 ROWS FETCH NEXT @p3 ROWS ONLY`
1613
2570
  );
1614
2571
  return {
1615
2572
  pagination: {
1616
2573
  total: Number(total),
1617
- page: pagination.page,
1618
- perPage: pagination.perPage,
1619
- hasMore: Number(total) > (pagination.page + 1) * pagination.perPage
2574
+ page,
2575
+ perPage: perPageForResponse,
2576
+ hasMore: end < total
1620
2577
  },
1621
2578
  scores: result.recordset.map((row) => transformScoreRow(row))
1622
2579
  };
1623
2580
  } catch (error) {
1624
2581
  throw new MastraError(
1625
2582
  {
1626
- id: "MASTRA_STORAGE_MSSQL_STORE_GET_SCORES_BY_ENTITY_ID_FAILED",
2583
+ id: createStorageErrorId("MSSQL", "LIST_SCORES_BY_ENTITY_ID", "FAILED"),
1627
2584
  domain: ErrorDomain.STORAGE,
1628
2585
  category: ErrorCategory.THIRD_PARTY,
1629
2586
  details: { entityId, entityType }
@@ -1632,8 +2589,66 @@ var ScoresMSSQL = class extends ScoresStorage {
1632
2589
  );
1633
2590
  }
1634
2591
  }
2592
+ async listScoresBySpan({
2593
+ traceId,
2594
+ spanId,
2595
+ pagination
2596
+ }) {
2597
+ try {
2598
+ const request = this.pool.request();
2599
+ request.input("p1", traceId);
2600
+ request.input("p2", spanId);
2601
+ const totalResult = await request.query(
2602
+ `SELECT COUNT(*) as count FROM ${getTableName({ indexName: TABLE_SCORERS, schemaName: getSchemaName(this.schema) })} WHERE [traceId] = @p1 AND [spanId] = @p2`
2603
+ );
2604
+ const total = totalResult.recordset[0]?.count || 0;
2605
+ const { page, perPage: perPageInput } = pagination;
2606
+ const perPage = normalizePerPage(perPageInput, 100);
2607
+ const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
2608
+ if (total === 0) {
2609
+ return {
2610
+ pagination: {
2611
+ total: 0,
2612
+ page,
2613
+ perPage: perPageForResponse,
2614
+ hasMore: false
2615
+ },
2616
+ scores: []
2617
+ };
2618
+ }
2619
+ const limitValue = perPageInput === false ? total : perPage;
2620
+ const end = perPageInput === false ? total : start + perPage;
2621
+ const dataRequest = this.pool.request();
2622
+ dataRequest.input("p1", traceId);
2623
+ dataRequest.input("p2", spanId);
2624
+ dataRequest.input("p3", limitValue);
2625
+ dataRequest.input("p4", start);
2626
+ const result = await dataRequest.query(
2627
+ `SELECT * FROM ${getTableName({ indexName: TABLE_SCORERS, schemaName: getSchemaName(this.schema) })} WHERE [traceId] = @p1 AND [spanId] = @p2 ORDER BY [createdAt] DESC OFFSET @p4 ROWS FETCH NEXT @p3 ROWS ONLY`
2628
+ );
2629
+ return {
2630
+ pagination: {
2631
+ total: Number(total),
2632
+ page,
2633
+ perPage: perPageForResponse,
2634
+ hasMore: end < total
2635
+ },
2636
+ scores: result.recordset.map((row) => transformScoreRow(row))
2637
+ };
2638
+ } catch (error) {
2639
+ throw new MastraError(
2640
+ {
2641
+ id: createStorageErrorId("MSSQL", "LIST_SCORES_BY_SPAN", "FAILED"),
2642
+ domain: ErrorDomain.STORAGE,
2643
+ category: ErrorCategory.THIRD_PARTY,
2644
+ details: { traceId, spanId }
2645
+ },
2646
+ error
2647
+ );
2648
+ }
2649
+ }
1635
2650
  };
1636
- var TracesMSSQL = class extends TracesStorage {
2651
+ var WorkflowsMSSQL = class extends WorkflowsStorage {
1637
2652
  pool;
1638
2653
  operations;
1639
2654
  schema;
@@ -1647,194 +2662,170 @@ var TracesMSSQL = class extends TracesStorage {
1647
2662
  this.operations = operations;
1648
2663
  this.schema = schema;
1649
2664
  }
1650
- /** @deprecated use getTracesPaginated instead*/
1651
- async getTraces(args) {
1652
- if (args.fromDate || args.toDate) {
1653
- args.dateRange = {
1654
- start: args.fromDate,
1655
- end: args.toDate
1656
- };
2665
+ parseWorkflowRun(row) {
2666
+ let parsedSnapshot = row.snapshot;
2667
+ if (typeof parsedSnapshot === "string") {
2668
+ try {
2669
+ parsedSnapshot = JSON.parse(row.snapshot);
2670
+ } catch (e) {
2671
+ this.logger?.warn?.(`Failed to parse snapshot for workflow ${row.workflow_name}:`, e);
2672
+ }
1657
2673
  }
1658
- const result = await this.getTracesPaginated(args);
1659
- return result.traces;
2674
+ return {
2675
+ workflowName: row.workflow_name,
2676
+ runId: row.run_id,
2677
+ snapshot: parsedSnapshot,
2678
+ createdAt: row.createdAt,
2679
+ updatedAt: row.updatedAt,
2680
+ resourceId: row.resourceId
2681
+ };
1660
2682
  }
1661
- async getTracesPaginated(args) {
1662
- const { name, scope, page = 0, perPage: perPageInput, attributes, filters, dateRange } = args;
1663
- const fromDate = dateRange?.start;
1664
- const toDate = dateRange?.end;
1665
- const perPage = perPageInput !== void 0 ? perPageInput : 100;
1666
- const currentOffset = page * perPage;
1667
- const paramMap = {};
1668
- const conditions = [];
1669
- let paramIndex = 1;
1670
- if (name) {
1671
- const paramName = `p${paramIndex++}`;
1672
- conditions.push(`[name] LIKE @${paramName}`);
1673
- paramMap[paramName] = `${name}%`;
1674
- }
1675
- if (scope) {
1676
- const paramName = `p${paramIndex++}`;
1677
- conditions.push(`[scope] = @${paramName}`);
1678
- paramMap[paramName] = scope;
1679
- }
1680
- if (attributes) {
1681
- Object.entries(attributes).forEach(([key, value]) => {
1682
- const parsedKey = parseFieldKey(key);
1683
- const paramName = `p${paramIndex++}`;
1684
- conditions.push(`JSON_VALUE([attributes], '$.${parsedKey}') = @${paramName}`);
1685
- paramMap[paramName] = value;
1686
- });
1687
- }
1688
- if (filters) {
1689
- Object.entries(filters).forEach(([key, value]) => {
1690
- const parsedKey = parseFieldKey(key);
1691
- const paramName = `p${paramIndex++}`;
1692
- conditions.push(`[${parsedKey}] = @${paramName}`);
1693
- paramMap[paramName] = value;
1694
- });
1695
- }
1696
- if (fromDate instanceof Date && !isNaN(fromDate.getTime())) {
1697
- const paramName = `p${paramIndex++}`;
1698
- conditions.push(`[createdAt] >= @${paramName}`);
1699
- paramMap[paramName] = fromDate.toISOString();
1700
- }
1701
- if (toDate instanceof Date && !isNaN(toDate.getTime())) {
1702
- const paramName = `p${paramIndex++}`;
1703
- conditions.push(`[createdAt] <= @${paramName}`);
1704
- paramMap[paramName] = toDate.toISOString();
1705
- }
1706
- const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
1707
- const countQuery = `SELECT COUNT(*) as total FROM ${getTableName({ indexName: TABLE_TRACES, schemaName: getSchemaName(this.schema) })} ${whereClause}`;
1708
- let total = 0;
2683
+ async updateWorkflowResults({
2684
+ workflowName,
2685
+ runId,
2686
+ stepId,
2687
+ result,
2688
+ requestContext
2689
+ }) {
2690
+ const table = getTableName({ indexName: TABLE_WORKFLOW_SNAPSHOT, schemaName: getSchemaName(this.schema) });
2691
+ const transaction = this.pool.transaction();
1709
2692
  try {
1710
- const countRequest = this.pool.request();
1711
- Object.entries(paramMap).forEach(([key, value]) => {
1712
- if (value instanceof Date) {
1713
- countRequest.input(key, sql2.DateTime, value);
1714
- } else {
1715
- countRequest.input(key, value);
1716
- }
1717
- });
1718
- const countResult = await countRequest.query(countQuery);
1719
- total = parseInt(countResult.recordset[0].total, 10);
2693
+ await transaction.begin();
2694
+ const selectRequest = new sql3.Request(transaction);
2695
+ selectRequest.input("workflow_name", workflowName);
2696
+ selectRequest.input("run_id", runId);
2697
+ const existingSnapshotResult = await selectRequest.query(
2698
+ `SELECT snapshot FROM ${table} WITH (UPDLOCK, HOLDLOCK) WHERE workflow_name = @workflow_name AND run_id = @run_id`
2699
+ );
2700
+ let snapshot;
2701
+ if (!existingSnapshotResult.recordset || existingSnapshotResult.recordset.length === 0) {
2702
+ snapshot = {
2703
+ context: {},
2704
+ activePaths: [],
2705
+ activeStepsPath: {},
2706
+ timestamp: Date.now(),
2707
+ suspendedPaths: {},
2708
+ resumeLabels: {},
2709
+ serializedStepGraph: [],
2710
+ status: "pending",
2711
+ value: {},
2712
+ waitingPaths: {},
2713
+ runId,
2714
+ requestContext: {}
2715
+ };
2716
+ } else {
2717
+ const existingSnapshot = existingSnapshotResult.recordset[0].snapshot;
2718
+ snapshot = typeof existingSnapshot === "string" ? JSON.parse(existingSnapshot) : existingSnapshot;
2719
+ }
2720
+ snapshot.context[stepId] = result;
2721
+ snapshot.requestContext = { ...snapshot.requestContext, ...requestContext };
2722
+ const upsertReq = new sql3.Request(transaction);
2723
+ upsertReq.input("workflow_name", workflowName);
2724
+ upsertReq.input("run_id", runId);
2725
+ upsertReq.input("snapshot", JSON.stringify(snapshot));
2726
+ upsertReq.input("createdAt", sql3.DateTime2, /* @__PURE__ */ new Date());
2727
+ upsertReq.input("updatedAt", sql3.DateTime2, /* @__PURE__ */ new Date());
2728
+ await upsertReq.query(
2729
+ `MERGE ${table} AS target
2730
+ USING (SELECT @workflow_name AS workflow_name, @run_id AS run_id) AS src
2731
+ ON target.workflow_name = src.workflow_name AND target.run_id = src.run_id
2732
+ WHEN MATCHED THEN UPDATE SET snapshot = @snapshot, [updatedAt] = @updatedAt
2733
+ WHEN NOT MATCHED THEN INSERT (workflow_name, run_id, snapshot, [createdAt], [updatedAt])
2734
+ VALUES (@workflow_name, @run_id, @snapshot, @createdAt, @updatedAt);`
2735
+ );
2736
+ await transaction.commit();
2737
+ return snapshot.context;
1720
2738
  } catch (error) {
2739
+ try {
2740
+ await transaction.rollback();
2741
+ } catch {
2742
+ }
1721
2743
  throw new MastraError(
1722
2744
  {
1723
- id: "MASTRA_STORAGE_MSSQL_STORE_GET_TRACES_PAGINATED_FAILED_TO_RETRIEVE_TOTAL_COUNT",
2745
+ id: createStorageErrorId("MSSQL", "UPDATE_WORKFLOW_RESULTS", "FAILED"),
1724
2746
  domain: ErrorDomain.STORAGE,
1725
2747
  category: ErrorCategory.THIRD_PARTY,
1726
2748
  details: {
1727
- name: args.name ?? "",
1728
- scope: args.scope ?? ""
2749
+ workflowName,
2750
+ runId,
2751
+ stepId
1729
2752
  }
1730
2753
  },
1731
2754
  error
1732
2755
  );
1733
2756
  }
1734
- if (total === 0) {
1735
- return {
1736
- traces: [],
1737
- total: 0,
1738
- page,
1739
- perPage,
1740
- hasMore: false
1741
- };
1742
- }
1743
- 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`;
1744
- const dataRequest = this.pool.request();
1745
- Object.entries(paramMap).forEach(([key, value]) => {
1746
- if (value instanceof Date) {
1747
- dataRequest.input(key, sql2.DateTime, value);
1748
- } else {
1749
- dataRequest.input(key, value);
1750
- }
1751
- });
1752
- dataRequest.input("offset", currentOffset);
1753
- dataRequest.input("limit", perPage);
2757
+ }
2758
+ async updateWorkflowState({
2759
+ workflowName,
2760
+ runId,
2761
+ opts
2762
+ }) {
2763
+ const table = getTableName({ indexName: TABLE_WORKFLOW_SNAPSHOT, schemaName: getSchemaName(this.schema) });
2764
+ const transaction = this.pool.transaction();
1754
2765
  try {
1755
- const rowsResult = await dataRequest.query(dataQuery);
1756
- const rows = rowsResult.recordset;
1757
- const traces = rows.map((row) => ({
1758
- id: row.id,
1759
- parentSpanId: row.parentSpanId,
1760
- traceId: row.traceId,
1761
- name: row.name,
1762
- scope: row.scope,
1763
- kind: row.kind,
1764
- status: JSON.parse(row.status),
1765
- events: JSON.parse(row.events),
1766
- links: JSON.parse(row.links),
1767
- attributes: JSON.parse(row.attributes),
1768
- startTime: row.startTime,
1769
- endTime: row.endTime,
1770
- other: row.other,
1771
- createdAt: row.createdAt
1772
- }));
1773
- return {
1774
- traces,
1775
- total,
1776
- page,
1777
- perPage,
1778
- hasMore: currentOffset + traces.length < total
1779
- };
2766
+ await transaction.begin();
2767
+ const selectRequest = new sql3.Request(transaction);
2768
+ selectRequest.input("workflow_name", workflowName);
2769
+ selectRequest.input("run_id", runId);
2770
+ const existingSnapshotResult = await selectRequest.query(
2771
+ `SELECT snapshot FROM ${table} WITH (UPDLOCK, HOLDLOCK) WHERE workflow_name = @workflow_name AND run_id = @run_id`
2772
+ );
2773
+ if (!existingSnapshotResult.recordset || existingSnapshotResult.recordset.length === 0) {
2774
+ await transaction.rollback();
2775
+ return void 0;
2776
+ }
2777
+ const existingSnapshot = existingSnapshotResult.recordset[0].snapshot;
2778
+ const snapshot = typeof existingSnapshot === "string" ? JSON.parse(existingSnapshot) : existingSnapshot;
2779
+ if (!snapshot || !snapshot?.context) {
2780
+ await transaction.rollback();
2781
+ throw new MastraError(
2782
+ {
2783
+ id: createStorageErrorId("MSSQL", "UPDATE_WORKFLOW_STATE", "SNAPSHOT_NOT_FOUND"),
2784
+ domain: ErrorDomain.STORAGE,
2785
+ category: ErrorCategory.SYSTEM,
2786
+ details: {
2787
+ workflowName,
2788
+ runId
2789
+ }
2790
+ },
2791
+ new Error(`Snapshot not found for runId ${runId}`)
2792
+ );
2793
+ }
2794
+ const updatedSnapshot = { ...snapshot, ...opts };
2795
+ const updateRequest = new sql3.Request(transaction);
2796
+ updateRequest.input("snapshot", JSON.stringify(updatedSnapshot));
2797
+ updateRequest.input("workflow_name", workflowName);
2798
+ updateRequest.input("run_id", runId);
2799
+ updateRequest.input("updatedAt", sql3.DateTime2, /* @__PURE__ */ new Date());
2800
+ await updateRequest.query(
2801
+ `UPDATE ${table} SET snapshot = @snapshot, [updatedAt] = @updatedAt WHERE workflow_name = @workflow_name AND run_id = @run_id`
2802
+ );
2803
+ await transaction.commit();
2804
+ return updatedSnapshot;
1780
2805
  } catch (error) {
2806
+ try {
2807
+ await transaction.rollback();
2808
+ } catch {
2809
+ }
2810
+ if (error instanceof MastraError) throw error;
1781
2811
  throw new MastraError(
1782
2812
  {
1783
- id: "MASTRA_STORAGE_MSSQL_STORE_GET_TRACES_PAGINATED_FAILED_TO_RETRIEVE_TRACES",
2813
+ id: createStorageErrorId("MSSQL", "UPDATE_WORKFLOW_STATE", "FAILED"),
1784
2814
  domain: ErrorDomain.STORAGE,
1785
2815
  category: ErrorCategory.THIRD_PARTY,
1786
2816
  details: {
1787
- name: args.name ?? "",
1788
- scope: args.scope ?? ""
2817
+ workflowName,
2818
+ runId
1789
2819
  }
1790
2820
  },
1791
2821
  error
1792
2822
  );
1793
2823
  }
1794
2824
  }
1795
- async batchTraceInsert({ records }) {
1796
- this.logger.debug("Batch inserting traces", { count: records.length });
1797
- await this.operations.batchInsert({
1798
- tableName: TABLE_TRACES,
1799
- records
1800
- });
1801
- }
1802
- };
1803
- function parseWorkflowRun(row) {
1804
- let parsedSnapshot = row.snapshot;
1805
- if (typeof parsedSnapshot === "string") {
1806
- try {
1807
- parsedSnapshot = JSON.parse(row.snapshot);
1808
- } catch (e) {
1809
- console.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
1810
- }
1811
- }
1812
- return {
1813
- workflowName: row.workflow_name,
1814
- runId: row.run_id,
1815
- snapshot: parsedSnapshot,
1816
- createdAt: row.createdAt,
1817
- updatedAt: row.updatedAt,
1818
- resourceId: row.resourceId
1819
- };
1820
- }
1821
- var WorkflowsMSSQL = class extends WorkflowsStorage {
1822
- pool;
1823
- operations;
1824
- schema;
1825
- constructor({
1826
- pool,
1827
- operations,
1828
- schema
1829
- }) {
1830
- super();
1831
- this.pool = pool;
1832
- this.operations = operations;
1833
- this.schema = schema;
1834
- }
1835
2825
  async persistWorkflowSnapshot({
1836
2826
  workflowName,
1837
2827
  runId,
2828
+ resourceId,
1838
2829
  snapshot
1839
2830
  }) {
1840
2831
  const table = getTableName({ indexName: TABLE_WORKFLOW_SNAPSHOT, schemaName: getSchemaName(this.schema) });
@@ -1843,22 +2834,24 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
1843
2834
  const request = this.pool.request();
1844
2835
  request.input("workflow_name", workflowName);
1845
2836
  request.input("run_id", runId);
2837
+ request.input("resourceId", resourceId);
1846
2838
  request.input("snapshot", JSON.stringify(snapshot));
1847
- request.input("createdAt", sql2.DateTime2, new Date(now));
1848
- request.input("updatedAt", sql2.DateTime2, new Date(now));
2839
+ request.input("createdAt", sql3.DateTime2, new Date(now));
2840
+ request.input("updatedAt", sql3.DateTime2, new Date(now));
1849
2841
  const mergeSql = `MERGE INTO ${table} AS target
1850
2842
  USING (SELECT @workflow_name AS workflow_name, @run_id AS run_id) AS src
1851
2843
  ON target.workflow_name = src.workflow_name AND target.run_id = src.run_id
1852
2844
  WHEN MATCHED THEN UPDATE SET
2845
+ resourceId = @resourceId,
1853
2846
  snapshot = @snapshot,
1854
2847
  [updatedAt] = @updatedAt
1855
- WHEN NOT MATCHED THEN INSERT (workflow_name, run_id, snapshot, [createdAt], [updatedAt])
1856
- VALUES (@workflow_name, @run_id, @snapshot, @createdAt, @updatedAt);`;
2848
+ WHEN NOT MATCHED THEN INSERT (workflow_name, run_id, resourceId, snapshot, [createdAt], [updatedAt])
2849
+ VALUES (@workflow_name, @run_id, @resourceId, @snapshot, @createdAt, @updatedAt);`;
1857
2850
  await request.query(mergeSql);
1858
2851
  } catch (error) {
1859
2852
  throw new MastraError(
1860
2853
  {
1861
- id: "MASTRA_STORAGE_MSSQL_STORE_PERSIST_WORKFLOW_SNAPSHOT_FAILED",
2854
+ id: createStorageErrorId("MSSQL", "PERSIST_WORKFLOW_SNAPSHOT", "FAILED"),
1862
2855
  domain: ErrorDomain.STORAGE,
1863
2856
  category: ErrorCategory.THIRD_PARTY,
1864
2857
  details: {
@@ -1889,7 +2882,7 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
1889
2882
  } catch (error) {
1890
2883
  throw new MastraError(
1891
2884
  {
1892
- id: "MASTRA_STORAGE_MSSQL_STORE_LOAD_WORKFLOW_SNAPSHOT_FAILED",
2885
+ id: createStorageErrorId("MSSQL", "LOAD_WORKFLOW_SNAPSHOT", "FAILED"),
1893
2886
  domain: ErrorDomain.STORAGE,
1894
2887
  category: ErrorCategory.THIRD_PARTY,
1895
2888
  details: {
@@ -1925,11 +2918,11 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
1925
2918
  if (!result.recordset || result.recordset.length === 0) {
1926
2919
  return null;
1927
2920
  }
1928
- return parseWorkflowRun(result.recordset[0]);
2921
+ return this.parseWorkflowRun(result.recordset[0]);
1929
2922
  } catch (error) {
1930
2923
  throw new MastraError(
1931
2924
  {
1932
- id: "MASTRA_STORAGE_MSSQL_STORE_GET_WORKFLOW_RUN_BY_ID_FAILED",
2925
+ id: createStorageErrorId("MSSQL", "GET_WORKFLOW_RUN_BY_ID", "FAILED"),
1933
2926
  domain: ErrorDomain.STORAGE,
1934
2927
  category: ErrorCategory.THIRD_PARTY,
1935
2928
  details: {
@@ -1941,13 +2934,43 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
1941
2934
  );
1942
2935
  }
1943
2936
  }
1944
- async getWorkflowRuns({
2937
+ async deleteWorkflowRunById({ runId, workflowName }) {
2938
+ const table = getTableName({ indexName: TABLE_WORKFLOW_SNAPSHOT, schemaName: getSchemaName(this.schema) });
2939
+ const transaction = this.pool.transaction();
2940
+ try {
2941
+ await transaction.begin();
2942
+ const deleteRequest = new sql3.Request(transaction);
2943
+ deleteRequest.input("workflow_name", workflowName);
2944
+ deleteRequest.input("run_id", runId);
2945
+ await deleteRequest.query(`DELETE FROM ${table} WHERE workflow_name = @workflow_name AND run_id = @run_id`);
2946
+ await transaction.commit();
2947
+ } catch (error) {
2948
+ try {
2949
+ await transaction.rollback();
2950
+ } catch {
2951
+ }
2952
+ throw new MastraError(
2953
+ {
2954
+ id: createStorageErrorId("MSSQL", "DELETE_WORKFLOW_RUN_BY_ID", "FAILED"),
2955
+ domain: ErrorDomain.STORAGE,
2956
+ category: ErrorCategory.THIRD_PARTY,
2957
+ details: {
2958
+ runId,
2959
+ workflowName
2960
+ }
2961
+ },
2962
+ error
2963
+ );
2964
+ }
2965
+ }
2966
+ async listWorkflowRuns({
1945
2967
  workflowName,
1946
2968
  fromDate,
1947
2969
  toDate,
1948
- limit,
1949
- offset,
1950
- resourceId
2970
+ page,
2971
+ perPage,
2972
+ resourceId,
2973
+ status
1951
2974
  } = {}) {
1952
2975
  try {
1953
2976
  const conditions = [];
@@ -1956,13 +2979,17 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
1956
2979
  conditions.push(`[workflow_name] = @workflowName`);
1957
2980
  paramMap["workflowName"] = workflowName;
1958
2981
  }
2982
+ if (status) {
2983
+ conditions.push(`JSON_VALUE([snapshot], '$.status') = @status`);
2984
+ paramMap["status"] = status;
2985
+ }
1959
2986
  if (resourceId) {
1960
2987
  const hasResourceId = await this.operations.hasColumn(TABLE_WORKFLOW_SNAPSHOT, "resourceId");
1961
2988
  if (hasResourceId) {
1962
2989
  conditions.push(`[resourceId] = @resourceId`);
1963
2990
  paramMap["resourceId"] = resourceId;
1964
2991
  } else {
1965
- console.warn(`[${TABLE_WORKFLOW_SNAPSHOT}] resourceId column not found. Skipping resourceId filter.`);
2992
+ this.logger?.warn?.(`[${TABLE_WORKFLOW_SNAPSHOT}] resourceId column not found. Skipping resourceId filter.`);
1966
2993
  }
1967
2994
  }
1968
2995
  if (fromDate instanceof Date && !isNaN(fromDate.getTime())) {
@@ -1979,29 +3006,32 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
1979
3006
  const request = this.pool.request();
1980
3007
  Object.entries(paramMap).forEach(([key, value]) => {
1981
3008
  if (value instanceof Date) {
1982
- request.input(key, sql2.DateTime, value);
3009
+ request.input(key, sql3.DateTime, value);
1983
3010
  } else {
1984
3011
  request.input(key, value);
1985
3012
  }
1986
3013
  });
1987
- if (limit !== void 0 && offset !== void 0) {
3014
+ const usePagination = typeof perPage === "number" && typeof page === "number";
3015
+ if (usePagination) {
1988
3016
  const countQuery = `SELECT COUNT(*) as count FROM ${tableName} ${whereClause}`;
1989
3017
  const countResult = await request.query(countQuery);
1990
3018
  total = Number(countResult.recordset[0]?.count || 0);
1991
3019
  }
1992
3020
  let query = `SELECT * FROM ${tableName} ${whereClause} ORDER BY [seq_id] DESC`;
1993
- if (limit !== void 0 && offset !== void 0) {
1994
- query += ` OFFSET @offset ROWS FETCH NEXT @limit ROWS ONLY`;
1995
- request.input("limit", limit);
3021
+ if (usePagination) {
3022
+ const normalizedPerPage = normalizePerPage(perPage, Number.MAX_SAFE_INTEGER);
3023
+ const offset = page * normalizedPerPage;
3024
+ query += ` OFFSET @offset ROWS FETCH NEXT @perPage ROWS ONLY`;
3025
+ request.input("perPage", normalizedPerPage);
1996
3026
  request.input("offset", offset);
1997
3027
  }
1998
3028
  const result = await request.query(query);
1999
- const runs = (result.recordset || []).map((row) => parseWorkflowRun(row));
3029
+ const runs = (result.recordset || []).map((row) => this.parseWorkflowRun(row));
2000
3030
  return { runs, total: total || runs.length };
2001
3031
  } catch (error) {
2002
3032
  throw new MastraError(
2003
3033
  {
2004
- id: "MASTRA_STORAGE_MSSQL_STORE_GET_WORKFLOW_RUNS_FAILED",
3034
+ id: createStorageErrorId("MSSQL", "LIST_WORKFLOW_RUNS", "FAILED"),
2005
3035
  domain: ErrorDomain.STORAGE,
2006
3036
  category: ErrorCategory.THIRD_PARTY,
2007
3037
  details: {
@@ -2021,7 +3051,10 @@ var MSSQLStore = class extends MastraStorage {
2021
3051
  isConnected = null;
2022
3052
  stores;
2023
3053
  constructor(config) {
2024
- super({ name: "MSSQLStore" });
3054
+ if (!config.id || typeof config.id !== "string" || config.id.trim() === "") {
3055
+ throw new Error("MSSQLStore: id must be provided and cannot be empty.");
3056
+ }
3057
+ super({ id: config.id, name: "MSSQLStore", disableInit: config.disableInit });
2025
3058
  try {
2026
3059
  if ("connectionString" in config) {
2027
3060
  if (!config.connectionString || typeof config.connectionString !== "string" || config.connectionString.trim() === "") {
@@ -2036,7 +3069,7 @@ var MSSQLStore = class extends MastraStorage {
2036
3069
  }
2037
3070
  }
2038
3071
  this.schema = config.schemaName || "dbo";
2039
- this.pool = "connectionString" in config ? new sql2.ConnectionPool(config.connectionString) : new sql2.ConnectionPool({
3072
+ this.pool = "connectionString" in config ? new sql3.ConnectionPool(config.connectionString) : new sql3.ConnectionPool({
2040
3073
  server: config.server,
2041
3074
  database: config.database,
2042
3075
  user: config.user,
@@ -2044,24 +3077,22 @@ var MSSQLStore = class extends MastraStorage {
2044
3077
  port: config.port,
2045
3078
  options: config.options || { encrypt: true, trustServerCertificate: true }
2046
3079
  });
2047
- const legacyEvals = new LegacyEvalsMSSQL({ pool: this.pool, schema: this.schema });
2048
3080
  const operations = new StoreOperationsMSSQL({ pool: this.pool, schemaName: this.schema });
2049
3081
  const scores = new ScoresMSSQL({ pool: this.pool, operations, schema: this.schema });
2050
- const traces = new TracesMSSQL({ pool: this.pool, operations, schema: this.schema });
2051
3082
  const workflows = new WorkflowsMSSQL({ pool: this.pool, operations, schema: this.schema });
2052
3083
  const memory = new MemoryMSSQL({ pool: this.pool, schema: this.schema, operations });
3084
+ const observability = new ObservabilityMSSQL({ pool: this.pool, operations, schema: this.schema });
2053
3085
  this.stores = {
2054
3086
  operations,
2055
3087
  scores,
2056
- traces,
2057
3088
  workflows,
2058
- legacyEvals,
2059
- memory
3089
+ memory,
3090
+ observability
2060
3091
  };
2061
3092
  } catch (e) {
2062
3093
  throw new MastraError(
2063
3094
  {
2064
- id: "MASTRA_STORAGE_MSSQL_STORE_INITIALIZATION_FAILED",
3095
+ id: createStorageErrorId("MSSQL", "INITIALIZATION", "FAILED"),
2065
3096
  domain: ErrorDomain.STORAGE,
2066
3097
  category: ErrorCategory.USER
2067
3098
  },
@@ -2076,11 +3107,16 @@ var MSSQLStore = class extends MastraStorage {
2076
3107
  try {
2077
3108
  await this.isConnected;
2078
3109
  await super.init();
3110
+ try {
3111
+ await this.stores.operations.createAutomaticIndexes();
3112
+ } catch (indexError) {
3113
+ this.logger?.warn?.("Failed to create indexes:", indexError);
3114
+ }
2079
3115
  } catch (error) {
2080
3116
  this.isConnected = null;
2081
3117
  throw new MastraError(
2082
3118
  {
2083
- id: "MASTRA_STORAGE_MSSQL_STORE_INIT_FAILED",
3119
+ id: createStorageErrorId("MSSQL", "INIT", "FAILED"),
2084
3120
  domain: ErrorDomain.STORAGE,
2085
3121
  category: ErrorCategory.THIRD_PARTY
2086
3122
  },
@@ -2102,28 +3138,12 @@ var MSSQLStore = class extends MastraStorage {
2102
3138
  resourceWorkingMemory: true,
2103
3139
  hasColumn: true,
2104
3140
  createTable: true,
2105
- deleteMessages: true
3141
+ deleteMessages: true,
3142
+ listScoresBySpan: true,
3143
+ observabilityInstance: true,
3144
+ indexManagement: true
2106
3145
  };
2107
3146
  }
2108
- /** @deprecated use getEvals instead */
2109
- async getEvalsByAgentName(agentName, type) {
2110
- return this.stores.legacyEvals.getEvalsByAgentName(agentName, type);
2111
- }
2112
- async getEvals(options = {}) {
2113
- return this.stores.legacyEvals.getEvals(options);
2114
- }
2115
- /**
2116
- * @deprecated use getTracesPaginated instead
2117
- */
2118
- async getTraces(args) {
2119
- return this.stores.traces.getTraces(args);
2120
- }
2121
- async getTracesPaginated(args) {
2122
- return this.stores.traces.getTracesPaginated(args);
2123
- }
2124
- async batchTraceInsert({ records }) {
2125
- return this.stores.traces.batchTraceInsert({ records });
2126
- }
2127
3147
  async createTable({
2128
3148
  tableName,
2129
3149
  schema
@@ -2158,15 +3178,6 @@ var MSSQLStore = class extends MastraStorage {
2158
3178
  async getThreadById({ threadId }) {
2159
3179
  return this.stores.memory.getThreadById({ threadId });
2160
3180
  }
2161
- /**
2162
- * @deprecated use getThreadsByResourceIdPaginated instead
2163
- */
2164
- async getThreadsByResourceId(args) {
2165
- return this.stores.memory.getThreadsByResourceId(args);
2166
- }
2167
- async getThreadsByResourceIdPaginated(args) {
2168
- return this.stores.memory.getThreadsByResourceIdPaginated(args);
2169
- }
2170
3181
  async saveThread({ thread }) {
2171
3182
  return this.stores.memory.saveThread({ thread });
2172
3183
  }
@@ -2180,11 +3191,8 @@ var MSSQLStore = class extends MastraStorage {
2180
3191
  async deleteThread({ threadId }) {
2181
3192
  return this.stores.memory.deleteThread({ threadId });
2182
3193
  }
2183
- async getMessages(args) {
2184
- return this.stores.memory.getMessages(args);
2185
- }
2186
- async getMessagesPaginated(args) {
2187
- return this.stores.memory.getMessagesPaginated(args);
3194
+ async listMessagesById({ messageIds }) {
3195
+ return this.stores.memory.listMessagesById({ messageIds });
2188
3196
  }
2189
3197
  async saveMessages(args) {
2190
3198
  return this.stores.memory.saveMessages(args);
@@ -2213,12 +3221,29 @@ var MSSQLStore = class extends MastraStorage {
2213
3221
  /**
2214
3222
  * Workflows
2215
3223
  */
3224
+ async updateWorkflowResults({
3225
+ workflowName,
3226
+ runId,
3227
+ stepId,
3228
+ result,
3229
+ requestContext
3230
+ }) {
3231
+ return this.stores.workflows.updateWorkflowResults({ workflowName, runId, stepId, result, requestContext });
3232
+ }
3233
+ async updateWorkflowState({
3234
+ workflowName,
3235
+ runId,
3236
+ opts
3237
+ }) {
3238
+ return this.stores.workflows.updateWorkflowState({ workflowName, runId, opts });
3239
+ }
2216
3240
  async persistWorkflowSnapshot({
2217
3241
  workflowName,
2218
3242
  runId,
3243
+ resourceId,
2219
3244
  snapshot
2220
3245
  }) {
2221
- return this.stores.workflows.persistWorkflowSnapshot({ workflowName, runId, snapshot });
3246
+ return this.stores.workflows.persistWorkflowSnapshot({ workflowName, runId, resourceId, snapshot });
2222
3247
  }
2223
3248
  async loadWorkflowSnapshot({
2224
3249
  workflowName,
@@ -2226,15 +3251,8 @@ var MSSQLStore = class extends MastraStorage {
2226
3251
  }) {
2227
3252
  return this.stores.workflows.loadWorkflowSnapshot({ workflowName, runId });
2228
3253
  }
2229
- async getWorkflowRuns({
2230
- workflowName,
2231
- fromDate,
2232
- toDate,
2233
- limit,
2234
- offset,
2235
- resourceId
2236
- } = {}) {
2237
- return this.stores.workflows.getWorkflowRuns({ workflowName, fromDate, toDate, limit, offset, resourceId });
3254
+ async listWorkflowRuns(args = {}) {
3255
+ return this.stores.workflows.listWorkflowRuns(args);
2238
3256
  }
2239
3257
  async getWorkflowRunById({
2240
3258
  runId,
@@ -2242,41 +3260,114 @@ var MSSQLStore = class extends MastraStorage {
2242
3260
  }) {
2243
3261
  return this.stores.workflows.getWorkflowRunById({ runId, workflowName });
2244
3262
  }
3263
+ async deleteWorkflowRunById({ runId, workflowName }) {
3264
+ return this.stores.workflows.deleteWorkflowRunById({ runId, workflowName });
3265
+ }
2245
3266
  async close() {
2246
3267
  await this.pool.close();
2247
3268
  }
3269
+ /**
3270
+ * Index Management
3271
+ */
3272
+ async createIndex(options) {
3273
+ return this.stores.operations.createIndex(options);
3274
+ }
3275
+ async listIndexes(tableName) {
3276
+ return this.stores.operations.listIndexes(tableName);
3277
+ }
3278
+ async describeIndex(indexName) {
3279
+ return this.stores.operations.describeIndex(indexName);
3280
+ }
3281
+ async dropIndex(indexName) {
3282
+ return this.stores.operations.dropIndex(indexName);
3283
+ }
3284
+ /**
3285
+ * Tracing / Observability
3286
+ */
3287
+ getObservabilityStore() {
3288
+ if (!this.stores.observability) {
3289
+ throw new MastraError({
3290
+ id: createStorageErrorId("MSSQL", "OBSERVABILITY", "NOT_INITIALIZED"),
3291
+ domain: ErrorDomain.STORAGE,
3292
+ category: ErrorCategory.SYSTEM,
3293
+ text: "Observability storage is not initialized"
3294
+ });
3295
+ }
3296
+ return this.stores.observability;
3297
+ }
3298
+ async createSpan(span) {
3299
+ return this.getObservabilityStore().createSpan(span);
3300
+ }
3301
+ async updateSpan({
3302
+ spanId,
3303
+ traceId,
3304
+ updates
3305
+ }) {
3306
+ return this.getObservabilityStore().updateSpan({ spanId, traceId, updates });
3307
+ }
3308
+ async getTrace(traceId) {
3309
+ return this.getObservabilityStore().getTrace(traceId);
3310
+ }
3311
+ async getTracesPaginated(args) {
3312
+ return this.getObservabilityStore().getTracesPaginated(args);
3313
+ }
3314
+ async batchCreateSpans(args) {
3315
+ return this.getObservabilityStore().batchCreateSpans(args);
3316
+ }
3317
+ async batchUpdateSpans(args) {
3318
+ return this.getObservabilityStore().batchUpdateSpans(args);
3319
+ }
3320
+ async batchDeleteTraces(args) {
3321
+ return this.getObservabilityStore().batchDeleteTraces(args);
3322
+ }
2248
3323
  /**
2249
3324
  * Scorers
2250
3325
  */
2251
3326
  async getScoreById({ id: _id }) {
2252
3327
  return this.stores.scores.getScoreById({ id: _id });
2253
3328
  }
2254
- async getScoresByScorerId({
3329
+ async listScoresByScorerId({
2255
3330
  scorerId: _scorerId,
2256
- pagination: _pagination
3331
+ pagination: _pagination,
3332
+ entityId: _entityId,
3333
+ entityType: _entityType,
3334
+ source: _source
2257
3335
  }) {
2258
- return this.stores.scores.getScoresByScorerId({ scorerId: _scorerId, pagination: _pagination });
3336
+ return this.stores.scores.listScoresByScorerId({
3337
+ scorerId: _scorerId,
3338
+ pagination: _pagination,
3339
+ entityId: _entityId,
3340
+ entityType: _entityType,
3341
+ source: _source
3342
+ });
2259
3343
  }
2260
- async saveScore(_score) {
2261
- return this.stores.scores.saveScore(_score);
3344
+ async saveScore(score) {
3345
+ return this.stores.scores.saveScore(score);
2262
3346
  }
2263
- async getScoresByRunId({
3347
+ async listScoresByRunId({
2264
3348
  runId: _runId,
2265
3349
  pagination: _pagination
2266
3350
  }) {
2267
- return this.stores.scores.getScoresByRunId({ runId: _runId, pagination: _pagination });
3351
+ return this.stores.scores.listScoresByRunId({ runId: _runId, pagination: _pagination });
2268
3352
  }
2269
- async getScoresByEntityId({
3353
+ async listScoresByEntityId({
2270
3354
  entityId: _entityId,
2271
3355
  entityType: _entityType,
2272
3356
  pagination: _pagination
2273
3357
  }) {
2274
- return this.stores.scores.getScoresByEntityId({
3358
+ return this.stores.scores.listScoresByEntityId({
2275
3359
  entityId: _entityId,
2276
3360
  entityType: _entityType,
2277
3361
  pagination: _pagination
2278
3362
  });
2279
3363
  }
3364
+ async listScoresBySpan({
3365
+ traceId,
3366
+ spanId,
3367
+ pagination: _pagination
3368
+ }) {
3369
+ return this.stores.scores.listScoresBySpan({ traceId, spanId, pagination: _pagination });
3370
+ }
2280
3371
  };
2281
3372
 
2282
3373
  export { MSSQLStore };