@mastra/mssql 0.0.0-fix-11329-windows-path-20251222155941 → 0.0.0-fix-local-pkg-cwd-20251224015404

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -3,9 +3,9 @@
3
3
  var error = require('@mastra/core/error');
4
4
  var storage = require('@mastra/core/storage');
5
5
  var sql = require('mssql');
6
+ var agent = require('@mastra/core/agent');
6
7
  var base = require('@mastra/core/base');
7
8
  var utils = require('@mastra/core/utils');
8
- var agent = require('@mastra/core/agent');
9
9
  var crypto = require('crypto');
10
10
  var evals = require('@mastra/core/evals');
11
11
 
@@ -26,31 +26,89 @@ function getTableName({ indexName, schemaName }) {
26
26
 
27
27
  // src/storage/db/index.ts
28
28
  function resolveMssqlConfig(config) {
29
- if ("pool" in config && "db" in config) {
30
- return { pool: config.pool, db: config.db, schema: config.schema, needsConnect: false };
29
+ if ("pool" in config && !("server" in config)) {
30
+ return {
31
+ pool: config.pool,
32
+ schemaName: config.schemaName,
33
+ skipDefaultIndexes: config.skipDefaultIndexes,
34
+ indexes: config.indexes,
35
+ needsConnect: false
36
+ };
31
37
  }
38
+ const restConfig = config;
32
39
  const pool = new sql__default.default.ConnectionPool({
33
- server: config.server,
34
- database: config.database,
35
- user: config.user,
36
- password: config.password,
37
- port: config.port,
38
- options: config.options || { encrypt: true, trustServerCertificate: true }
40
+ server: restConfig.server,
41
+ database: restConfig.database,
42
+ user: restConfig.user,
43
+ password: restConfig.password,
44
+ port: restConfig.port,
45
+ options: restConfig.options || { encrypt: true, trustServerCertificate: true }
39
46
  });
40
- const db = new MssqlDB({ pool, schemaName: config.schemaName });
41
- return { pool, db, schema: config.schemaName, needsConnect: true };
47
+ return {
48
+ pool,
49
+ schemaName: restConfig.schemaName,
50
+ skipDefaultIndexes: restConfig.skipDefaultIndexes,
51
+ indexes: restConfig.indexes,
52
+ needsConnect: true
53
+ };
42
54
  }
43
55
  var MssqlDB = class extends base.MastraBase {
44
56
  pool;
45
57
  schemaName;
58
+ skipDefaultIndexes;
46
59
  setupSchemaPromise = null;
47
60
  schemaSetupComplete = void 0;
48
- getSqlType(type, isPrimaryKey = false, useLargeStorage = false) {
61
+ /**
62
+ * Columns that participate in composite indexes need smaller sizes (NVARCHAR(100)).
63
+ * MSSQL has a 900-byte index key limit, so composite indexes with NVARCHAR(400) columns fail.
64
+ * These are typically ID/type fields that don't need 400 chars.
65
+ */
66
+ COMPOSITE_INDEX_COLUMNS = [
67
+ "traceId",
68
+ // Used in: PRIMARY KEY (traceId, spanId), index (traceId, spanId, seq_id)
69
+ "spanId",
70
+ // Used in: PRIMARY KEY (traceId, spanId), index (traceId, spanId, seq_id)
71
+ "parentSpanId",
72
+ // Used in: index (parentSpanId, startedAt)
73
+ "entityType",
74
+ // Used in: (entityType, entityId), (entityType, entityName)
75
+ "entityId",
76
+ // Used in: (entityType, entityId)
77
+ "entityName",
78
+ // Used in: (entityType, entityName)
79
+ "organizationId",
80
+ // Used in: (organizationId, userId)
81
+ "userId"
82
+ // Used in: (organizationId, userId)
83
+ ];
84
+ /**
85
+ * Columns that store large amounts of data and should use NVARCHAR(MAX).
86
+ * Avoid listing columns that participate in indexes (resourceId, thread_id, agent_name, name, etc.)
87
+ */
88
+ LARGE_DATA_COLUMNS = [
89
+ "workingMemory",
90
+ "snapshot",
91
+ "metadata",
92
+ "content",
93
+ // messages.content - can be very long conversation content
94
+ "input",
95
+ // evals.input - test input data
96
+ "output",
97
+ // evals.output - test output data
98
+ "instructions",
99
+ // evals.instructions - evaluation instructions
100
+ "other"
101
+ // traces.other - additional trace data
102
+ ];
103
+ getSqlType(type, isPrimaryKey = false, useLargeStorage = false, useSmallStorage = false) {
49
104
  switch (type) {
50
105
  case "text":
51
106
  if (useLargeStorage) {
52
107
  return "NVARCHAR(MAX)";
53
108
  }
109
+ if (useSmallStorage) {
110
+ return "NVARCHAR(100)";
111
+ }
54
112
  return isPrimaryKey ? "NVARCHAR(255)" : "NVARCHAR(400)";
55
113
  case "timestamp":
56
114
  return "DATETIME2(7)";
@@ -74,10 +132,15 @@ var MssqlDB = class extends base.MastraBase {
74
132
  });
75
133
  }
76
134
  }
77
- constructor({ pool, schemaName }) {
135
+ constructor({
136
+ pool,
137
+ schemaName,
138
+ skipDefaultIndexes
139
+ }) {
78
140
  super({ component: "STORAGE", name: "MssqlDB" });
79
141
  this.pool = pool;
80
142
  this.schemaName = schemaName;
143
+ this.skipDefaultIndexes = skipDefaultIndexes;
81
144
  }
82
145
  async hasColumn(table, column) {
83
146
  const schema = this.schemaName || "dbo";
@@ -209,29 +272,15 @@ var MssqlDB = class extends base.MastraBase {
209
272
  }) {
210
273
  try {
211
274
  const uniqueConstraintColumns = tableName === storage.TABLE_WORKFLOW_SNAPSHOT ? ["workflow_name", "run_id"] : [];
212
- const largeDataColumns = [
213
- "workingMemory",
214
- "snapshot",
215
- "metadata",
216
- "content",
217
- // messages.content - can be very long conversation content
218
- "input",
219
- // evals.input - test input data
220
- "output",
221
- // evals.output - test output data
222
- "instructions",
223
- // evals.instructions - evaluation instructions
224
- "other"
225
- // traces.other - additional trace data
226
- ];
227
275
  const columns = Object.entries(schema).map(([name, def]) => {
228
276
  const parsedName = utils.parseSqlIdentifier(name, "column name");
229
277
  const constraints = [];
230
278
  if (def.primaryKey) constraints.push("PRIMARY KEY");
231
279
  if (!def.nullable) constraints.push("NOT NULL");
232
280
  const isIndexed = !!def.primaryKey || uniqueConstraintColumns.includes(name);
233
- const useLargeStorage = largeDataColumns.includes(name);
234
- return `[${parsedName}] ${this.getSqlType(def.type, isIndexed, useLargeStorage)} ${constraints.join(" ")}`.trim();
281
+ const useLargeStorage = this.LARGE_DATA_COLUMNS.includes(name);
282
+ const useSmallStorage = this.COMPOSITE_INDEX_COLUMNS.includes(name);
283
+ return `[${parsedName}] ${this.getSqlType(def.type, isIndexed, useLargeStorage, useSmallStorage)} ${constraints.join(" ")}`.trim();
235
284
  }).join(",\n");
236
285
  if (this.schemaName) {
237
286
  await this.setupSchema();
@@ -268,18 +317,37 @@ ${columns}
268
317
  const alterSql = `ALTER TABLE ${getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) })} ADD seq_id BIGINT IDENTITY(1,1)`;
269
318
  await this.pool.request().query(alterSql);
270
319
  }
320
+ const schemaPrefix = this.schemaName ? `${this.schemaName}_` : "";
271
321
  if (tableName === storage.TABLE_WORKFLOW_SNAPSHOT) {
272
- const constraintName = "mastra_workflow_snapshot_workflow_name_run_id_key";
322
+ const constraintName = `${schemaPrefix}mastra_workflow_snapshot_workflow_name_run_id_key`;
273
323
  const checkConstraintSql = `SELECT 1 AS found FROM sys.key_constraints WHERE name = @constraintName`;
274
324
  const checkConstraintRequest = this.pool.request();
275
325
  checkConstraintRequest.input("constraintName", constraintName);
276
326
  const constraintResult = await checkConstraintRequest.query(checkConstraintSql);
277
327
  const constraintExists = Array.isArray(constraintResult.recordset) && constraintResult.recordset.length > 0;
278
328
  if (!constraintExists) {
279
- const addConstraintSql = `ALTER TABLE ${getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) })} ADD CONSTRAINT ${constraintName} UNIQUE ([workflow_name], [run_id])`;
329
+ const addConstraintSql = `ALTER TABLE ${getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) })} ADD CONSTRAINT [${constraintName}] UNIQUE ([workflow_name], [run_id])`;
280
330
  await this.pool.request().query(addConstraintSql);
281
331
  }
282
332
  }
333
+ if (tableName === storage.TABLE_SPANS) {
334
+ await this.migrateSpansTable();
335
+ const pkConstraintName = `${schemaPrefix}mastra_ai_spans_traceid_spanid_pk`;
336
+ const checkPkRequest = this.pool.request();
337
+ checkPkRequest.input("constraintName", pkConstraintName);
338
+ const pkResult = await checkPkRequest.query(
339
+ `SELECT 1 AS found FROM sys.key_constraints WHERE name = @constraintName`
340
+ );
341
+ const pkExists = Array.isArray(pkResult.recordset) && pkResult.recordset.length > 0;
342
+ if (!pkExists) {
343
+ try {
344
+ const addPkSql = `ALTER TABLE ${getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) })} ADD CONSTRAINT [${pkConstraintName}] PRIMARY KEY ([traceId], [spanId])`;
345
+ await this.pool.request().query(addPkSql);
346
+ } catch (pkError) {
347
+ this.logger?.warn?.(`Failed to add composite primary key to spans table:`, pkError);
348
+ }
349
+ }
350
+ }
283
351
  } catch (error$1) {
284
352
  throw new error.MastraError(
285
353
  {
@@ -294,6 +362,34 @@ ${columns}
294
362
  );
295
363
  }
296
364
  }
365
+ /**
366
+ * Migrates the spans table schema from OLD_SPAN_SCHEMA to current SPAN_SCHEMA.
367
+ * This adds new columns that don't exist in old schema.
368
+ */
369
+ async migrateSpansTable() {
370
+ const fullTableName = getTableName({ indexName: storage.TABLE_SPANS, schemaName: getSchemaName(this.schemaName) });
371
+ const schema = storage.TABLE_SCHEMAS[storage.TABLE_SPANS];
372
+ try {
373
+ for (const [columnName, columnDef] of Object.entries(schema)) {
374
+ const columnExists = await this.hasColumn(storage.TABLE_SPANS, columnName);
375
+ if (!columnExists) {
376
+ const parsedColumnName = utils.parseSqlIdentifier(columnName, "column name");
377
+ const useLargeStorage = this.LARGE_DATA_COLUMNS.includes(columnName);
378
+ const useSmallStorage = this.COMPOSITE_INDEX_COLUMNS.includes(columnName);
379
+ const isIndexed = !!columnDef.primaryKey;
380
+ const sqlType = this.getSqlType(columnDef.type, isIndexed, useLargeStorage, useSmallStorage);
381
+ const nullable = columnDef.nullable ? "" : "NOT NULL";
382
+ const defaultValue = !columnDef.nullable ? this.getDefaultValue(columnDef.type) : "";
383
+ const alterSql = `ALTER TABLE ${fullTableName} ADD [${parsedColumnName}] ${sqlType} ${nullable} ${defaultValue}`.trim();
384
+ await this.pool.request().query(alterSql);
385
+ this.logger?.debug?.(`Added column '${columnName}' to ${fullTableName}`);
386
+ }
387
+ }
388
+ this.logger?.info?.(`Migration completed for ${fullTableName}`);
389
+ } catch (error) {
390
+ this.logger?.warn?.(`Failed to migrate spans table ${fullTableName}:`, error);
391
+ }
392
+ }
297
393
  /**
298
394
  * Alters table schema to add columns if they don't exist
299
395
  * @param tableName Name of the table
@@ -318,21 +414,12 @@ ${columns}
318
414
  const columnExists = Array.isArray(checkResult.recordset) && checkResult.recordset.length > 0;
319
415
  if (!columnExists) {
320
416
  const columnDef = schema[columnName];
321
- const largeDataColumns = [
322
- "workingMemory",
323
- "snapshot",
324
- "metadata",
325
- "content",
326
- "input",
327
- "output",
328
- "instructions",
329
- "other"
330
- ];
331
- const useLargeStorage = largeDataColumns.includes(columnName);
417
+ const useLargeStorage = this.LARGE_DATA_COLUMNS.includes(columnName);
418
+ const useSmallStorage = this.COMPOSITE_INDEX_COLUMNS.includes(columnName);
332
419
  const isIndexed = !!columnDef.primaryKey;
333
- const sqlType = this.getSqlType(columnDef.type, isIndexed, useLargeStorage);
334
- const nullable = columnDef.nullable === false ? "NOT NULL" : "";
335
- const defaultValue = columnDef.nullable === false ? this.getDefaultValue(columnDef.type) : "";
420
+ const sqlType = this.getSqlType(columnDef.type, isIndexed, useLargeStorage, useSmallStorage);
421
+ const nullable = columnDef.nullable ? "" : "NOT NULL";
422
+ const defaultValue = !columnDef.nullable ? this.getDefaultValue(columnDef.type) : "";
336
423
  const parsedColumnName = utils.parseSqlIdentifier(columnName, "column name");
337
424
  const alterSql = `ALTER TABLE ${fullTableName} ADD [${parsedColumnName}] ${sqlType} ${nullable} ${defaultValue}`.trim();
338
425
  await this.pool.request().query(alterSql);
@@ -916,84 +1003,6 @@ ${columns}
916
1003
  );
917
1004
  }
918
1005
  }
919
- /**
920
- * Returns definitions for automatic performance indexes
921
- * IMPORTANT: Uses seq_id DESC instead of createdAt DESC for MSSQL due to millisecond accuracy limitations
922
- * NOTE: Using NVARCHAR(400) for text columns (800 bytes) leaves room for composite indexes
923
- */
924
- getAutomaticIndexDefinitions() {
925
- const schemaPrefix = this.schemaName ? `${this.schemaName}_` : "";
926
- return [
927
- // Composite indexes for optimal filtering + sorting performance
928
- // NVARCHAR(400) = 800 bytes, plus BIGINT (8 bytes) = 808 bytes total (under 900-byte limit)
929
- {
930
- name: `${schemaPrefix}mastra_threads_resourceid_seqid_idx`,
931
- table: storage.TABLE_THREADS,
932
- columns: ["resourceId", "seq_id DESC"]
933
- },
934
- {
935
- name: `${schemaPrefix}mastra_messages_thread_id_seqid_idx`,
936
- table: storage.TABLE_MESSAGES,
937
- columns: ["thread_id", "seq_id DESC"]
938
- },
939
- {
940
- name: `${schemaPrefix}mastra_traces_name_seqid_idx`,
941
- table: storage.TABLE_TRACES,
942
- columns: ["name", "seq_id DESC"]
943
- },
944
- {
945
- name: `${schemaPrefix}mastra_scores_trace_id_span_id_seqid_idx`,
946
- table: storage.TABLE_SCORERS,
947
- columns: ["traceId", "spanId", "seq_id DESC"]
948
- },
949
- // Spans indexes for optimal trace querying
950
- {
951
- name: `${schemaPrefix}mastra_ai_spans_traceid_startedat_idx`,
952
- table: storage.TABLE_SPANS,
953
- columns: ["traceId", "startedAt DESC"]
954
- },
955
- {
956
- name: `${schemaPrefix}mastra_ai_spans_parentspanid_startedat_idx`,
957
- table: storage.TABLE_SPANS,
958
- columns: ["parentSpanId", "startedAt DESC"]
959
- },
960
- {
961
- name: `${schemaPrefix}mastra_ai_spans_name_idx`,
962
- table: storage.TABLE_SPANS,
963
- columns: ["name"]
964
- },
965
- {
966
- name: `${schemaPrefix}mastra_ai_spans_spantype_startedat_idx`,
967
- table: storage.TABLE_SPANS,
968
- columns: ["spanType", "startedAt DESC"]
969
- }
970
- ];
971
- }
972
- /**
973
- * Creates automatic indexes for optimal query performance
974
- * Uses getAutomaticIndexDefinitions() to determine which indexes to create
975
- */
976
- async createAutomaticIndexes() {
977
- try {
978
- const indexes = this.getAutomaticIndexDefinitions();
979
- for (const indexOptions of indexes) {
980
- try {
981
- await this.createIndex(indexOptions);
982
- } catch (error) {
983
- this.logger?.warn?.(`Failed to create index ${indexOptions.name}:`, error);
984
- }
985
- }
986
- } catch (error$1) {
987
- throw new error.MastraError(
988
- {
989
- id: storage.createStorageErrorId("MSSQL", "CREATE_PERFORMANCE_INDEXES", "FAILED"),
990
- domain: error.ErrorDomain.STORAGE,
991
- category: error.ErrorCategory.THIRD_PARTY
992
- },
993
- error$1
994
- );
995
- }
996
- }
997
1006
  };
998
1007
  function getSchemaName2(schema) {
999
1008
  return schema ? `[${utils.parseSqlIdentifier(schema, "schema name")}]` : void 0;
@@ -1107,11 +1116,15 @@ function transformFromSqlRow({
1107
1116
  }
1108
1117
 
1109
1118
  // src/storage/domains/memory/index.ts
1110
- var MemoryMSSQL = class extends storage.MemoryStorage {
1119
+ var MemoryMSSQL = class _MemoryMSSQL extends storage.MemoryStorage {
1111
1120
  pool;
1112
1121
  schema;
1113
1122
  db;
1114
1123
  needsConnect;
1124
+ skipDefaultIndexes;
1125
+ indexes;
1126
+ /** Tables managed by this domain */
1127
+ static MANAGED_TABLES = [storage.TABLE_THREADS, storage.TABLE_MESSAGES, storage.TABLE_RESOURCES];
1115
1128
  _parseAndFormatMessages(messages, format) {
1116
1129
  const messagesWithParsedContent = messages.map((message) => {
1117
1130
  if (typeof message.content === "string") {
@@ -1129,11 +1142,13 @@ var MemoryMSSQL = class extends storage.MemoryStorage {
1129
1142
  }
1130
1143
  constructor(config) {
1131
1144
  super();
1132
- const { pool, db, schema, needsConnect } = resolveMssqlConfig(config);
1145
+ const { pool, schemaName, skipDefaultIndexes, indexes, needsConnect } = resolveMssqlConfig(config);
1133
1146
  this.pool = pool;
1134
- this.schema = schema;
1135
- this.db = db;
1147
+ this.schema = schemaName;
1148
+ this.db = new MssqlDB({ pool, schemaName, skipDefaultIndexes });
1136
1149
  this.needsConnect = needsConnect;
1150
+ this.skipDefaultIndexes = skipDefaultIndexes;
1151
+ this.indexes = indexes?.filter((idx) => _MemoryMSSQL.MANAGED_TABLES.includes(idx.table));
1137
1152
  }
1138
1153
  async init() {
1139
1154
  if (this.needsConnect) {
@@ -1143,6 +1158,57 @@ var MemoryMSSQL = class extends storage.MemoryStorage {
1143
1158
  await this.db.createTable({ tableName: storage.TABLE_THREADS, schema: storage.TABLE_SCHEMAS[storage.TABLE_THREADS] });
1144
1159
  await this.db.createTable({ tableName: storage.TABLE_MESSAGES, schema: storage.TABLE_SCHEMAS[storage.TABLE_MESSAGES] });
1145
1160
  await this.db.createTable({ tableName: storage.TABLE_RESOURCES, schema: storage.TABLE_SCHEMAS[storage.TABLE_RESOURCES] });
1161
+ await this.createDefaultIndexes();
1162
+ await this.createCustomIndexes();
1163
+ }
1164
+ /**
1165
+ * Returns default index definitions for the memory domain tables.
1166
+ * IMPORTANT: Uses seq_id DESC instead of createdAt DESC for MSSQL due to millisecond accuracy limitations
1167
+ */
1168
+ getDefaultIndexDefinitions() {
1169
+ const schemaPrefix = this.schema ? `${this.schema}_` : "";
1170
+ return [
1171
+ {
1172
+ name: `${schemaPrefix}mastra_threads_resourceid_seqid_idx`,
1173
+ table: storage.TABLE_THREADS,
1174
+ columns: ["resourceId", "seq_id DESC"]
1175
+ },
1176
+ {
1177
+ name: `${schemaPrefix}mastra_messages_thread_id_seqid_idx`,
1178
+ table: storage.TABLE_MESSAGES,
1179
+ columns: ["thread_id", "seq_id DESC"]
1180
+ }
1181
+ ];
1182
+ }
1183
+ /**
1184
+ * Creates default indexes for optimal query performance.
1185
+ */
1186
+ async createDefaultIndexes() {
1187
+ if (this.skipDefaultIndexes) {
1188
+ return;
1189
+ }
1190
+ for (const indexDef of this.getDefaultIndexDefinitions()) {
1191
+ try {
1192
+ await this.db.createIndex(indexDef);
1193
+ } catch (error) {
1194
+ this.logger?.warn?.(`Failed to create index ${indexDef.name}:`, error);
1195
+ }
1196
+ }
1197
+ }
1198
+ /**
1199
+ * Creates custom user-defined indexes for this domain's tables.
1200
+ */
1201
+ async createCustomIndexes() {
1202
+ if (!this.indexes || this.indexes.length === 0) {
1203
+ return;
1204
+ }
1205
+ for (const indexDef of this.indexes) {
1206
+ try {
1207
+ await this.db.createIndex(indexDef);
1208
+ } catch (error) {
1209
+ this.logger?.warn?.(`Failed to create custom index ${indexDef.name}:`, error);
1210
+ }
1211
+ }
1146
1212
  }
1147
1213
  async dangerouslyClearAll() {
1148
1214
  await this.db.clearTable({ tableName: storage.TABLE_MESSAGES });
@@ -2021,18 +2087,24 @@ var MemoryMSSQL = class extends storage.MemoryStorage {
2021
2087
  }
2022
2088
  }
2023
2089
  };
2024
- var ObservabilityMSSQL = class extends storage.ObservabilityStorage {
2090
+ var ObservabilityMSSQL = class _ObservabilityMSSQL extends storage.ObservabilityStorage {
2025
2091
  pool;
2026
2092
  db;
2027
2093
  schema;
2028
2094
  needsConnect;
2095
+ skipDefaultIndexes;
2096
+ indexes;
2097
+ /** Tables managed by this domain */
2098
+ static MANAGED_TABLES = [storage.TABLE_SPANS];
2029
2099
  constructor(config) {
2030
2100
  super();
2031
- const { pool, db, schema, needsConnect } = resolveMssqlConfig(config);
2101
+ const { pool, schemaName, skipDefaultIndexes, indexes, needsConnect } = resolveMssqlConfig(config);
2032
2102
  this.pool = pool;
2033
- this.db = db;
2034
- this.schema = schema;
2103
+ this.schema = schemaName;
2104
+ this.db = new MssqlDB({ pool, schemaName, skipDefaultIndexes });
2035
2105
  this.needsConnect = needsConnect;
2106
+ this.skipDefaultIndexes = skipDefaultIndexes;
2107
+ this.indexes = indexes?.filter((idx) => _ObservabilityMSSQL.MANAGED_TABLES.includes(idx.table));
2036
2108
  }
2037
2109
  async init() {
2038
2110
  if (this.needsConnect) {
@@ -2040,6 +2112,92 @@ var ObservabilityMSSQL = class extends storage.ObservabilityStorage {
2040
2112
  this.needsConnect = false;
2041
2113
  }
2042
2114
  await this.db.createTable({ tableName: storage.TABLE_SPANS, schema: storage.SPAN_SCHEMA });
2115
+ await this.createDefaultIndexes();
2116
+ await this.createCustomIndexes();
2117
+ }
2118
+ /**
2119
+ * Returns default index definitions for the observability domain tables.
2120
+ */
2121
+ getDefaultIndexDefinitions() {
2122
+ const schemaPrefix = this.schema ? `${this.schema}_` : "";
2123
+ return [
2124
+ {
2125
+ name: `${schemaPrefix}mastra_ai_spans_traceid_startedat_idx`,
2126
+ table: storage.TABLE_SPANS,
2127
+ columns: ["traceId", "startedAt DESC"]
2128
+ },
2129
+ {
2130
+ name: `${schemaPrefix}mastra_ai_spans_parentspanid_startedat_idx`,
2131
+ table: storage.TABLE_SPANS,
2132
+ columns: ["parentSpanId", "startedAt DESC"]
2133
+ },
2134
+ {
2135
+ name: `${schemaPrefix}mastra_ai_spans_name_idx`,
2136
+ table: storage.TABLE_SPANS,
2137
+ columns: ["name"]
2138
+ },
2139
+ {
2140
+ name: `${schemaPrefix}mastra_ai_spans_spantype_startedat_idx`,
2141
+ table: storage.TABLE_SPANS,
2142
+ columns: ["spanType", "startedAt DESC"]
2143
+ },
2144
+ // Root spans filtered index - every listTraces query filters parentSpanId IS NULL
2145
+ {
2146
+ name: `${schemaPrefix}mastra_ai_spans_root_spans_idx`,
2147
+ table: storage.TABLE_SPANS,
2148
+ columns: ["startedAt DESC"],
2149
+ where: "[parentSpanId] IS NULL"
2150
+ },
2151
+ // Entity identification indexes - common filtering patterns
2152
+ {
2153
+ name: `${schemaPrefix}mastra_ai_spans_entitytype_entityid_idx`,
2154
+ table: storage.TABLE_SPANS,
2155
+ columns: ["entityType", "entityId"]
2156
+ },
2157
+ {
2158
+ name: `${schemaPrefix}mastra_ai_spans_entitytype_entityname_idx`,
2159
+ table: storage.TABLE_SPANS,
2160
+ columns: ["entityType", "entityName"]
2161
+ },
2162
+ // Multi-tenant filtering - organizationId + userId
2163
+ {
2164
+ name: `${schemaPrefix}mastra_ai_spans_orgid_userid_idx`,
2165
+ table: storage.TABLE_SPANS,
2166
+ columns: ["organizationId", "userId"]
2167
+ }
2168
+ // Note: MSSQL doesn't support GIN indexes for JSONB/array containment queries
2169
+ // Metadata and tags filtering will use full table scans on NVARCHAR(MAX) columns
2170
+ ];
2171
+ }
2172
+ /**
2173
+ * Creates default indexes for optimal query performance.
2174
+ */
2175
+ async createDefaultIndexes() {
2176
+ if (this.skipDefaultIndexes) {
2177
+ return;
2178
+ }
2179
+ for (const indexDef of this.getDefaultIndexDefinitions()) {
2180
+ try {
2181
+ await this.db.createIndex(indexDef);
2182
+ } catch (error) {
2183
+ this.logger?.warn?.(`Failed to create index ${indexDef.name}:`, error);
2184
+ }
2185
+ }
2186
+ }
2187
+ /**
2188
+ * Creates custom user-defined indexes for this domain's tables.
2189
+ */
2190
+ async createCustomIndexes() {
2191
+ if (!this.indexes || this.indexes.length === 0) {
2192
+ return;
2193
+ }
2194
+ for (const indexDef of this.indexes) {
2195
+ try {
2196
+ await this.db.createIndex(indexDef);
2197
+ } catch (error) {
2198
+ this.logger?.warn?.(`Failed to create custom index ${indexDef.name}:`, error);
2199
+ }
2200
+ }
2043
2201
  }
2044
2202
  async dangerouslyClearAll() {
2045
2203
  await this.db.clearTable({ tableName: storage.TABLE_SPANS });
@@ -2050,15 +2208,18 @@ var ObservabilityMSSQL = class extends storage.ObservabilityStorage {
2050
2208
  supported: ["batch-with-updates", "insert-only"]
2051
2209
  };
2052
2210
  }
2053
- async createSpan(span) {
2211
+ async createSpan(args) {
2212
+ const { span } = args;
2054
2213
  try {
2055
2214
  const startedAt = span.startedAt instanceof Date ? span.startedAt.toISOString() : span.startedAt;
2056
2215
  const endedAt = span.endedAt instanceof Date ? span.endedAt.toISOString() : span.endedAt;
2216
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2057
2217
  const record = {
2058
2218
  ...span,
2059
2219
  startedAt,
2060
- endedAt
2061
- // Note: createdAt/updatedAt will be set by default values
2220
+ endedAt,
2221
+ createdAt: now,
2222
+ updatedAt: now
2062
2223
  };
2063
2224
  return this.db.insert({ tableName: storage.TABLE_SPANS, record });
2064
2225
  } catch (error$1) {
@@ -2071,14 +2232,15 @@ var ObservabilityMSSQL = class extends storage.ObservabilityStorage {
2071
2232
  spanId: span.spanId,
2072
2233
  traceId: span.traceId,
2073
2234
  spanType: span.spanType,
2074
- spanName: span.name
2235
+ name: span.name
2075
2236
  }
2076
2237
  },
2077
2238
  error$1
2078
2239
  );
2079
2240
  }
2080
2241
  }
2081
- async getTrace(traceId) {
2242
+ async getTrace(args) {
2243
+ const { traceId } = args;
2082
2244
  try {
2083
2245
  const tableName = getTableName2({
2084
2246
  indexName: storage.TABLE_SPANS,
@@ -2088,12 +2250,17 @@ var ObservabilityMSSQL = class extends storage.ObservabilityStorage {
2088
2250
  request.input("traceId", traceId);
2089
2251
  const result = await request.query(
2090
2252
  `SELECT
2091
- [traceId], [spanId], [parentSpanId], [name], [scope], [spanType],
2092
- [attributes], [metadata], [links], [input], [output], [error], [isEvent],
2253
+ [traceId], [spanId], [parentSpanId], [name],
2254
+ [entityType], [entityId], [entityName],
2255
+ [userId], [organizationId], [resourceId],
2256
+ [runId], [sessionId], [threadId], [requestId],
2257
+ [environment], [source], [serviceName], [scope],
2258
+ [spanType], [attributes], [metadata], [tags], [links],
2259
+ [input], [output], [error], [isEvent],
2093
2260
  [startedAt], [endedAt], [createdAt], [updatedAt]
2094
2261
  FROM ${tableName}
2095
2262
  WHERE [traceId] = @traceId
2096
- ORDER BY [startedAt] DESC`
2263
+ ORDER BY [startedAt] ASC`
2097
2264
  );
2098
2265
  if (!result.recordset || result.recordset.length === 0) {
2099
2266
  return null;
@@ -2121,11 +2288,95 @@ var ObservabilityMSSQL = class extends storage.ObservabilityStorage {
2121
2288
  );
2122
2289
  }
2123
2290
  }
2124
- async updateSpan({
2125
- spanId,
2126
- traceId,
2127
- updates
2128
- }) {
2291
+ async getSpan(args) {
2292
+ const { traceId, spanId } = args;
2293
+ try {
2294
+ const tableName = getTableName2({
2295
+ indexName: storage.TABLE_SPANS,
2296
+ schemaName: getSchemaName2(this.schema)
2297
+ });
2298
+ const request = this.pool.request();
2299
+ request.input("traceId", traceId);
2300
+ request.input("spanId", spanId);
2301
+ const result = await request.query(
2302
+ `SELECT
2303
+ [traceId], [spanId], [parentSpanId], [name],
2304
+ [entityType], [entityId], [entityName],
2305
+ [userId], [organizationId], [resourceId],
2306
+ [runId], [sessionId], [threadId], [requestId],
2307
+ [environment], [source], [serviceName], [scope],
2308
+ [spanType], [attributes], [metadata], [tags], [links],
2309
+ [input], [output], [error], [isEvent],
2310
+ [startedAt], [endedAt], [createdAt], [updatedAt]
2311
+ FROM ${tableName}
2312
+ WHERE [traceId] = @traceId AND [spanId] = @spanId`
2313
+ );
2314
+ if (!result.recordset || result.recordset.length === 0) {
2315
+ return null;
2316
+ }
2317
+ return {
2318
+ span: transformFromSqlRow({
2319
+ tableName: storage.TABLE_SPANS,
2320
+ sqlRow: result.recordset[0]
2321
+ })
2322
+ };
2323
+ } catch (error$1) {
2324
+ throw new error.MastraError(
2325
+ {
2326
+ id: storage.createStorageErrorId("MSSQL", "GET_SPAN", "FAILED"),
2327
+ domain: error.ErrorDomain.STORAGE,
2328
+ category: error.ErrorCategory.USER,
2329
+ details: { traceId, spanId }
2330
+ },
2331
+ error$1
2332
+ );
2333
+ }
2334
+ }
2335
+ async getRootSpan(args) {
2336
+ const { traceId } = args;
2337
+ try {
2338
+ const tableName = getTableName2({
2339
+ indexName: storage.TABLE_SPANS,
2340
+ schemaName: getSchemaName2(this.schema)
2341
+ });
2342
+ const request = this.pool.request();
2343
+ request.input("traceId", traceId);
2344
+ const result = await request.query(
2345
+ `SELECT
2346
+ [traceId], [spanId], [parentSpanId], [name],
2347
+ [entityType], [entityId], [entityName],
2348
+ [userId], [organizationId], [resourceId],
2349
+ [runId], [sessionId], [threadId], [requestId],
2350
+ [environment], [source], [serviceName], [scope],
2351
+ [spanType], [attributes], [metadata], [tags], [links],
2352
+ [input], [output], [error], [isEvent],
2353
+ [startedAt], [endedAt], [createdAt], [updatedAt]
2354
+ FROM ${tableName}
2355
+ WHERE [traceId] = @traceId AND [parentSpanId] IS NULL`
2356
+ );
2357
+ if (!result.recordset || result.recordset.length === 0) {
2358
+ return null;
2359
+ }
2360
+ return {
2361
+ span: transformFromSqlRow({
2362
+ tableName: storage.TABLE_SPANS,
2363
+ sqlRow: result.recordset[0]
2364
+ })
2365
+ };
2366
+ } catch (error$1) {
2367
+ throw new error.MastraError(
2368
+ {
2369
+ id: storage.createStorageErrorId("MSSQL", "GET_ROOT_SPAN", "FAILED"),
2370
+ domain: error.ErrorDomain.STORAGE,
2371
+ category: error.ErrorCategory.USER,
2372
+ details: { traceId }
2373
+ },
2374
+ error$1
2375
+ );
2376
+ }
2377
+ }
2378
+ async updateSpan(args) {
2379
+ const { traceId, spanId, updates } = args;
2129
2380
  try {
2130
2381
  const data = { ...updates };
2131
2382
  if (data.endedAt instanceof Date) {
@@ -2134,6 +2385,7 @@ var ObservabilityMSSQL = class extends storage.ObservabilityStorage {
2134
2385
  if (data.startedAt instanceof Date) {
2135
2386
  data.startedAt = data.startedAt.toISOString();
2136
2387
  }
2388
+ data.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
2137
2389
  await this.db.update({
2138
2390
  tableName: storage.TABLE_SPANS,
2139
2391
  keys: { spanId, traceId },
@@ -2154,63 +2406,184 @@ var ObservabilityMSSQL = class extends storage.ObservabilityStorage {
2154
2406
  );
2155
2407
  }
2156
2408
  }
2157
- async getTracesPaginated({
2158
- filters,
2159
- pagination
2160
- }) {
2161
- const page = pagination?.page ?? 0;
2162
- const perPage = pagination?.perPage ?? 10;
2163
- const { entityId, entityType, ...actualFilters } = filters || {};
2164
- const filtersWithDateRange = {
2165
- ...actualFilters,
2166
- ...buildDateRangeFilter(pagination?.dateRange, "startedAt"),
2167
- parentSpanId: null
2168
- // Only get root spans for traces
2169
- };
2170
- const whereClause = prepareWhereClause(filtersWithDateRange);
2171
- let actualWhereClause = whereClause.sql;
2172
- const params = { ...whereClause.params };
2173
- let currentParamIndex = Object.keys(params).length + 1;
2174
- if (entityId && entityType) {
2175
- let name = "";
2176
- if (entityType === "workflow") {
2177
- name = `workflow run: '${entityId}'`;
2178
- } else if (entityType === "agent") {
2179
- name = `agent run: '${entityId}'`;
2180
- } else {
2181
- const error$1 = new error.MastraError({
2182
- id: storage.createStorageErrorId("MSSQL", "GET_TRACES_PAGINATED", "INVALID_ENTITY_TYPE"),
2183
- domain: error.ErrorDomain.STORAGE,
2184
- category: error.ErrorCategory.USER,
2185
- details: {
2186
- entityType
2187
- },
2188
- text: `Cannot filter by entity type: ${entityType}`
2189
- });
2190
- throw error$1;
2191
- }
2192
- const entityParam = `p${currentParamIndex++}`;
2193
- if (actualWhereClause) {
2194
- actualWhereClause += ` AND [name] = @${entityParam}`;
2195
- } else {
2196
- actualWhereClause = ` WHERE [name] = @${entityParam}`;
2197
- }
2198
- params[entityParam] = name;
2199
- }
2409
+ async listTraces(args) {
2410
+ const { filters, pagination, orderBy } = storage.listTracesArgsSchema.parse(args);
2411
+ const { page, perPage } = pagination;
2200
2412
  const tableName = getTableName2({
2201
2413
  indexName: storage.TABLE_SPANS,
2202
2414
  schemaName: getSchemaName2(this.schema)
2203
2415
  });
2204
2416
  try {
2417
+ const conditions = ["r.[parentSpanId] IS NULL"];
2418
+ const params = {};
2419
+ let paramIndex = 1;
2420
+ if (filters) {
2421
+ if (filters.startedAt?.start) {
2422
+ const param = `p${paramIndex++}`;
2423
+ conditions.push(`r.[startedAt] >= @${param}`);
2424
+ params[param] = filters.startedAt.start.toISOString();
2425
+ }
2426
+ if (filters.startedAt?.end) {
2427
+ const param = `p${paramIndex++}`;
2428
+ conditions.push(`r.[startedAt] <= @${param}`);
2429
+ params[param] = filters.startedAt.end.toISOString();
2430
+ }
2431
+ if (filters.endedAt?.start) {
2432
+ const param = `p${paramIndex++}`;
2433
+ conditions.push(`r.[endedAt] >= @${param}`);
2434
+ params[param] = filters.endedAt.start.toISOString();
2435
+ }
2436
+ if (filters.endedAt?.end) {
2437
+ const param = `p${paramIndex++}`;
2438
+ conditions.push(`r.[endedAt] <= @${param}`);
2439
+ params[param] = filters.endedAt.end.toISOString();
2440
+ }
2441
+ if (filters.spanType !== void 0) {
2442
+ const param = `p${paramIndex++}`;
2443
+ conditions.push(`r.[spanType] = @${param}`);
2444
+ params[param] = filters.spanType;
2445
+ }
2446
+ if (filters.entityType !== void 0) {
2447
+ const param = `p${paramIndex++}`;
2448
+ conditions.push(`r.[entityType] = @${param}`);
2449
+ params[param] = filters.entityType;
2450
+ }
2451
+ if (filters.entityId !== void 0) {
2452
+ const param = `p${paramIndex++}`;
2453
+ conditions.push(`r.[entityId] = @${param}`);
2454
+ params[param] = filters.entityId;
2455
+ }
2456
+ if (filters.entityName !== void 0) {
2457
+ const param = `p${paramIndex++}`;
2458
+ conditions.push(`r.[entityName] = @${param}`);
2459
+ params[param] = filters.entityName;
2460
+ }
2461
+ if (filters.userId !== void 0) {
2462
+ const param = `p${paramIndex++}`;
2463
+ conditions.push(`r.[userId] = @${param}`);
2464
+ params[param] = filters.userId;
2465
+ }
2466
+ if (filters.organizationId !== void 0) {
2467
+ const param = `p${paramIndex++}`;
2468
+ conditions.push(`r.[organizationId] = @${param}`);
2469
+ params[param] = filters.organizationId;
2470
+ }
2471
+ if (filters.resourceId !== void 0) {
2472
+ const param = `p${paramIndex++}`;
2473
+ conditions.push(`r.[resourceId] = @${param}`);
2474
+ params[param] = filters.resourceId;
2475
+ }
2476
+ if (filters.runId !== void 0) {
2477
+ const param = `p${paramIndex++}`;
2478
+ conditions.push(`r.[runId] = @${param}`);
2479
+ params[param] = filters.runId;
2480
+ }
2481
+ if (filters.sessionId !== void 0) {
2482
+ const param = `p${paramIndex++}`;
2483
+ conditions.push(`r.[sessionId] = @${param}`);
2484
+ params[param] = filters.sessionId;
2485
+ }
2486
+ if (filters.threadId !== void 0) {
2487
+ const param = `p${paramIndex++}`;
2488
+ conditions.push(`r.[threadId] = @${param}`);
2489
+ params[param] = filters.threadId;
2490
+ }
2491
+ if (filters.requestId !== void 0) {
2492
+ const param = `p${paramIndex++}`;
2493
+ conditions.push(`r.[requestId] = @${param}`);
2494
+ params[param] = filters.requestId;
2495
+ }
2496
+ if (filters.environment !== void 0) {
2497
+ const param = `p${paramIndex++}`;
2498
+ conditions.push(`r.[environment] = @${param}`);
2499
+ params[param] = filters.environment;
2500
+ }
2501
+ if (filters.source !== void 0) {
2502
+ const param = `p${paramIndex++}`;
2503
+ conditions.push(`r.[source] = @${param}`);
2504
+ params[param] = filters.source;
2505
+ }
2506
+ if (filters.serviceName !== void 0) {
2507
+ const param = `p${paramIndex++}`;
2508
+ conditions.push(`r.[serviceName] = @${param}`);
2509
+ params[param] = filters.serviceName;
2510
+ }
2511
+ if (filters.scope != null) {
2512
+ for (const [key, value] of Object.entries(filters.scope)) {
2513
+ if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(key)) {
2514
+ throw new error.MastraError({
2515
+ id: storage.createStorageErrorId("MSSQL", "LIST_TRACES", "INVALID_FILTER_KEY"),
2516
+ domain: error.ErrorDomain.STORAGE,
2517
+ category: error.ErrorCategory.USER,
2518
+ details: { key }
2519
+ });
2520
+ }
2521
+ const param = `p${paramIndex++}`;
2522
+ conditions.push(`JSON_VALUE(r.[scope], '$.${key}') = @${param}`);
2523
+ params[param] = typeof value === "string" ? value : JSON.stringify(value);
2524
+ }
2525
+ }
2526
+ if (filters.metadata != null) {
2527
+ for (const [key, value] of Object.entries(filters.metadata)) {
2528
+ if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(key)) {
2529
+ throw new error.MastraError({
2530
+ id: storage.createStorageErrorId("MSSQL", "LIST_TRACES", "INVALID_FILTER_KEY"),
2531
+ domain: error.ErrorDomain.STORAGE,
2532
+ category: error.ErrorCategory.USER,
2533
+ details: { key }
2534
+ });
2535
+ }
2536
+ const param = `p${paramIndex++}`;
2537
+ conditions.push(`JSON_VALUE(r.[metadata], '$.${key}') = @${param}`);
2538
+ params[param] = typeof value === "string" ? value : JSON.stringify(value);
2539
+ }
2540
+ }
2541
+ if (filters.tags != null && filters.tags.length > 0) {
2542
+ for (const tag of filters.tags) {
2543
+ const param = `p${paramIndex++}`;
2544
+ conditions.push(`EXISTS (SELECT 1 FROM OPENJSON(r.[tags]) WHERE [value] = @${param})`);
2545
+ params[param] = tag;
2546
+ }
2547
+ }
2548
+ if (filters.status !== void 0) {
2549
+ switch (filters.status) {
2550
+ case storage.TraceStatus.ERROR:
2551
+ conditions.push(`r.[error] IS NOT NULL`);
2552
+ break;
2553
+ case storage.TraceStatus.RUNNING:
2554
+ conditions.push(`r.[endedAt] IS NULL AND r.[error] IS NULL`);
2555
+ break;
2556
+ case storage.TraceStatus.SUCCESS:
2557
+ conditions.push(`r.[endedAt] IS NOT NULL AND r.[error] IS NULL`);
2558
+ break;
2559
+ }
2560
+ }
2561
+ if (filters.hasChildError !== void 0) {
2562
+ if (filters.hasChildError) {
2563
+ conditions.push(`EXISTS (
2564
+ SELECT 1 FROM ${tableName} c
2565
+ WHERE c.[traceId] = r.[traceId] AND c.[error] IS NOT NULL
2566
+ )`);
2567
+ } else {
2568
+ conditions.push(`NOT EXISTS (
2569
+ SELECT 1 FROM ${tableName} c
2570
+ WHERE c.[traceId] = r.[traceId] AND c.[error] IS NOT NULL
2571
+ )`);
2572
+ }
2573
+ }
2574
+ }
2575
+ const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
2576
+ const sortField = orderBy.field;
2577
+ const sortDirection = orderBy.direction;
2205
2578
  const countRequest = this.pool.request();
2206
2579
  Object.entries(params).forEach(([key, value]) => {
2207
2580
  countRequest.input(key, value);
2208
2581
  });
2209
2582
  const countResult = await countRequest.query(
2210
- `SELECT COUNT(*) as count FROM ${tableName}${actualWhereClause}`
2583
+ `SELECT COUNT(*) as count FROM ${tableName} r ${whereClause}`
2211
2584
  );
2212
- const total = countResult.recordset[0]?.count ?? 0;
2213
- if (total === 0) {
2585
+ const count = countResult.recordset[0]?.count ?? 0;
2586
+ if (count === 0) {
2214
2587
  return {
2215
2588
  pagination: {
2216
2589
  total: 0,
@@ -2227,28 +2600,39 @@ var ObservabilityMSSQL = class extends storage.ObservabilityStorage {
2227
2600
  });
2228
2601
  dataRequest.input("offset", page * perPage);
2229
2602
  dataRequest.input("limit", perPage);
2230
- const dataResult = await dataRequest.query(
2231
- `SELECT * FROM ${tableName}${actualWhereClause} ORDER BY [startedAt] DESC OFFSET @offset ROWS FETCH NEXT @limit ROWS ONLY`
2232
- );
2233
- const spans = dataResult.recordset.map(
2234
- (row) => transformFromSqlRow({
2235
- tableName: storage.TABLE_SPANS,
2236
- sqlRow: row
2237
- })
2603
+ const result = await dataRequest.query(
2604
+ `SELECT
2605
+ r.[traceId], r.[spanId], r.[parentSpanId], r.[name],
2606
+ r.[entityType], r.[entityId], r.[entityName],
2607
+ r.[userId], r.[organizationId], r.[resourceId],
2608
+ r.[runId], r.[sessionId], r.[threadId], r.[requestId],
2609
+ r.[environment], r.[source], r.[serviceName], r.[scope],
2610
+ r.[spanType], r.[attributes], r.[metadata], r.[tags], r.[links],
2611
+ r.[input], r.[output], r.[error], r.[isEvent],
2612
+ r.[startedAt], r.[endedAt], r.[createdAt], r.[updatedAt]
2613
+ FROM ${tableName} r
2614
+ ${whereClause}
2615
+ ORDER BY r.[${sortField}] ${sortDirection}
2616
+ OFFSET @offset ROWS FETCH NEXT @limit ROWS ONLY`
2238
2617
  );
2239
2618
  return {
2240
2619
  pagination: {
2241
- total,
2620
+ total: count,
2242
2621
  page,
2243
2622
  perPage,
2244
- hasMore: (page + 1) * perPage < total
2623
+ hasMore: (page + 1) * perPage < count
2245
2624
  },
2246
- spans
2625
+ spans: result.recordset.map(
2626
+ (span) => transformFromSqlRow({
2627
+ tableName: storage.TABLE_SPANS,
2628
+ sqlRow: span
2629
+ })
2630
+ )
2247
2631
  };
2248
2632
  } catch (error$1) {
2249
2633
  throw new error.MastraError(
2250
2634
  {
2251
- id: storage.createStorageErrorId("MSSQL", "GET_TRACES_PAGINATED", "FAILED"),
2635
+ id: storage.createStorageErrorId("MSSQL", "LIST_TRACES", "FAILED"),
2252
2636
  domain: error.ErrorDomain.STORAGE,
2253
2637
  category: error.ErrorCategory.USER
2254
2638
  },
@@ -2261,12 +2645,15 @@ var ObservabilityMSSQL = class extends storage.ObservabilityStorage {
2261
2645
  return;
2262
2646
  }
2263
2647
  try {
2648
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2264
2649
  await this.db.batchInsert({
2265
2650
  tableName: storage.TABLE_SPANS,
2266
2651
  records: args.records.map((span) => ({
2267
2652
  ...span,
2268
2653
  startedAt: span.startedAt instanceof Date ? span.startedAt.toISOString() : span.startedAt,
2269
- endedAt: span.endedAt instanceof Date ? span.endedAt.toISOString() : span.endedAt
2654
+ endedAt: span.endedAt instanceof Date ? span.endedAt.toISOString() : span.endedAt,
2655
+ createdAt: now,
2656
+ updatedAt: now
2270
2657
  }))
2271
2658
  });
2272
2659
  } catch (error$1) {
@@ -2287,6 +2674,7 @@ var ObservabilityMSSQL = class extends storage.ObservabilityStorage {
2287
2674
  if (!args.records || args.records.length === 0) {
2288
2675
  return;
2289
2676
  }
2677
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2290
2678
  try {
2291
2679
  const updates = args.records.map(({ traceId, spanId, updates: data }) => {
2292
2680
  const processedData = { ...data };
@@ -2296,6 +2684,7 @@ var ObservabilityMSSQL = class extends storage.ObservabilityStorage {
2296
2684
  if (processedData.startedAt instanceof Date) {
2297
2685
  processedData.startedAt = processedData.startedAt.toISOString();
2298
2686
  }
2687
+ processedData.updatedAt = now;
2299
2688
  return {
2300
2689
  keys: { spanId, traceId },
2301
2690
  data: processedData
@@ -2349,18 +2738,24 @@ function transformScoreRow(row) {
2349
2738
  convertTimestamps: true
2350
2739
  });
2351
2740
  }
2352
- var ScoresMSSQL = class extends storage.ScoresStorage {
2741
+ var ScoresMSSQL = class _ScoresMSSQL extends storage.ScoresStorage {
2353
2742
  pool;
2354
2743
  db;
2355
2744
  schema;
2356
2745
  needsConnect;
2746
+ skipDefaultIndexes;
2747
+ indexes;
2748
+ /** Tables managed by this domain */
2749
+ static MANAGED_TABLES = [storage.TABLE_SCORERS];
2357
2750
  constructor(config) {
2358
2751
  super();
2359
- const { pool, db, schema, needsConnect } = resolveMssqlConfig(config);
2752
+ const { pool, schemaName, skipDefaultIndexes, indexes, needsConnect } = resolveMssqlConfig(config);
2360
2753
  this.pool = pool;
2361
- this.db = db;
2362
- this.schema = schema;
2754
+ this.schema = schemaName;
2755
+ this.db = new MssqlDB({ pool, schemaName, skipDefaultIndexes });
2363
2756
  this.needsConnect = needsConnect;
2757
+ this.skipDefaultIndexes = skipDefaultIndexes;
2758
+ this.indexes = indexes?.filter((idx) => _ScoresMSSQL.MANAGED_TABLES.includes(idx.table));
2364
2759
  }
2365
2760
  async init() {
2366
2761
  if (this.needsConnect) {
@@ -2368,6 +2763,52 @@ var ScoresMSSQL = class extends storage.ScoresStorage {
2368
2763
  this.needsConnect = false;
2369
2764
  }
2370
2765
  await this.db.createTable({ tableName: storage.TABLE_SCORERS, schema: storage.TABLE_SCHEMAS[storage.TABLE_SCORERS] });
2766
+ await this.createDefaultIndexes();
2767
+ await this.createCustomIndexes();
2768
+ }
2769
+ /**
2770
+ * Returns default index definitions for the scores domain tables.
2771
+ * IMPORTANT: Uses seq_id DESC instead of createdAt DESC for MSSQL due to millisecond accuracy limitations
2772
+ */
2773
+ getDefaultIndexDefinitions() {
2774
+ const schemaPrefix = this.schema ? `${this.schema}_` : "";
2775
+ return [
2776
+ {
2777
+ name: `${schemaPrefix}mastra_scores_trace_id_span_id_seqid_idx`,
2778
+ table: storage.TABLE_SCORERS,
2779
+ columns: ["traceId", "spanId", "seq_id DESC"]
2780
+ }
2781
+ ];
2782
+ }
2783
+ /**
2784
+ * Creates default indexes for optimal query performance.
2785
+ */
2786
+ async createDefaultIndexes() {
2787
+ if (this.skipDefaultIndexes) {
2788
+ return;
2789
+ }
2790
+ for (const indexDef of this.getDefaultIndexDefinitions()) {
2791
+ try {
2792
+ await this.db.createIndex(indexDef);
2793
+ } catch (error) {
2794
+ this.logger?.warn?.(`Failed to create index ${indexDef.name}:`, error);
2795
+ }
2796
+ }
2797
+ }
2798
+ /**
2799
+ * Creates custom user-defined indexes for this domain's tables.
2800
+ */
2801
+ async createCustomIndexes() {
2802
+ if (!this.indexes || this.indexes.length === 0) {
2803
+ return;
2804
+ }
2805
+ for (const indexDef of this.indexes) {
2806
+ try {
2807
+ await this.db.createIndex(indexDef);
2808
+ } catch (error) {
2809
+ this.logger?.warn?.(`Failed to create custom index ${indexDef.name}:`, error);
2810
+ }
2811
+ }
2371
2812
  }
2372
2813
  async dangerouslyClearAll() {
2373
2814
  await this.db.clearTable({ tableName: storage.TABLE_SCORERS });
@@ -2406,7 +2847,7 @@ var ScoresMSSQL = class extends storage.ScoresStorage {
2406
2847
  domain: error.ErrorDomain.STORAGE,
2407
2848
  category: error.ErrorCategory.USER,
2408
2849
  details: {
2409
- scorer: score.scorer?.id ?? "unknown",
2850
+ scorer: typeof score.scorer?.id === "string" ? score.scorer.id : String(score.scorer?.id ?? "unknown"),
2410
2851
  entityId: score.entityId ?? "unknown",
2411
2852
  entityType: score.entityType ?? "unknown",
2412
2853
  traceId: score.traceId ?? "",
@@ -2712,18 +3153,40 @@ var ScoresMSSQL = class extends storage.ScoresStorage {
2712
3153
  }
2713
3154
  }
2714
3155
  };
2715
- var WorkflowsMSSQL = class extends storage.WorkflowsStorage {
3156
+ var WorkflowsMSSQL = class _WorkflowsMSSQL extends storage.WorkflowsStorage {
2716
3157
  pool;
2717
3158
  db;
2718
3159
  schema;
2719
3160
  needsConnect;
3161
+ skipDefaultIndexes;
3162
+ indexes;
3163
+ /** Tables managed by this domain */
3164
+ static MANAGED_TABLES = [storage.TABLE_WORKFLOW_SNAPSHOT];
2720
3165
  constructor(config) {
2721
3166
  super();
2722
- const { pool, db, schema, needsConnect } = resolveMssqlConfig(config);
3167
+ const { pool, schemaName, skipDefaultIndexes, indexes, needsConnect } = resolveMssqlConfig(config);
2723
3168
  this.pool = pool;
2724
- this.db = db;
2725
- this.schema = schema;
3169
+ this.schema = schemaName;
3170
+ this.db = new MssqlDB({ pool, schemaName, skipDefaultIndexes });
2726
3171
  this.needsConnect = needsConnect;
3172
+ this.skipDefaultIndexes = skipDefaultIndexes;
3173
+ this.indexes = indexes?.filter((idx) => _WorkflowsMSSQL.MANAGED_TABLES.includes(idx.table));
3174
+ }
3175
+ /**
3176
+ * Returns default index definitions for the workflows domain tables.
3177
+ * Currently no default indexes are defined for workflows.
3178
+ */
3179
+ getDefaultIndexDefinitions() {
3180
+ return [];
3181
+ }
3182
+ /**
3183
+ * Creates default indexes for optimal query performance.
3184
+ * Currently no default indexes are defined for workflows.
3185
+ */
3186
+ async createDefaultIndexes() {
3187
+ if (this.skipDefaultIndexes) {
3188
+ return;
3189
+ }
2727
3190
  }
2728
3191
  async init() {
2729
3192
  if (this.needsConnect) {
@@ -2731,6 +3194,23 @@ var WorkflowsMSSQL = class extends storage.WorkflowsStorage {
2731
3194
  this.needsConnect = false;
2732
3195
  }
2733
3196
  await this.db.createTable({ tableName: storage.TABLE_WORKFLOW_SNAPSHOT, schema: storage.TABLE_SCHEMAS[storage.TABLE_WORKFLOW_SNAPSHOT] });
3197
+ await this.createDefaultIndexes();
3198
+ await this.createCustomIndexes();
3199
+ }
3200
+ /**
3201
+ * Creates custom user-defined indexes for this domain's tables.
3202
+ */
3203
+ async createCustomIndexes() {
3204
+ if (!this.indexes || this.indexes.length === 0) {
3205
+ return;
3206
+ }
3207
+ for (const indexDef of this.indexes) {
3208
+ try {
3209
+ await this.db.createIndex(indexDef);
3210
+ } catch (error) {
3211
+ this.logger?.warn?.(`Failed to create custom index ${indexDef.name}:`, error);
3212
+ }
3213
+ }
2734
3214
  }
2735
3215
  async dangerouslyClearAll() {
2736
3216
  await this.db.clearTable({ tableName: storage.TABLE_WORKFLOW_SNAPSHOT });
@@ -3125,7 +3605,6 @@ var MSSQLStore = class extends storage.MastraStorage {
3125
3605
  pool;
3126
3606
  schema;
3127
3607
  isConnected = null;
3128
- #db;
3129
3608
  stores;
3130
3609
  constructor(config) {
3131
3610
  if (!config.id || typeof config.id !== "string" || config.id.trim() === "") {
@@ -3157,8 +3636,12 @@ var MSSQLStore = class extends storage.MastraStorage {
3157
3636
  options: config.options || { encrypt: true, trustServerCertificate: true }
3158
3637
  });
3159
3638
  }
3160
- this.#db = new MssqlDB({ pool: this.pool, schemaName: this.schema });
3161
- const domainConfig = { pool: this.pool, db: this.#db, schema: this.schema };
3639
+ const domainConfig = {
3640
+ pool: this.pool,
3641
+ schemaName: this.schema,
3642
+ skipDefaultIndexes: config.skipDefaultIndexes,
3643
+ indexes: config.indexes
3644
+ };
3162
3645
  const scores = new ScoresMSSQL(domainConfig);
3163
3646
  const workflows = new WorkflowsMSSQL(domainConfig);
3164
3647
  const memory = new MemoryMSSQL(domainConfig);
@@ -3187,11 +3670,6 @@ var MSSQLStore = class extends storage.MastraStorage {
3187
3670
  try {
3188
3671
  await this.isConnected;
3189
3672
  await super.init();
3190
- try {
3191
- await this.#db.createAutomaticIndexes();
3192
- } catch (indexError) {
3193
- this.logger?.warn?.("Failed to create indexes:", indexError);
3194
- }
3195
3673
  } catch (error$1) {
3196
3674
  this.isConnected = null;
3197
3675
  throw new error.MastraError(
@@ -3219,207 +3697,20 @@ var MSSQLStore = class extends storage.MastraStorage {
3219
3697
  hasColumn: true,
3220
3698
  createTable: true,
3221
3699
  deleteMessages: true,
3700
+ observability: true,
3701
+ indexManagement: true,
3222
3702
  listScoresBySpan: true,
3223
- observabilityInstance: true,
3224
- indexManagement: true
3703
+ agents: false
3225
3704
  };
3226
3705
  }
3227
3706
  /**
3228
- * Memory
3707
+ * Closes the MSSQL connection pool.
3708
+ *
3709
+ * This will close the connection pool, including pre-configured pools.
3229
3710
  */
3230
- async getThreadById({ threadId }) {
3231
- return this.stores.memory.getThreadById({ threadId });
3232
- }
3233
- async saveThread({ thread }) {
3234
- return this.stores.memory.saveThread({ thread });
3235
- }
3236
- async updateThread({
3237
- id,
3238
- title,
3239
- metadata
3240
- }) {
3241
- return this.stores.memory.updateThread({ id, title, metadata });
3242
- }
3243
- async deleteThread({ threadId }) {
3244
- return this.stores.memory.deleteThread({ threadId });
3245
- }
3246
- async listMessagesById({ messageIds }) {
3247
- return this.stores.memory.listMessagesById({ messageIds });
3248
- }
3249
- async saveMessages(args) {
3250
- return this.stores.memory.saveMessages(args);
3251
- }
3252
- async updateMessages({
3253
- messages
3254
- }) {
3255
- return this.stores.memory.updateMessages({ messages });
3256
- }
3257
- async deleteMessages(messageIds) {
3258
- return this.stores.memory.deleteMessages(messageIds);
3259
- }
3260
- async getResourceById({ resourceId }) {
3261
- return this.stores.memory.getResourceById({ resourceId });
3262
- }
3263
- async saveResource({ resource }) {
3264
- return this.stores.memory.saveResource({ resource });
3265
- }
3266
- async updateResource({
3267
- resourceId,
3268
- workingMemory,
3269
- metadata
3270
- }) {
3271
- return this.stores.memory.updateResource({ resourceId, workingMemory, metadata });
3272
- }
3273
- /**
3274
- * Workflows
3275
- */
3276
- async updateWorkflowResults({
3277
- workflowName,
3278
- runId,
3279
- stepId,
3280
- result,
3281
- requestContext
3282
- }) {
3283
- return this.stores.workflows.updateWorkflowResults({ workflowName, runId, stepId, result, requestContext });
3284
- }
3285
- async updateWorkflowState({
3286
- workflowName,
3287
- runId,
3288
- opts
3289
- }) {
3290
- return this.stores.workflows.updateWorkflowState({ workflowName, runId, opts });
3291
- }
3292
- async persistWorkflowSnapshot({
3293
- workflowName,
3294
- runId,
3295
- resourceId,
3296
- snapshot
3297
- }) {
3298
- return this.stores.workflows.persistWorkflowSnapshot({ workflowName, runId, resourceId, snapshot });
3299
- }
3300
- async loadWorkflowSnapshot({
3301
- workflowName,
3302
- runId
3303
- }) {
3304
- return this.stores.workflows.loadWorkflowSnapshot({ workflowName, runId });
3305
- }
3306
- async listWorkflowRuns(args = {}) {
3307
- return this.stores.workflows.listWorkflowRuns(args);
3308
- }
3309
- async getWorkflowRunById({
3310
- runId,
3311
- workflowName
3312
- }) {
3313
- return this.stores.workflows.getWorkflowRunById({ runId, workflowName });
3314
- }
3315
- async deleteWorkflowRunById({ runId, workflowName }) {
3316
- return this.stores.workflows.deleteWorkflowRunById({ runId, workflowName });
3317
- }
3318
3711
  async close() {
3319
3712
  await this.pool.close();
3320
3713
  }
3321
- /**
3322
- * Index Management
3323
- */
3324
- async createIndex(options) {
3325
- return this.#db.createIndex(options);
3326
- }
3327
- async listIndexes(tableName) {
3328
- return this.#db.listIndexes(tableName);
3329
- }
3330
- async describeIndex(indexName) {
3331
- return this.#db.describeIndex(indexName);
3332
- }
3333
- async dropIndex(indexName) {
3334
- return this.#db.dropIndex(indexName);
3335
- }
3336
- /**
3337
- * Tracing / Observability
3338
- */
3339
- getObservabilityStore() {
3340
- if (!this.stores.observability) {
3341
- throw new error.MastraError({
3342
- id: storage.createStorageErrorId("MSSQL", "OBSERVABILITY", "NOT_INITIALIZED"),
3343
- domain: error.ErrorDomain.STORAGE,
3344
- category: error.ErrorCategory.SYSTEM,
3345
- text: "Observability storage is not initialized"
3346
- });
3347
- }
3348
- return this.stores.observability;
3349
- }
3350
- async createSpan(span) {
3351
- return this.getObservabilityStore().createSpan(span);
3352
- }
3353
- async updateSpan({
3354
- spanId,
3355
- traceId,
3356
- updates
3357
- }) {
3358
- return this.getObservabilityStore().updateSpan({ spanId, traceId, updates });
3359
- }
3360
- async getTrace(traceId) {
3361
- return this.getObservabilityStore().getTrace(traceId);
3362
- }
3363
- async getTracesPaginated(args) {
3364
- return this.getObservabilityStore().getTracesPaginated(args);
3365
- }
3366
- async batchCreateSpans(args) {
3367
- return this.getObservabilityStore().batchCreateSpans(args);
3368
- }
3369
- async batchUpdateSpans(args) {
3370
- return this.getObservabilityStore().batchUpdateSpans(args);
3371
- }
3372
- async batchDeleteTraces(args) {
3373
- return this.getObservabilityStore().batchDeleteTraces(args);
3374
- }
3375
- /**
3376
- * Scorers
3377
- */
3378
- async getScoreById({ id: _id }) {
3379
- return this.stores.scores.getScoreById({ id: _id });
3380
- }
3381
- async listScoresByScorerId({
3382
- scorerId: _scorerId,
3383
- pagination: _pagination,
3384
- entityId: _entityId,
3385
- entityType: _entityType,
3386
- source: _source
3387
- }) {
3388
- return this.stores.scores.listScoresByScorerId({
3389
- scorerId: _scorerId,
3390
- pagination: _pagination,
3391
- entityId: _entityId,
3392
- entityType: _entityType,
3393
- source: _source
3394
- });
3395
- }
3396
- async saveScore(score) {
3397
- return this.stores.scores.saveScore(score);
3398
- }
3399
- async listScoresByRunId({
3400
- runId: _runId,
3401
- pagination: _pagination
3402
- }) {
3403
- return this.stores.scores.listScoresByRunId({ runId: _runId, pagination: _pagination });
3404
- }
3405
- async listScoresByEntityId({
3406
- entityId: _entityId,
3407
- entityType: _entityType,
3408
- pagination: _pagination
3409
- }) {
3410
- return this.stores.scores.listScoresByEntityId({
3411
- entityId: _entityId,
3412
- entityType: _entityType,
3413
- pagination: _pagination
3414
- });
3415
- }
3416
- async listScoresBySpan({
3417
- traceId,
3418
- spanId,
3419
- pagination: _pagination
3420
- }) {
3421
- return this.stores.scores.listScoresBySpan({ traceId, spanId, pagination: _pagination });
3422
- }
3423
3714
  };
3424
3715
 
3425
3716
  exports.MSSQLStore = MSSQLStore;