@mastra/observability 1.7.3 → 1.8.0-alpha.1

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
@@ -13855,17 +13855,36 @@ var serializationOptionsSchema = external_exports.object({
13855
13855
  maxArrayLength: external_exports.number().int().positive().optional(),
13856
13856
  maxObjectKeys: external_exports.number().int().positive().optional()
13857
13857
  }).optional();
13858
- var observabilityInstanceConfigSchema = external_exports.object({
13859
- name: external_exports.string().min(1, "Name is required"),
13858
+ var LOG_LEVELS = ["debug", "info", "warn", "error", "fatal"];
13859
+ var cardinalityConfigSchema = external_exports.object({
13860
+ blockedLabels: external_exports.array(external_exports.string()).optional(),
13861
+ blockUUIDs: external_exports.boolean().optional()
13862
+ }).optional();
13863
+ var loggingConfigSchema = external_exports.object({
13864
+ enabled: external_exports.boolean().optional(),
13865
+ level: external_exports.enum(LOG_LEVELS).optional()
13866
+ }).optional();
13867
+ var spanFilterSchema = external_exports.function({
13868
+ input: external_exports.tuple([external_exports.any()]),
13869
+ output: external_exports.boolean()
13870
+ }).optional();
13871
+ var observabilityInstanceConfigFields = {
13860
13872
  serviceName: external_exports.string().min(1, "Service name is required"),
13861
13873
  sampling: samplingStrategySchema.optional(),
13862
13874
  exporters: external_exports.array(external_exports.any()).optional(),
13863
13875
  bridge: external_exports.any().optional(),
13864
13876
  spanOutputProcessors: external_exports.array(external_exports.any()).optional(),
13865
13877
  includeInternalSpans: external_exports.boolean().optional(),
13878
+ excludeSpanTypes: external_exports.array(external_exports.nativeEnum(observability.SpanType)).optional(),
13879
+ spanFilter: spanFilterSchema,
13866
13880
  requestContextKeys: external_exports.array(external_exports.string()).optional(),
13867
13881
  serializationOptions: serializationOptionsSchema,
13868
- cardinality: external_exports.any().optional()
13882
+ cardinality: cardinalityConfigSchema,
13883
+ logging: loggingConfigSchema
13884
+ };
13885
+ var observabilityInstanceConfigSchema = external_exports.object({
13886
+ name: external_exports.string().min(1, "Name is required"),
13887
+ ...observabilityInstanceConfigFields
13869
13888
  }).refine(
13870
13889
  (data) => {
13871
13890
  const hasExporters = data.exporters && data.exporters.length > 0;
@@ -13876,16 +13895,7 @@ var observabilityInstanceConfigSchema = external_exports.object({
13876
13895
  message: "At least one exporter or a bridge is required"
13877
13896
  }
13878
13897
  );
13879
- var observabilityConfigValueSchema = external_exports.object({
13880
- serviceName: external_exports.string().min(1, "Service name is required"),
13881
- sampling: samplingStrategySchema.optional(),
13882
- exporters: external_exports.array(external_exports.any()).optional(),
13883
- bridge: external_exports.any().optional(),
13884
- spanOutputProcessors: external_exports.array(external_exports.any()).optional(),
13885
- includeInternalSpans: external_exports.boolean().optional(),
13886
- requestContextKeys: external_exports.array(external_exports.string()).optional(),
13887
- serializationOptions: serializationOptionsSchema
13888
- }).refine(
13898
+ var observabilityConfigValueSchema = external_exports.object(observabilityInstanceConfigFields).refine(
13889
13899
  (data) => {
13890
13900
  const hasExporters = data.exporters && data.exporters.length > 0;
13891
13901
  const hasBridge = !!data.bridge;
@@ -15034,18 +15044,144 @@ function chainFormatters(formatters) {
15034
15044
  return currentSpan;
15035
15045
  };
15036
15046
  }
15047
+ var SIGNAL_PUBLISH_SUFFIXES = {
15048
+ traces: "/spans/publish",
15049
+ logs: "/logs/publish",
15050
+ metrics: "/metrics/publish",
15051
+ scores: "/scores/publish",
15052
+ feedback: "/feedback/publish"
15053
+ };
15054
+ var SIGNAL_PUBLISH_PATHS = {
15055
+ traces: "/ai/spans/publish",
15056
+ logs: "/ai/logs/publish",
15057
+ metrics: "/ai/metrics/publish",
15058
+ scores: "/ai/scores/publish",
15059
+ feedback: "/ai/feedback/publish"
15060
+ };
15061
+ var SIGNAL_PAYLOAD_KEYS = {
15062
+ traces: "spans",
15063
+ logs: "logs",
15064
+ metrics: "metrics",
15065
+ scores: "scores",
15066
+ feedback: "feedback"
15067
+ };
15068
+ var DEFAULT_CLOUD_ENDPOINT = "https://api.mastra.ai";
15069
+ function trimTrailingSlashes(value) {
15070
+ let end = value.length;
15071
+ while (end > 0 && value.charCodeAt(end - 1) === 47) {
15072
+ end--;
15073
+ }
15074
+ return end === value.length ? value : value.slice(0, end);
15075
+ }
15076
+ function createInvalidEndpointError(endpoint, text, cause) {
15077
+ return new error$1.MastraError(
15078
+ {
15079
+ id: `CLOUD_EXPORTER_INVALID_ENDPOINT`,
15080
+ text,
15081
+ domain: error$1.ErrorDomain.MASTRA_OBSERVABILITY,
15082
+ category: error$1.ErrorCategory.USER,
15083
+ details: {
15084
+ endpoint
15085
+ }
15086
+ },
15087
+ cause
15088
+ );
15089
+ }
15090
+ function resolveBaseEndpoint(baseEndpoint) {
15091
+ const normalizedEndpoint = trimTrailingSlashes(baseEndpoint);
15092
+ const invalidText = 'CloudExporter endpoint must be a base origin like "https://collector.example.com" with no path, search, or hash.';
15093
+ try {
15094
+ const parsedEndpoint = new URL(normalizedEndpoint);
15095
+ if (parsedEndpoint.pathname !== "/" || parsedEndpoint.search || parsedEndpoint.hash) {
15096
+ throw createInvalidEndpointError(baseEndpoint, invalidText);
15097
+ }
15098
+ return trimTrailingSlashes(parsedEndpoint.origin);
15099
+ } catch (error48) {
15100
+ if (error48 instanceof error$1.MastraError) {
15101
+ throw error48;
15102
+ }
15103
+ throw createInvalidEndpointError(baseEndpoint, invalidText, error48);
15104
+ }
15105
+ }
15106
+ function buildSignalEndpoint(baseEndpoint, signal) {
15107
+ return `${baseEndpoint}${SIGNAL_PUBLISH_PATHS[signal]}`;
15108
+ }
15109
+ function resolveExplicitSignalEndpoint(signal, endpoint) {
15110
+ const normalizedEndpoint = trimTrailingSlashes(endpoint);
15111
+ const invalidText = `CloudExporter ${signal}Endpoint must be a base origin like "https://collector.example.com" or a full ${signal} publish URL ending in "${SIGNAL_PUBLISH_SUFFIXES[signal]}".`;
15112
+ try {
15113
+ const parsedEndpoint = new URL(normalizedEndpoint);
15114
+ if (parsedEndpoint.search || parsedEndpoint.hash) {
15115
+ throw createInvalidEndpointError(endpoint, invalidText);
15116
+ }
15117
+ const normalizedOrigin = trimTrailingSlashes(parsedEndpoint.origin);
15118
+ const normalizedPathname = trimTrailingSlashes(parsedEndpoint.pathname);
15119
+ if (!normalizedPathname || normalizedPathname === "/") {
15120
+ return buildSignalEndpoint(normalizedOrigin, signal);
15121
+ }
15122
+ if (normalizedPathname.endsWith(SIGNAL_PUBLISH_SUFFIXES[signal])) {
15123
+ return `${normalizedOrigin}${normalizedPathname}`;
15124
+ }
15125
+ throw createInvalidEndpointError(endpoint, invalidText);
15126
+ } catch (error48) {
15127
+ if (error48 instanceof error$1.MastraError) {
15128
+ throw error48;
15129
+ }
15130
+ throw createInvalidEndpointError(endpoint, invalidText, error48);
15131
+ }
15132
+ }
15133
+ function deriveSignalEndpointFromTracesEndpoint(signal, tracesEndpoint) {
15134
+ if (signal === "traces") {
15135
+ return tracesEndpoint;
15136
+ }
15137
+ const normalizedTracesEndpoint = trimTrailingSlashes(tracesEndpoint);
15138
+ const invalidText = 'CloudExporter tracesEndpoint must be a base origin like "https://collector.example.com" or a full traces publish URL ending in "/spans/publish".';
15139
+ try {
15140
+ const parsedEndpoint = new URL(normalizedTracesEndpoint);
15141
+ const normalizedOrigin = trimTrailingSlashes(parsedEndpoint.origin);
15142
+ const normalizedPathname = trimTrailingSlashes(parsedEndpoint.pathname);
15143
+ if (!normalizedPathname.endsWith(SIGNAL_PUBLISH_SUFFIXES.traces)) {
15144
+ throw createInvalidEndpointError(tracesEndpoint, invalidText);
15145
+ }
15146
+ const basePath = normalizedPathname.slice(0, -SIGNAL_PUBLISH_SUFFIXES.traces.length);
15147
+ return `${normalizedOrigin}${basePath}${SIGNAL_PUBLISH_SUFFIXES[signal]}`;
15148
+ } catch (error48) {
15149
+ if (error48 instanceof error$1.MastraError) {
15150
+ throw error48;
15151
+ }
15152
+ throw createInvalidEndpointError(tracesEndpoint, invalidText, error48);
15153
+ }
15154
+ }
15037
15155
  var CloudExporter = class extends BaseExporter {
15038
15156
  name = "mastra-cloud-observability-exporter";
15039
15157
  cloudConfig;
15040
15158
  buffer;
15041
15159
  flushTimer = null;
15160
+ inFlightFlushes = /* @__PURE__ */ new Set();
15042
15161
  constructor(config2 = {}) {
15043
15162
  super(config2);
15044
15163
  const accessToken = config2.accessToken ?? process.env.MASTRA_CLOUD_ACCESS_TOKEN;
15045
15164
  if (!accessToken) {
15046
15165
  this.setDisabled("MASTRA_CLOUD_ACCESS_TOKEN environment variable not set.");
15047
15166
  }
15048
- const endpoint = config2.endpoint ?? process.env.MASTRA_CLOUD_TRACES_ENDPOINT ?? "https://api.mastra.ai/ai/spans/publish";
15167
+ const tracesEndpointOverride = config2.tracesEndpoint ?? process.env.MASTRA_CLOUD_TRACES_ENDPOINT;
15168
+ let baseEndpoint;
15169
+ let tracesEndpoint;
15170
+ if (tracesEndpointOverride) {
15171
+ tracesEndpoint = resolveExplicitSignalEndpoint("traces", tracesEndpointOverride);
15172
+ } else {
15173
+ baseEndpoint = resolveBaseEndpoint(config2.endpoint ?? DEFAULT_CLOUD_ENDPOINT);
15174
+ tracesEndpoint = buildSignalEndpoint(baseEndpoint, "traces");
15175
+ }
15176
+ const resolveConfiguredSignalEndpoint = (signal, explicitEndpoint) => {
15177
+ if (explicitEndpoint) {
15178
+ return resolveExplicitSignalEndpoint(signal, explicitEndpoint);
15179
+ }
15180
+ if (tracesEndpointOverride) {
15181
+ return deriveSignalEndpointFromTracesEndpoint(signal, tracesEndpoint);
15182
+ }
15183
+ return buildSignalEndpoint(baseEndpoint, signal);
15184
+ };
15049
15185
  this.cloudConfig = {
15050
15186
  logger: this.logger,
15051
15187
  logLevel: config2.logLevel ?? logger.LogLevel.INFO,
@@ -15053,10 +15189,18 @@ var CloudExporter = class extends BaseExporter {
15053
15189
  maxBatchWaitMs: config2.maxBatchWaitMs ?? 5e3,
15054
15190
  maxRetries: config2.maxRetries ?? 3,
15055
15191
  accessToken: accessToken || "",
15056
- endpoint
15192
+ tracesEndpoint,
15193
+ logsEndpoint: resolveConfiguredSignalEndpoint("logs", config2.logsEndpoint),
15194
+ metricsEndpoint: resolveConfiguredSignalEndpoint("metrics", config2.metricsEndpoint),
15195
+ scoresEndpoint: resolveConfiguredSignalEndpoint("scores", config2.scoresEndpoint),
15196
+ feedbackEndpoint: resolveConfiguredSignalEndpoint("feedback", config2.feedbackEndpoint)
15057
15197
  };
15058
15198
  this.buffer = {
15059
15199
  spans: [],
15200
+ logs: [],
15201
+ metrics: [],
15202
+ scores: [],
15203
+ feedback: [],
15060
15204
  totalSize: 0
15061
15205
  };
15062
15206
  }
@@ -15065,45 +15209,111 @@ var CloudExporter = class extends BaseExporter {
15065
15209
  return;
15066
15210
  }
15067
15211
  this.addToBuffer(event);
15068
- if (this.shouldFlush()) {
15069
- this.flush().catch((error48) => {
15070
- this.logger.error("Batch flush failed", {
15071
- error: error48 instanceof Error ? error48.message : String(error48)
15072
- });
15073
- });
15074
- } else if (this.buffer.totalSize === 1) {
15075
- this.scheduleFlush();
15212
+ await this.handleBufferedEvent();
15213
+ }
15214
+ async onLogEvent(event) {
15215
+ if (this.isDisabled) {
15216
+ return;
15076
15217
  }
15218
+ this.addLogToBuffer(event);
15219
+ await this.handleBufferedEvent();
15077
15220
  }
15078
- addToBuffer(event) {
15079
- if (this.buffer.totalSize === 0) {
15080
- this.buffer.firstEventTime = /* @__PURE__ */ new Date();
15221
+ async onMetricEvent(event) {
15222
+ if (this.isDisabled) {
15223
+ return;
15224
+ }
15225
+ this.addMetricToBuffer(event);
15226
+ await this.handleBufferedEvent();
15227
+ }
15228
+ async onScoreEvent(event) {
15229
+ if (this.isDisabled) {
15230
+ return;
15231
+ }
15232
+ this.addScoreToBuffer(event);
15233
+ await this.handleBufferedEvent();
15234
+ }
15235
+ async onFeedbackEvent(event) {
15236
+ if (this.isDisabled) {
15237
+ return;
15081
15238
  }
15239
+ this.addFeedbackToBuffer(event);
15240
+ await this.handleBufferedEvent();
15241
+ }
15242
+ addToBuffer(event) {
15243
+ this.markBufferStart();
15082
15244
  const spanRecord = this.formatSpan(event.exportedSpan);
15083
15245
  this.buffer.spans.push(spanRecord);
15084
15246
  this.buffer.totalSize++;
15085
15247
  }
15248
+ addLogToBuffer(event) {
15249
+ this.markBufferStart();
15250
+ this.buffer.logs.push(this.formatLog(event.log));
15251
+ this.buffer.totalSize++;
15252
+ }
15253
+ addMetricToBuffer(event) {
15254
+ this.markBufferStart();
15255
+ this.buffer.metrics.push(this.formatMetric(event.metric));
15256
+ this.buffer.totalSize++;
15257
+ }
15258
+ addScoreToBuffer(event) {
15259
+ this.markBufferStart();
15260
+ this.buffer.scores.push(this.formatScore(event.score));
15261
+ this.buffer.totalSize++;
15262
+ }
15263
+ addFeedbackToBuffer(event) {
15264
+ this.markBufferStart();
15265
+ this.buffer.feedback.push(this.formatFeedback(event.feedback));
15266
+ this.buffer.totalSize++;
15267
+ }
15268
+ markBufferStart() {
15269
+ if (this.buffer.totalSize === 0) {
15270
+ this.buffer.firstEventTime = /* @__PURE__ */ new Date();
15271
+ }
15272
+ }
15086
15273
  formatSpan(span) {
15087
15274
  const spanRecord = {
15088
- traceId: span.traceId,
15275
+ ...span,
15089
15276
  spanId: span.id,
15090
- parentSpanId: span.parentSpanId ?? null,
15091
- name: span.name,
15092
15277
  spanType: span.type,
15093
- attributes: span.attributes ?? null,
15094
- metadata: span.metadata ?? null,
15095
- requestContext: span.requestContext ?? null,
15096
15278
  startedAt: span.startTime,
15097
15279
  endedAt: span.endTime ?? null,
15098
- input: span.input ?? null,
15099
- output: span.output ?? null,
15100
- error: span.errorInfo,
15101
- isEvent: span.isEvent,
15280
+ error: span.errorInfo ?? null,
15102
15281
  createdAt: /* @__PURE__ */ new Date(),
15103
15282
  updatedAt: null
15104
15283
  };
15105
15284
  return spanRecord;
15106
15285
  }
15286
+ formatLog(log) {
15287
+ return {
15288
+ ...log
15289
+ };
15290
+ }
15291
+ formatMetric(metric) {
15292
+ return {
15293
+ ...metric
15294
+ };
15295
+ }
15296
+ formatScore(score) {
15297
+ return {
15298
+ ...score
15299
+ };
15300
+ }
15301
+ formatFeedback(feedback) {
15302
+ return {
15303
+ ...feedback
15304
+ };
15305
+ }
15306
+ async handleBufferedEvent() {
15307
+ if (this.shouldFlush()) {
15308
+ void this.flush().catch((error48) => {
15309
+ this.logger.error("Batch flush failed", {
15310
+ error: error48 instanceof Error ? error48.message : String(error48)
15311
+ });
15312
+ });
15313
+ } else if (this.buffer.totalSize === 1) {
15314
+ this.scheduleFlush();
15315
+ }
15316
+ }
15107
15317
  shouldFlush() {
15108
15318
  if (this.buffer.totalSize >= this.cloudConfig.maxBatchSize) {
15109
15319
  return true;
@@ -15121,7 +15331,7 @@ var CloudExporter = class extends BaseExporter {
15121
15331
  clearTimeout(this.flushTimer);
15122
15332
  }
15123
15333
  this.flushTimer = setTimeout(() => {
15124
- this.flush().catch((error48) => {
15334
+ void this.flush().catch((error48) => {
15125
15335
  const mastraError = new error$1.MastraError(
15126
15336
  {
15127
15337
  id: `CLOUD_EXPORTER_FAILED_TO_SCHEDULE_FLUSH`,
@@ -15145,49 +15355,91 @@ var CloudExporter = class extends BaseExporter {
15145
15355
  }
15146
15356
  const startTime = Date.now();
15147
15357
  const spansCopy = [...this.buffer.spans];
15358
+ const logsCopy = [...this.buffer.logs];
15359
+ const metricsCopy = [...this.buffer.metrics];
15360
+ const scoresCopy = [...this.buffer.scores];
15361
+ const feedbackCopy = [...this.buffer.feedback];
15362
+ const batchSize = this.buffer.totalSize;
15148
15363
  const flushReason = this.buffer.totalSize >= this.cloudConfig.maxBatchSize ? "size" : "time";
15149
15364
  this.resetBuffer();
15150
- try {
15151
- await this.batchUpload(spansCopy);
15152
- const elapsed = Date.now() - startTime;
15365
+ const results = await Promise.all([
15366
+ this.flushSignalBatch("traces", spansCopy),
15367
+ this.flushSignalBatch("logs", logsCopy),
15368
+ this.flushSignalBatch("metrics", metricsCopy),
15369
+ this.flushSignalBatch("scores", scoresCopy),
15370
+ this.flushSignalBatch("feedback", feedbackCopy)
15371
+ ]);
15372
+ const failedSignals = results.filter((result) => !result.succeeded).map((result) => result.signal);
15373
+ const elapsed = Date.now() - startTime;
15374
+ if (failedSignals.length === 0) {
15153
15375
  this.logger.debug("Batch flushed successfully", {
15154
- batchSize: spansCopy.length,
15376
+ batchSize,
15155
15377
  flushReason,
15156
15378
  durationMs: elapsed
15157
15379
  });
15380
+ return;
15381
+ }
15382
+ this.logger.warn("Batch flush completed with dropped signal batches", {
15383
+ batchSize,
15384
+ flushReason,
15385
+ durationMs: elapsed,
15386
+ failedSignals
15387
+ });
15388
+ }
15389
+ /**
15390
+ * Uploads a signal batch to the configured cloud API using fetchWithRetry.
15391
+ */
15392
+ async batchUpload(signal, records) {
15393
+ const headers = {
15394
+ Authorization: `Bearer ${this.cloudConfig.accessToken}`,
15395
+ "Content-Type": "application/json"
15396
+ };
15397
+ const endpointMap = {
15398
+ traces: this.cloudConfig.tracesEndpoint,
15399
+ logs: this.cloudConfig.logsEndpoint,
15400
+ metrics: this.cloudConfig.metricsEndpoint,
15401
+ scores: this.cloudConfig.scoresEndpoint,
15402
+ feedback: this.cloudConfig.feedbackEndpoint
15403
+ };
15404
+ const options = {
15405
+ method: "POST",
15406
+ headers,
15407
+ body: JSON.stringify({ [SIGNAL_PAYLOAD_KEYS[signal]]: records })
15408
+ };
15409
+ await utils.fetchWithRetry(endpointMap[signal], options, this.cloudConfig.maxRetries);
15410
+ }
15411
+ async flushSignalBatch(signal, records) {
15412
+ if (records.length === 0) {
15413
+ return { signal, succeeded: true };
15414
+ }
15415
+ try {
15416
+ await this.batchUpload(signal, records);
15417
+ return { signal, succeeded: true };
15158
15418
  } catch (error48) {
15419
+ const errorId = `CLOUD_EXPORTER_FAILED_TO_BATCH_UPLOAD_${signal.toUpperCase()}`;
15159
15420
  const mastraError = new error$1.MastraError(
15160
15421
  {
15161
- id: `CLOUD_EXPORTER_FAILED_TO_BATCH_UPLOAD`,
15422
+ id: errorId,
15162
15423
  domain: error$1.ErrorDomain.MASTRA_OBSERVABILITY,
15163
15424
  category: error$1.ErrorCategory.USER,
15164
15425
  details: {
15165
- droppedBatchSize: spansCopy.length
15426
+ signal,
15427
+ droppedBatchSize: records.length
15166
15428
  }
15167
15429
  },
15168
15430
  error48
15169
15431
  );
15170
15432
  this.logger.trackException(mastraError);
15171
15433
  this.logger.error("Batch upload failed after all retries, dropping batch", mastraError);
15434
+ return { signal, succeeded: false };
15172
15435
  }
15173
15436
  }
15174
- /**
15175
- * Uploads spans to cloud API using fetchWithRetry for all retry logic
15176
- */
15177
- async batchUpload(spans) {
15178
- const headers = {
15179
- Authorization: `Bearer ${this.cloudConfig.accessToken}`,
15180
- "Content-Type": "application/json"
15181
- };
15182
- const options = {
15183
- method: "POST",
15184
- headers,
15185
- body: JSON.stringify({ spans })
15186
- };
15187
- await utils.fetchWithRetry(this.cloudConfig.endpoint, options, this.cloudConfig.maxRetries);
15188
- }
15189
15437
  resetBuffer() {
15190
15438
  this.buffer.spans = [];
15439
+ this.buffer.logs = [];
15440
+ this.buffer.metrics = [];
15441
+ this.buffer.scores = [];
15442
+ this.buffer.feedback = [];
15191
15443
  this.buffer.firstEventTime = void 0;
15192
15444
  this.buffer.totalSize = 0;
15193
15445
  }
@@ -15200,11 +15452,21 @@ var CloudExporter = class extends BaseExporter {
15200
15452
  if (this.isDisabled) {
15201
15453
  return;
15202
15454
  }
15203
- if (this.buffer.totalSize > 0) {
15204
- this.logger.debug("Flushing buffered events", {
15205
- bufferedEvents: this.buffer.totalSize
15206
- });
15207
- await this.flushBuffer();
15455
+ while (this.buffer.totalSize > 0 || this.inFlightFlushes.size > 0) {
15456
+ if (this.buffer.totalSize > 0) {
15457
+ this.logger.debug("Flushing buffered events", {
15458
+ bufferedEvents: this.buffer.totalSize
15459
+ });
15460
+ const flushPromise = this.flushBuffer();
15461
+ this.inFlightFlushes.add(flushPromise);
15462
+ try {
15463
+ await flushPromise;
15464
+ } finally {
15465
+ this.inFlightFlushes.delete(flushPromise);
15466
+ }
15467
+ continue;
15468
+ }
15469
+ await Promise.allSettled([...this.inFlightFlushes]);
15208
15470
  }
15209
15471
  }
15210
15472
  async shutdown() {
@@ -16442,6 +16704,9 @@ var TestExporter = class extends BaseExporter {
16442
16704
  if (value instanceof Date) {
16443
16705
  return "<date>";
16444
16706
  }
16707
+ if (key === "createdAt" && typeof value === "number") {
16708
+ return "<date>";
16709
+ }
16445
16710
  if (typeof value === "string") {
16446
16711
  if (key === "traceId" && (uuidRegex.test(value) || hexId32Regex.test(value))) {
16447
16712
  if (!traceIdMap.has(value)) {
@@ -16470,7 +16735,23 @@ var TestExporter = class extends BaseExporter {
16470
16735
  if (value && typeof value === "object") {
16471
16736
  const normalized = {};
16472
16737
  for (const [k, v] of Object.entries(value)) {
16473
- normalized[k] = normalizeValue(v, k);
16738
+ if (key === "providerOptions" && k === "mastra" && v && typeof v === "object") {
16739
+ const mastraOptions = v;
16740
+ const remainingMastraOptions = Object.fromEntries(
16741
+ Object.entries(mastraOptions).filter(([mastraKey]) => mastraKey !== "createdAt")
16742
+ );
16743
+ if (Object.keys(remainingMastraOptions).length > 0) {
16744
+ normalized[k] = normalizeValue(remainingMastraOptions, k);
16745
+ }
16746
+ continue;
16747
+ }
16748
+ const normalizedValue = normalizeValue(v, k);
16749
+ if (normalizedValue !== void 0) {
16750
+ normalized[k] = normalizedValue;
16751
+ }
16752
+ }
16753
+ if (key === "providerOptions" && Object.keys(normalized).length === 0) {
16754
+ return void 0;
16474
16755
  }
16475
16756
  return normalized;
16476
16757
  }
@@ -17044,69 +17325,436 @@ var BaseObservabilityEventBus = class _BaseObservabilityEventBus extends base.Ma
17044
17325
  }
17045
17326
  };
17046
17327
 
17047
- // src/bus/observability-bus.ts
17048
- var MAX_FLUSH_ITERATIONS = 3;
17049
- var ObservabilityBus = class extends BaseObservabilityEventBus {
17050
- exporters = [];
17051
- bridge;
17052
- /** In-flight handler promises from routeToHandler. Self-cleaning via .finally(). */
17053
- pendingHandlers = /* @__PURE__ */ new Set();
17054
- constructor() {
17055
- super({ name: "ObservabilityBus" });
17328
+ // src/spans/serialization.ts
17329
+ var DEFAULT_KEYS_TO_STRIP = /* @__PURE__ */ new Set([
17330
+ "logger",
17331
+ "experimental_providerMetadata",
17332
+ "providerMetadata",
17333
+ "steps",
17334
+ "tracingContext",
17335
+ "execute",
17336
+ // Tool execute functions
17337
+ "validate"
17338
+ // Schema validate functions
17339
+ ]);
17340
+ var DEFAULT_DEEP_CLEAN_OPTIONS = Object.freeze({
17341
+ keysToStrip: DEFAULT_KEYS_TO_STRIP,
17342
+ maxDepth: 8,
17343
+ maxStringLength: 128 * 1024,
17344
+ // 128KB - sufficient for large LLM prompts/responses
17345
+ maxArrayLength: 50,
17346
+ maxObjectKeys: 50
17347
+ });
17348
+ function mergeSerializationOptions(userOptions) {
17349
+ if (!userOptions) {
17350
+ return DEFAULT_DEEP_CLEAN_OPTIONS;
17056
17351
  }
17057
- /**
17058
- * Register an exporter to receive routed events.
17059
- * Duplicate registrations (same instance) are silently ignored.
17060
- *
17061
- * @param exporter - The exporter to register.
17062
- */
17063
- registerExporter(exporter) {
17064
- if (this.exporters.includes(exporter)) {
17065
- return;
17066
- }
17067
- this.exporters.push(exporter);
17352
+ return {
17353
+ keysToStrip: DEFAULT_KEYS_TO_STRIP,
17354
+ maxDepth: userOptions.maxDepth ?? DEFAULT_DEEP_CLEAN_OPTIONS.maxDepth,
17355
+ maxStringLength: userOptions.maxStringLength ?? DEFAULT_DEEP_CLEAN_OPTIONS.maxStringLength,
17356
+ maxArrayLength: userOptions.maxArrayLength ?? DEFAULT_DEEP_CLEAN_OPTIONS.maxArrayLength,
17357
+ maxObjectKeys: userOptions.maxObjectKeys ?? DEFAULT_DEEP_CLEAN_OPTIONS.maxObjectKeys
17358
+ };
17359
+ }
17360
+ function truncateString(s, maxChars) {
17361
+ if (s.length <= maxChars) {
17362
+ return s;
17068
17363
  }
17069
- /**
17070
- * Unregister an exporter.
17071
- *
17072
- * @param exporter - The exporter instance to remove.
17073
- * @returns `true` if the exporter was found and removed, `false` otherwise.
17074
- */
17075
- unregisterExporter(exporter) {
17076
- const index = this.exporters.indexOf(exporter);
17077
- if (index !== -1) {
17078
- this.exporters.splice(index, 1);
17079
- return true;
17080
- }
17081
- return false;
17364
+ return s.slice(0, maxChars) + "\u2026[truncated]";
17365
+ }
17366
+ function formatSerializationError(error48) {
17367
+ return `[${error48 instanceof Error ? truncateString(error48.message, 256) : "unknown error"}]`;
17368
+ }
17369
+ function getMapKeyType(key) {
17370
+ if (key === null) {
17371
+ return "null";
17082
17372
  }
17083
- /**
17084
- * Get registered exporters (read-only snapshot).
17085
- */
17086
- getExporters() {
17087
- return [...this.exporters];
17373
+ if (key instanceof Date) {
17374
+ return "date";
17088
17375
  }
17089
- /**
17090
- * Register a bridge to receive all routed events alongside exporters.
17091
- * Only one bridge can be registered at a time; replacing an existing bridge
17092
- * logs a warning.
17093
- *
17094
- * @param bridge - The bridge to register.
17095
- */
17096
- registerBridge(bridge) {
17097
- if (this.bridge) {
17098
- this.logger.warn(`[ObservabilityBus] Replacing existing bridge with new bridge`);
17099
- }
17100
- this.bridge = bridge;
17376
+ if (Array.isArray(key)) {
17377
+ return "array";
17101
17378
  }
17102
- /**
17103
- * Unregister the bridge.
17104
- *
17105
- * @returns `true` if a bridge was registered and removed, `false` otherwise.
17106
- */
17107
- unregisterBridge() {
17108
- if (this.bridge) {
17109
- this.bridge = void 0;
17379
+ if (key instanceof Map) {
17380
+ return "map";
17381
+ }
17382
+ if (key instanceof Set) {
17383
+ return "set";
17384
+ }
17385
+ if (key instanceof Error) {
17386
+ return "error";
17387
+ }
17388
+ return typeof key;
17389
+ }
17390
+ function restoreSerializedMapKey(keyType, key) {
17391
+ switch (keyType) {
17392
+ case "undefined":
17393
+ return void 0;
17394
+ case "null":
17395
+ return null;
17396
+ case "bigint":
17397
+ return typeof key === "string" && key.endsWith("n") ? BigInt(key.slice(0, -1)) : key;
17398
+ case "date":
17399
+ return typeof key === "string" ? new Date(key) : key;
17400
+ default:
17401
+ return key;
17402
+ }
17403
+ }
17404
+ function isSerializedMap(value) {
17405
+ return typeof value === "object" && value !== null && value.__type === "Map" && Array.isArray(value.__map_entries);
17406
+ }
17407
+ function reconstructSerializedMap(value) {
17408
+ return new Map(
17409
+ value.__map_entries.map(([keyType, key, mapValue]) => [restoreSerializedMapKey(keyType, key), mapValue])
17410
+ );
17411
+ }
17412
+ function isJsonSchema(val) {
17413
+ if (typeof val !== "object" || val === null) return false;
17414
+ if (val.$schema && typeof val.$schema === "string" && val.$schema.includes("json-schema")) {
17415
+ return true;
17416
+ }
17417
+ if (val.type === "object" && val.properties && typeof val.properties === "object") {
17418
+ return true;
17419
+ }
17420
+ return false;
17421
+ }
17422
+ function compressJsonSchema(schema, depth = 0) {
17423
+ if (depth > 3) {
17424
+ return schema.type || "object";
17425
+ }
17426
+ const compositionKeys = ["oneOf", "anyOf", "allOf"].filter((key) => Array.isArray(schema[key]));
17427
+ if (compositionKeys.length > 0) {
17428
+ const compressed2 = {};
17429
+ for (const key of compositionKeys) {
17430
+ compressed2[key] = schema[key].map((entry) => compressJsonSchema(entry, depth + 1));
17431
+ }
17432
+ if (typeof schema.type === "string") {
17433
+ compressed2.type = schema.type;
17434
+ }
17435
+ return compressed2;
17436
+ }
17437
+ if (schema.type !== "object" || !schema.properties) {
17438
+ return schema.type || schema;
17439
+ }
17440
+ const required2 = new Set(Array.isArray(schema.required) ? schema.required : []);
17441
+ const compressed = {};
17442
+ for (const [key, propSchema] of Object.entries(schema.properties)) {
17443
+ const prop = propSchema;
17444
+ let value = prop.type || "unknown";
17445
+ if (prop.type === "object" && prop.properties) {
17446
+ value = compressJsonSchema(prop, depth + 1);
17447
+ if (required2.has(key)) {
17448
+ compressed[key + " (required)"] = value;
17449
+ continue;
17450
+ }
17451
+ } else if (prop.type === "array" && prop.items) {
17452
+ if (prop.items.type === "object" && prop.items.properties) {
17453
+ value = [compressJsonSchema(prop.items, depth + 1)];
17454
+ } else {
17455
+ value = `${prop.items.type || "any"}[]`;
17456
+ }
17457
+ } else if (prop.enum) {
17458
+ value = prop.enum.map((v) => JSON.stringify(v)).join(" | ");
17459
+ }
17460
+ if (required2.has(key) && typeof value === "string") {
17461
+ value += " (required)";
17462
+ }
17463
+ compressed[key] = value;
17464
+ }
17465
+ return compressed;
17466
+ }
17467
+ function deepClean(value, options = DEFAULT_DEEP_CLEAN_OPTIONS) {
17468
+ const { keysToStrip, maxDepth, maxStringLength, maxArrayLength, maxObjectKeys } = options;
17469
+ const stripSet = keysToStrip instanceof Set ? keysToStrip : new Set(Array.isArray(keysToStrip) ? keysToStrip : Object.keys(keysToStrip));
17470
+ const ancestors = /* @__PURE__ */ new WeakSet();
17471
+ function helper(val, depth) {
17472
+ if (depth > maxDepth) {
17473
+ return "[MaxDepth]";
17474
+ }
17475
+ if (val === null || val === void 0) {
17476
+ return val;
17477
+ }
17478
+ if (typeof val === "string") {
17479
+ return truncateString(val, maxStringLength);
17480
+ }
17481
+ if (typeof val === "number" || typeof val === "boolean") {
17482
+ return val;
17483
+ }
17484
+ if (typeof val === "bigint") {
17485
+ return `${val}n`;
17486
+ }
17487
+ if (typeof val === "function") {
17488
+ return "[Function]";
17489
+ }
17490
+ if (typeof val === "symbol") {
17491
+ return val.description ? `[Symbol(${val.description})]` : "[Symbol]";
17492
+ }
17493
+ if (val instanceof Date) {
17494
+ return val;
17495
+ }
17496
+ if (typeof val === "object") {
17497
+ if (ancestors.has(val)) {
17498
+ return "[Circular]";
17499
+ }
17500
+ ancestors.add(val);
17501
+ }
17502
+ try {
17503
+ if (val instanceof Error) {
17504
+ let errorName;
17505
+ let errorMessage;
17506
+ let errorStack;
17507
+ let rawCause;
17508
+ let causeReadFailed = false;
17509
+ try {
17510
+ errorName = val.name;
17511
+ } catch (error48) {
17512
+ errorName = formatSerializationError(error48);
17513
+ }
17514
+ try {
17515
+ errorMessage = val.message;
17516
+ } catch (error48) {
17517
+ errorMessage = formatSerializationError(error48);
17518
+ }
17519
+ try {
17520
+ errorStack = val.stack;
17521
+ } catch (error48) {
17522
+ errorStack = formatSerializationError(error48);
17523
+ }
17524
+ try {
17525
+ rawCause = val.cause;
17526
+ } catch (error48) {
17527
+ causeReadFailed = true;
17528
+ rawCause = formatSerializationError(error48);
17529
+ }
17530
+ const cleanedError = {
17531
+ name: typeof errorName === "string" ? truncateString(errorName, maxStringLength) : errorName,
17532
+ message: typeof errorMessage === "string" ? truncateString(errorMessage, maxStringLength) : errorMessage
17533
+ };
17534
+ if (typeof errorStack === "string") {
17535
+ cleanedError.stack = truncateString(errorStack, maxStringLength);
17536
+ } else if (errorStack !== void 0) {
17537
+ cleanedError.stack = errorStack;
17538
+ }
17539
+ if (causeReadFailed) {
17540
+ cleanedError.cause = rawCause;
17541
+ } else if (rawCause !== void 0) {
17542
+ try {
17543
+ cleanedError.cause = helper(rawCause, depth + 1);
17544
+ } catch (error48) {
17545
+ cleanedError.cause = formatSerializationError(error48);
17546
+ }
17547
+ }
17548
+ return cleanedError;
17549
+ }
17550
+ if (val instanceof Map) {
17551
+ const cleanedMap = { __type: "Map", __map_entries: [] };
17552
+ let mapKeyCount = 0;
17553
+ let omittedMapEntries = 0;
17554
+ for (const [mapKey, mapVal] of val) {
17555
+ if (typeof mapKey === "string" && stripSet.has(mapKey)) {
17556
+ continue;
17557
+ }
17558
+ if (mapKeyCount >= maxObjectKeys) {
17559
+ omittedMapEntries++;
17560
+ continue;
17561
+ }
17562
+ const mapKeyType = getMapKeyType(mapKey);
17563
+ let cleanedMapKey;
17564
+ let cleanedMapValue;
17565
+ try {
17566
+ cleanedMapKey = helper(mapKey, depth + 1);
17567
+ } catch (error48) {
17568
+ cleanedMapKey = formatSerializationError(error48);
17569
+ }
17570
+ try {
17571
+ cleanedMapValue = helper(mapVal, depth + 1);
17572
+ } catch (error48) {
17573
+ cleanedMapValue = formatSerializationError(error48);
17574
+ }
17575
+ cleanedMap.__map_entries.push([mapKeyType, cleanedMapKey, cleanedMapValue]);
17576
+ mapKeyCount++;
17577
+ }
17578
+ if (omittedMapEntries > 0) {
17579
+ cleanedMap.__truncated = `${omittedMapEntries} more keys omitted`;
17580
+ }
17581
+ return cleanedMap;
17582
+ }
17583
+ if (val instanceof Set) {
17584
+ const cleanedSet = [];
17585
+ let i = 0;
17586
+ const totalSetSize = val.size;
17587
+ for (const item of val) {
17588
+ if (i >= maxArrayLength) break;
17589
+ try {
17590
+ cleanedSet.push(helper(item, depth + 1));
17591
+ } catch (error48) {
17592
+ cleanedSet.push(formatSerializationError(error48));
17593
+ }
17594
+ i++;
17595
+ }
17596
+ if (totalSetSize > maxArrayLength) {
17597
+ cleanedSet.push(`[\u2026${totalSetSize - maxArrayLength} more items]`);
17598
+ }
17599
+ return cleanedSet;
17600
+ }
17601
+ if (Array.isArray(val)) {
17602
+ const cleaned2 = [];
17603
+ for (let i = 0; i < Math.min(val.length, maxArrayLength); i++) {
17604
+ try {
17605
+ cleaned2.push(helper(val[i], depth + 1));
17606
+ } catch (error48) {
17607
+ cleaned2.push(formatSerializationError(error48));
17608
+ }
17609
+ }
17610
+ if (val.length > maxArrayLength) {
17611
+ cleaned2.push(`[\u2026${val.length - maxArrayLength} more items]`);
17612
+ }
17613
+ return cleaned2;
17614
+ }
17615
+ if (typeof Buffer !== "undefined" && Buffer.isBuffer(val)) {
17616
+ return `[Buffer length=${val.length}]`;
17617
+ }
17618
+ if (ArrayBuffer.isView(val)) {
17619
+ const ctor = val.constructor?.name ?? "TypedArray";
17620
+ const byteLength = val.byteLength ?? "?";
17621
+ return `[${ctor} byteLength=${byteLength}]`;
17622
+ }
17623
+ if (val instanceof ArrayBuffer) {
17624
+ return `[ArrayBuffer byteLength=${val.byteLength}]`;
17625
+ }
17626
+ let serializeForSpan;
17627
+ try {
17628
+ serializeForSpan = val.serializeForSpan;
17629
+ } catch (error48) {
17630
+ return `[serializeForSpan failed: ${error48 instanceof Error ? truncateString(error48.message, 256) : "unknown error"}]`;
17631
+ }
17632
+ if (typeof serializeForSpan === "function") {
17633
+ try {
17634
+ return helper(serializeForSpan.call(val), depth);
17635
+ } catch (error48) {
17636
+ return `[serializeForSpan failed: ${error48 instanceof Error ? truncateString(error48.message, 256) : "unknown error"}]`;
17637
+ }
17638
+ }
17639
+ let looksLikeJsonSchema = false;
17640
+ try {
17641
+ looksLikeJsonSchema = isJsonSchema(val);
17642
+ } catch {
17643
+ looksLikeJsonSchema = false;
17644
+ }
17645
+ if (looksLikeJsonSchema) {
17646
+ try {
17647
+ const compressed = compressJsonSchema(val);
17648
+ return compressed === val ? "[JSONSchema]" : helper(compressed, depth);
17649
+ } catch {
17650
+ }
17651
+ }
17652
+ const cleaned = {};
17653
+ const keys = Object.keys(val).filter((key) => !stripSet.has(key));
17654
+ let keyCount = 0;
17655
+ for (const key of keys) {
17656
+ if (keyCount >= maxObjectKeys) {
17657
+ cleaned["__truncated"] = `${keys.length - keyCount} more keys omitted`;
17658
+ break;
17659
+ }
17660
+ try {
17661
+ cleaned[key] = helper(val[key], depth + 1);
17662
+ keyCount++;
17663
+ } catch (error48) {
17664
+ cleaned[key] = formatSerializationError(error48);
17665
+ keyCount++;
17666
+ }
17667
+ }
17668
+ return cleaned;
17669
+ } finally {
17670
+ if (typeof val === "object" && val !== null) {
17671
+ ancestors.delete(val);
17672
+ }
17673
+ }
17674
+ }
17675
+ return helper(value, 0);
17676
+ }
17677
+
17678
+ // src/bus/observability-bus.ts
17679
+ function cleanEvent(event, options) {
17680
+ switch (event.type) {
17681
+ case "log":
17682
+ return { type: "log", log: deepClean(event.log, options) };
17683
+ case "metric":
17684
+ return { type: "metric", metric: deepClean(event.metric, options) };
17685
+ case "score":
17686
+ return { type: "score", score: deepClean(event.score, options) };
17687
+ case "feedback":
17688
+ return { type: "feedback", feedback: deepClean(event.feedback, options) };
17689
+ default:
17690
+ return event;
17691
+ }
17692
+ }
17693
+ var MAX_FLUSH_ITERATIONS = 3;
17694
+ var ObservabilityBus = class extends BaseObservabilityEventBus {
17695
+ exporters = [];
17696
+ bridge;
17697
+ /** In-flight handler promises from routeToHandler. Self-cleaning via .finally(). */
17698
+ pendingHandlers = /* @__PURE__ */ new Set();
17699
+ /** Resolved deepClean options applied to non-tracing events before fan-out. */
17700
+ deepCleanOptions;
17701
+ constructor(opts) {
17702
+ super({ name: "ObservabilityBus" });
17703
+ this.deepCleanOptions = mergeSerializationOptions(opts?.serializationOptions);
17704
+ }
17705
+ /**
17706
+ * Register an exporter to receive routed events.
17707
+ * Duplicate registrations (same instance) are silently ignored.
17708
+ *
17709
+ * @param exporter - The exporter to register.
17710
+ */
17711
+ registerExporter(exporter) {
17712
+ if (this.exporters.includes(exporter)) {
17713
+ return;
17714
+ }
17715
+ this.exporters.push(exporter);
17716
+ }
17717
+ /**
17718
+ * Unregister an exporter.
17719
+ *
17720
+ * @param exporter - The exporter instance to remove.
17721
+ * @returns `true` if the exporter was found and removed, `false` otherwise.
17722
+ */
17723
+ unregisterExporter(exporter) {
17724
+ const index = this.exporters.indexOf(exporter);
17725
+ if (index !== -1) {
17726
+ this.exporters.splice(index, 1);
17727
+ return true;
17728
+ }
17729
+ return false;
17730
+ }
17731
+ /**
17732
+ * Get registered exporters (read-only snapshot).
17733
+ */
17734
+ getExporters() {
17735
+ return [...this.exporters];
17736
+ }
17737
+ /**
17738
+ * Register a bridge to receive all routed events alongside exporters.
17739
+ * Only one bridge can be registered at a time; replacing an existing bridge
17740
+ * logs a warning.
17741
+ *
17742
+ * @param bridge - The bridge to register.
17743
+ */
17744
+ registerBridge(bridge) {
17745
+ if (this.bridge) {
17746
+ this.logger.warn(`[ObservabilityBus] Replacing existing bridge with new bridge`);
17747
+ }
17748
+ this.bridge = bridge;
17749
+ }
17750
+ /**
17751
+ * Unregister the bridge.
17752
+ *
17753
+ * @returns `true` if a bridge was registered and removed, `false` otherwise.
17754
+ */
17755
+ unregisterBridge() {
17756
+ if (this.bridge) {
17757
+ this.bridge = void 0;
17110
17758
  return true;
17111
17759
  }
17112
17760
  return false;
@@ -17125,13 +17773,14 @@ var ObservabilityBus = class extends BaseObservabilityEventBus {
17125
17773
  * and can be drained via flush().
17126
17774
  */
17127
17775
  emit(event) {
17776
+ const cleaned = cleanEvent(event, this.deepCleanOptions);
17128
17777
  for (const exporter of this.exporters) {
17129
- this.trackPromise(routeToHandler(exporter, event, this.logger));
17778
+ this.trackPromise(routeToHandler(exporter, cleaned, this.logger));
17130
17779
  }
17131
17780
  if (this.bridge) {
17132
- this.trackPromise(routeToHandler(this.bridge, event, this.logger));
17781
+ this.trackPromise(routeToHandler(this.bridge, cleaned, this.logger));
17133
17782
  }
17134
- super.emit(event);
17783
+ super.emit(cleaned);
17135
17784
  }
17136
17785
  /**
17137
17786
  * Track an async handler promise so flush() can await it.
@@ -18402,218 +19051,6 @@ var ModelSpanTracker = class {
18402
19051
  }
18403
19052
  };
18404
19053
 
18405
- // src/spans/serialization.ts
18406
- var DEFAULT_KEYS_TO_STRIP = /* @__PURE__ */ new Set([
18407
- "logger",
18408
- "experimental_providerMetadata",
18409
- "providerMetadata",
18410
- "steps",
18411
- "tracingContext",
18412
- "execute",
18413
- // Tool execute functions
18414
- "validate"
18415
- // Schema validate functions
18416
- ]);
18417
- var DEFAULT_DEEP_CLEAN_OPTIONS = Object.freeze({
18418
- keysToStrip: DEFAULT_KEYS_TO_STRIP,
18419
- maxDepth: 8,
18420
- maxStringLength: 128 * 1024,
18421
- // 128KB - sufficient for large LLM prompts/responses
18422
- maxArrayLength: 50,
18423
- maxObjectKeys: 50
18424
- });
18425
- function mergeSerializationOptions(userOptions) {
18426
- if (!userOptions) {
18427
- return DEFAULT_DEEP_CLEAN_OPTIONS;
18428
- }
18429
- return {
18430
- keysToStrip: DEFAULT_KEYS_TO_STRIP,
18431
- maxDepth: userOptions.maxDepth ?? DEFAULT_DEEP_CLEAN_OPTIONS.maxDepth,
18432
- maxStringLength: userOptions.maxStringLength ?? DEFAULT_DEEP_CLEAN_OPTIONS.maxStringLength,
18433
- maxArrayLength: userOptions.maxArrayLength ?? DEFAULT_DEEP_CLEAN_OPTIONS.maxArrayLength,
18434
- maxObjectKeys: userOptions.maxObjectKeys ?? DEFAULT_DEEP_CLEAN_OPTIONS.maxObjectKeys
18435
- };
18436
- }
18437
- function truncateString(s, maxChars) {
18438
- if (s.length <= maxChars) {
18439
- return s;
18440
- }
18441
- return s.slice(0, maxChars) + "\u2026[truncated]";
18442
- }
18443
- function isJsonSchema(val) {
18444
- if (typeof val !== "object" || val === null) return false;
18445
- if (val.$schema && typeof val.$schema === "string" && val.$schema.includes("json-schema")) {
18446
- return true;
18447
- }
18448
- if (val.type === "object" && val.properties && typeof val.properties === "object") {
18449
- return true;
18450
- }
18451
- return false;
18452
- }
18453
- function compressJsonSchema(schema, depth = 0) {
18454
- if (depth > 3) {
18455
- return schema.type || "object";
18456
- }
18457
- const compositionKeys = ["oneOf", "anyOf", "allOf"].filter((key) => Array.isArray(schema[key]));
18458
- if (compositionKeys.length > 0) {
18459
- const compressed2 = {};
18460
- for (const key of compositionKeys) {
18461
- compressed2[key] = schema[key].map((entry) => compressJsonSchema(entry, depth + 1));
18462
- }
18463
- if (typeof schema.type === "string") {
18464
- compressed2.type = schema.type;
18465
- }
18466
- return compressed2;
18467
- }
18468
- if (schema.type !== "object" || !schema.properties) {
18469
- return schema.type || schema;
18470
- }
18471
- const required2 = new Set(Array.isArray(schema.required) ? schema.required : []);
18472
- const compressed = {};
18473
- for (const [key, propSchema] of Object.entries(schema.properties)) {
18474
- const prop = propSchema;
18475
- let value = prop.type || "unknown";
18476
- if (prop.type === "object" && prop.properties) {
18477
- value = compressJsonSchema(prop, depth + 1);
18478
- if (required2.has(key)) {
18479
- compressed[key + " (required)"] = value;
18480
- continue;
18481
- }
18482
- } else if (prop.type === "array" && prop.items) {
18483
- if (prop.items.type === "object" && prop.items.properties) {
18484
- value = [compressJsonSchema(prop.items, depth + 1)];
18485
- } else {
18486
- value = `${prop.items.type || "any"}[]`;
18487
- }
18488
- } else if (prop.enum) {
18489
- value = prop.enum.map((v) => JSON.stringify(v)).join(" | ");
18490
- }
18491
- if (required2.has(key) && typeof value === "string") {
18492
- value += " (required)";
18493
- }
18494
- compressed[key] = value;
18495
- }
18496
- return compressed;
18497
- }
18498
- function deepClean(value, options = DEFAULT_DEEP_CLEAN_OPTIONS) {
18499
- const { keysToStrip, maxDepth, maxStringLength, maxArrayLength, maxObjectKeys } = options;
18500
- const stripSet = keysToStrip instanceof Set ? keysToStrip : new Set(Array.isArray(keysToStrip) ? keysToStrip : Object.keys(keysToStrip));
18501
- const ancestors = /* @__PURE__ */ new WeakSet();
18502
- function helper(val, depth) {
18503
- if (depth > maxDepth) {
18504
- return "[MaxDepth]";
18505
- }
18506
- if (val === null || val === void 0) {
18507
- return val;
18508
- }
18509
- if (typeof val === "string") {
18510
- return truncateString(val, maxStringLength);
18511
- }
18512
- if (typeof val === "number" || typeof val === "boolean") {
18513
- return val;
18514
- }
18515
- if (typeof val === "bigint") {
18516
- return `${val}n`;
18517
- }
18518
- if (typeof val === "function") {
18519
- return "[Function]";
18520
- }
18521
- if (typeof val === "symbol") {
18522
- return val.description ? `[Symbol(${val.description})]` : "[Symbol]";
18523
- }
18524
- if (val instanceof Date) {
18525
- return val;
18526
- }
18527
- if (val instanceof Error) {
18528
- return {
18529
- name: val.name,
18530
- message: val.message ? truncateString(val.message, maxStringLength) : void 0
18531
- };
18532
- }
18533
- if (typeof val === "object") {
18534
- if (ancestors.has(val)) {
18535
- return "[Circular]";
18536
- }
18537
- ancestors.add(val);
18538
- }
18539
- try {
18540
- if (Array.isArray(val)) {
18541
- const cleaned2 = [];
18542
- for (let i = 0; i < Math.min(val.length, maxArrayLength); i++) {
18543
- try {
18544
- cleaned2.push(helper(val[i], depth + 1));
18545
- } catch (error48) {
18546
- cleaned2.push(`[${error48 instanceof Error ? truncateString(error48.message, 256) : "unknown error"}]`);
18547
- }
18548
- }
18549
- if (val.length > maxArrayLength) {
18550
- cleaned2.push(`[\u2026${val.length - maxArrayLength} more items]`);
18551
- }
18552
- return cleaned2;
18553
- }
18554
- if (typeof Buffer !== "undefined" && Buffer.isBuffer(val)) {
18555
- return `[Buffer length=${val.length}]`;
18556
- }
18557
- if (ArrayBuffer.isView(val)) {
18558
- const ctor = val.constructor?.name ?? "TypedArray";
18559
- const byteLength = val.byteLength ?? "?";
18560
- return `[${ctor} byteLength=${byteLength}]`;
18561
- }
18562
- if (val instanceof ArrayBuffer) {
18563
- return `[ArrayBuffer byteLength=${val.byteLength}]`;
18564
- }
18565
- let serializeForSpan;
18566
- try {
18567
- serializeForSpan = val.serializeForSpan;
18568
- } catch (error48) {
18569
- return `[serializeForSpan failed: ${error48 instanceof Error ? truncateString(error48.message, 256) : "unknown error"}]`;
18570
- }
18571
- if (typeof serializeForSpan === "function") {
18572
- try {
18573
- return helper(serializeForSpan.call(val), depth);
18574
- } catch (error48) {
18575
- return `[serializeForSpan failed: ${error48 instanceof Error ? truncateString(error48.message, 256) : "unknown error"}]`;
18576
- }
18577
- }
18578
- let looksLikeJsonSchema = false;
18579
- try {
18580
- looksLikeJsonSchema = isJsonSchema(val);
18581
- } catch {
18582
- looksLikeJsonSchema = false;
18583
- }
18584
- if (looksLikeJsonSchema) {
18585
- try {
18586
- const compressed = compressJsonSchema(val);
18587
- return compressed === val ? "[JSONSchema]" : helper(compressed, depth);
18588
- } catch {
18589
- }
18590
- }
18591
- const cleaned = {};
18592
- const keys = Object.keys(val).filter((key) => !stripSet.has(key));
18593
- let keyCount = 0;
18594
- for (const key of keys) {
18595
- if (keyCount >= maxObjectKeys) {
18596
- cleaned["__truncated"] = `${keys.length - keyCount} more keys omitted`;
18597
- break;
18598
- }
18599
- try {
18600
- cleaned[key] = helper(val[key], depth + 1);
18601
- keyCount++;
18602
- } catch (error48) {
18603
- cleaned[key] = `[${error48 instanceof Error ? truncateString(error48.message, 256) : "unknown error"}]`;
18604
- keyCount++;
18605
- }
18606
- }
18607
- return cleaned;
18608
- } finally {
18609
- if (typeof val === "object" && val !== null) {
18610
- ancestors.delete(val);
18611
- }
18612
- }
18613
- }
18614
- return helper(value, 0);
18615
- }
18616
-
18617
19054
  // src/spans/base.ts
18618
19055
  function isSpanInternal(spanType, flags) {
18619
19056
  if (flags === void 0 || flags === observability.InternalSpans.NONE) {
@@ -19067,12 +19504,16 @@ var BaseObservabilityInstance = class extends base.MastraBase {
19067
19504
  spanOutputProcessors: config2.spanOutputProcessors ?? [],
19068
19505
  bridge: config2.bridge ?? void 0,
19069
19506
  includeInternalSpans: config2.includeInternalSpans ?? false,
19507
+ excludeSpanTypes: config2.excludeSpanTypes,
19508
+ spanFilter: config2.spanFilter,
19070
19509
  requestContextKeys: config2.requestContextKeys ?? [],
19071
19510
  serializationOptions: config2.serializationOptions,
19072
19511
  logging: config2.logging
19073
19512
  };
19074
19513
  this.cardinalityFilter = new CardinalityFilter(config2.cardinality);
19075
- this.observabilityBus = new ObservabilityBus();
19514
+ this.observabilityBus = new ObservabilityBus({
19515
+ serializationOptions: this.config.serializationOptions
19516
+ });
19076
19517
  for (const exporter of this.exporters) {
19077
19518
  this.observabilityBus.registerExporter(exporter);
19078
19519
  }
@@ -19450,8 +19891,18 @@ var BaseObservabilityInstance = class extends base.MastraBase {
19450
19891
  getSpanForExport(span) {
19451
19892
  if (!span.isValid) return void 0;
19452
19893
  if (span.isInternal && !this.config.includeInternalSpans) return void 0;
19894
+ if (this.config.excludeSpanTypes?.includes(span.type)) return void 0;
19453
19895
  const processedSpan = this.processSpan(span);
19454
- return processedSpan?.exportSpan(this.config.includeInternalSpans);
19896
+ const exportedSpan = processedSpan?.exportSpan(this.config.includeInternalSpans);
19897
+ if (!exportedSpan) return void 0;
19898
+ if (this.config.spanFilter) {
19899
+ try {
19900
+ if (!this.config.spanFilter(exportedSpan)) return void 0;
19901
+ } catch (error48) {
19902
+ this.logger.error(`[Observability] spanFilter error`, error48);
19903
+ }
19904
+ }
19905
+ return exportedSpan;
19455
19906
  }
19456
19907
  /**
19457
19908
  * Emit a span started event.
@@ -20433,10 +20884,12 @@ exports.buildTracingOptions = buildTracingOptions;
20433
20884
  exports.chainFormatters = chainFormatters;
20434
20885
  exports.deepClean = deepClean;
20435
20886
  exports.getExternalParentId = getExternalParentId;
20887
+ exports.isSerializedMap = isSerializedMap;
20436
20888
  exports.mergeSerializationOptions = mergeSerializationOptions;
20437
20889
  exports.observabilityConfigValueSchema = observabilityConfigValueSchema;
20438
20890
  exports.observabilityInstanceConfigSchema = observabilityInstanceConfigSchema;
20439
20891
  exports.observabilityRegistryConfigSchema = observabilityRegistryConfigSchema;
20892
+ exports.reconstructSerializedMap = reconstructSerializedMap;
20440
20893
  exports.routeToHandler = routeToHandler;
20441
20894
  exports.samplingStrategySchema = samplingStrategySchema;
20442
20895
  exports.serializationOptionsSchema = serializationOptionsSchema;