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