@mastra/observability 0.0.0-span-scorring-test-20251124132129 → 0.0.0-top-level-fix-20251211111608

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
@@ -37,18 +37,38 @@ var observabilityInstanceConfigSchema = zod.z.object({
37
37
  serviceName: zod.z.string().min(1, "Service name is required"),
38
38
  sampling: samplingStrategySchema.optional(),
39
39
  exporters: zod.z.array(zod.z.any()).optional(),
40
+ bridge: zod.z.any().optional(),
40
41
  spanOutputProcessors: zod.z.array(zod.z.any()).optional(),
41
42
  includeInternalSpans: zod.z.boolean().optional(),
42
43
  requestContextKeys: zod.z.array(zod.z.string()).optional()
43
- });
44
+ }).refine(
45
+ (data) => {
46
+ const hasExporters = data.exporters && data.exporters.length > 0;
47
+ const hasBridge = !!data.bridge;
48
+ return hasExporters || hasBridge;
49
+ },
50
+ {
51
+ message: "At least one exporter or a bridge is required"
52
+ }
53
+ );
44
54
  var observabilityConfigValueSchema = zod.z.object({
45
55
  serviceName: zod.z.string().min(1, "Service name is required"),
46
56
  sampling: samplingStrategySchema.optional(),
47
57
  exporters: zod.z.array(zod.z.any()).optional(),
58
+ bridge: zod.z.any().optional(),
48
59
  spanOutputProcessors: zod.z.array(zod.z.any()).optional(),
49
60
  includeInternalSpans: zod.z.boolean().optional(),
50
61
  requestContextKeys: zod.z.array(zod.z.string()).optional()
51
- });
62
+ }).refine(
63
+ (data) => {
64
+ const hasExporters = data.exporters && data.exporters.length > 0;
65
+ const hasBridge = !!data.bridge;
66
+ return hasExporters || hasBridge;
67
+ },
68
+ {
69
+ message: "At least one exporter or a bridge is required"
70
+ }
71
+ );
52
72
  var observabilityRegistryConfigSchema = zod.z.object({
53
73
  default: zod.z.object({
54
74
  enabled: zod.z.boolean().optional()
@@ -75,6 +95,18 @@ var observabilityRegistryConfigSchema = zod.z.object({
75
95
  {
76
96
  message: 'A "configSelector" function is required when multiple configs are specified to determine which config to use.'
77
97
  }
98
+ ).refine(
99
+ (data) => {
100
+ if (data.configSelector) {
101
+ const isDefaultEnabled = data.default?.enabled === true;
102
+ const hasConfigs = data.configs && typeof data.configs === "object" && !Array.isArray(data.configs) ? Object.keys(data.configs).length > 0 : false;
103
+ return isDefaultEnabled || hasConfigs;
104
+ }
105
+ return true;
106
+ },
107
+ {
108
+ message: 'A "configSelector" requires at least one config or default observability to be configured.'
109
+ }
78
110
  );
79
111
  var BaseExporter = class {
80
112
  /** Mastra logger instance */
@@ -895,6 +927,79 @@ var DefaultExporter = class extends BaseExporter {
895
927
  this.logger.info("DefaultExporter shutdown complete");
896
928
  }
897
929
  };
930
+
931
+ // src/exporters/test.ts
932
+ var TestExporter = class extends BaseExporter {
933
+ name = "tracing-test-exporter";
934
+ #events = [];
935
+ constructor(config = {}) {
936
+ super(config);
937
+ }
938
+ async _exportTracingEvent(event) {
939
+ this.#events.push(event);
940
+ }
941
+ clearEvents() {
942
+ this.#events = [];
943
+ }
944
+ get events() {
945
+ return this.#events;
946
+ }
947
+ async shutdown() {
948
+ this.logger.info("TestExporter shutdown");
949
+ }
950
+ };
951
+
952
+ // src/usage.ts
953
+ function extractUsageMetrics(usage, providerMetadata) {
954
+ if (!usage) {
955
+ return {};
956
+ }
957
+ const inputDetails = {};
958
+ const outputDetails = {};
959
+ let inputTokens = usage.inputTokens;
960
+ const outputTokens = usage.outputTokens;
961
+ if (usage.cachedInputTokens) {
962
+ inputDetails.cacheRead = usage.cachedInputTokens;
963
+ }
964
+ if (usage.reasoningTokens) {
965
+ outputDetails.reasoning = usage.reasoningTokens;
966
+ }
967
+ const anthropic = providerMetadata?.anthropic;
968
+ if (anthropic) {
969
+ if (anthropic.cacheReadInputTokens) {
970
+ inputDetails.cacheRead = anthropic.cacheReadInputTokens;
971
+ }
972
+ if (anthropic.cacheCreationInputTokens) {
973
+ inputDetails.cacheWrite = anthropic.cacheCreationInputTokens;
974
+ }
975
+ if (anthropic.cacheReadInputTokens || anthropic.cacheCreationInputTokens) {
976
+ inputDetails.text = usage.inputTokens;
977
+ inputTokens = (usage.inputTokens ?? 0) + (anthropic.cacheReadInputTokens ?? 0) + (anthropic.cacheCreationInputTokens ?? 0);
978
+ }
979
+ }
980
+ const google = providerMetadata?.google;
981
+ if (google?.usageMetadata) {
982
+ if (google.usageMetadata.cachedContentTokenCount) {
983
+ inputDetails.cacheRead = google.usageMetadata.cachedContentTokenCount;
984
+ }
985
+ if (google.usageMetadata.thoughtsTokenCount) {
986
+ outputDetails.reasoning = google.usageMetadata.thoughtsTokenCount;
987
+ }
988
+ }
989
+ const result = {
990
+ inputTokens,
991
+ outputTokens
992
+ };
993
+ if (Object.keys(inputDetails).length > 0) {
994
+ result.inputDetails = inputDetails;
995
+ }
996
+ if (Object.keys(outputDetails).length > 0) {
997
+ result.outputDetails = outputDetails;
998
+ }
999
+ return result;
1000
+ }
1001
+
1002
+ // src/model-tracing.ts
898
1003
  var ModelSpanTracker = class {
899
1004
  #modelSpan;
900
1005
  #currentStepSpan;
@@ -902,9 +1007,23 @@ var ModelSpanTracker = class {
902
1007
  #accumulator = {};
903
1008
  #stepIndex = 0;
904
1009
  #chunkSequence = 0;
1010
+ #completionStartTime;
1011
+ /** Tracks tool output accumulators by toolCallId for consolidating sub-agent streams */
1012
+ #toolOutputAccumulators = /* @__PURE__ */ new Map();
1013
+ /** Tracks toolCallIds that had streaming output (to skip redundant tool-result spans) */
1014
+ #streamedToolCallIds = /* @__PURE__ */ new Set();
905
1015
  constructor(modelSpan) {
906
1016
  this.#modelSpan = modelSpan;
907
1017
  }
1018
+ /**
1019
+ * Capture the completion start time (time to first token) when the first content chunk arrives.
1020
+ */
1021
+ #captureCompletionStartTime() {
1022
+ if (this.#completionStartTime) {
1023
+ return;
1024
+ }
1025
+ this.#completionStartTime = /* @__PURE__ */ new Date();
1026
+ }
908
1027
  /**
909
1028
  * Get the tracing context for creating child spans.
910
1029
  * Returns the current step span if active, otherwise the model span.
@@ -921,10 +1040,16 @@ var ModelSpanTracker = class {
921
1040
  this.#modelSpan?.error(options);
922
1041
  }
923
1042
  /**
924
- * End the generation span
1043
+ * End the generation span with optional raw usage data.
1044
+ * If usage is provided, it will be converted to UsageStats with cache token details.
925
1045
  */
926
1046
  endGeneration(options) {
927
- this.#modelSpan?.end(options);
1047
+ const { usage, providerMetadata, ...spanOptions } = options ?? {};
1048
+ if (spanOptions.attributes) {
1049
+ spanOptions.attributes.completionStartTime = this.#completionStartTime;
1050
+ spanOptions.attributes.usage = extractUsageMetrics(usage, providerMetadata);
1051
+ }
1052
+ this.#modelSpan?.end(spanOptions);
928
1053
  }
929
1054
  /**
930
1055
  * Update the generation span
@@ -954,9 +1079,10 @@ var ModelSpanTracker = class {
954
1079
  #endStepSpan(payload) {
955
1080
  if (!this.#currentStepSpan) return;
956
1081
  const output = payload.output;
957
- const { usage, ...otherOutput } = output;
1082
+ const { usage: rawUsage, ...otherOutput } = output;
958
1083
  const stepResult = payload.stepResult;
959
1084
  const metadata = payload.metadata;
1085
+ const usage = extractUsageMetrics(rawUsage, metadata?.providerMetadata);
960
1086
  const cleanMetadata = metadata ? { ...metadata } : void 0;
961
1087
  if (cleanMetadata?.request) {
962
1088
  delete cleanMetadata.request;
@@ -1129,6 +1255,77 @@ var ModelSpanTracker = class {
1129
1255
  break;
1130
1256
  }
1131
1257
  }
1258
+ /**
1259
+ * Handle tool-output chunks from sub-agents.
1260
+ * Consolidates streaming text/reasoning deltas into a single span per tool call.
1261
+ */
1262
+ #handleToolOutputChunk(chunk) {
1263
+ if (chunk.type !== "tool-output") return;
1264
+ const payload = chunk.payload;
1265
+ const { output, toolCallId, toolName } = payload;
1266
+ let acc = this.#toolOutputAccumulators.get(toolCallId);
1267
+ if (!acc) {
1268
+ if (!this.#currentStepSpan) {
1269
+ this.#startStepSpan();
1270
+ }
1271
+ acc = {
1272
+ toolName: toolName || "unknown",
1273
+ toolCallId,
1274
+ text: "",
1275
+ reasoning: "",
1276
+ sequenceNumber: this.#chunkSequence++,
1277
+ // Name the span 'tool-result' for consistency (tool-call → tool-result)
1278
+ span: this.#currentStepSpan?.createChildSpan({
1279
+ name: `chunk: 'tool-result'`,
1280
+ type: observability.SpanType.MODEL_CHUNK,
1281
+ attributes: {
1282
+ chunkType: "tool-result",
1283
+ sequenceNumber: this.#chunkSequence - 1
1284
+ }
1285
+ })
1286
+ };
1287
+ this.#toolOutputAccumulators.set(toolCallId, acc);
1288
+ }
1289
+ if (output && typeof output === "object" && "type" in output) {
1290
+ const innerType = output.type;
1291
+ switch (innerType) {
1292
+ case "text-delta":
1293
+ if (output.payload?.text) {
1294
+ acc.text += output.payload.text;
1295
+ }
1296
+ break;
1297
+ case "reasoning-delta":
1298
+ if (output.payload?.text) {
1299
+ acc.reasoning += output.payload.text;
1300
+ }
1301
+ break;
1302
+ case "finish":
1303
+ case "workflow-finish":
1304
+ this.#endToolOutputSpan(toolCallId);
1305
+ break;
1306
+ }
1307
+ }
1308
+ }
1309
+ /**
1310
+ * End a tool output span and clean up the accumulator
1311
+ */
1312
+ #endToolOutputSpan(toolCallId) {
1313
+ const acc = this.#toolOutputAccumulators.get(toolCallId);
1314
+ if (!acc) return;
1315
+ const output = {
1316
+ toolCallId: acc.toolCallId,
1317
+ toolName: acc.toolName
1318
+ };
1319
+ if (acc.text) {
1320
+ output.text = acc.text;
1321
+ }
1322
+ if (acc.reasoning) {
1323
+ output.reasoning = acc.reasoning;
1324
+ }
1325
+ acc.span?.end({ output });
1326
+ this.#toolOutputAccumulators.delete(toolCallId);
1327
+ this.#streamedToolCallIds.add(toolCallId);
1328
+ }
1132
1329
  /**
1133
1330
  * Wraps a stream with model tracing transform to track MODEL_STEP and MODEL_CHUNK spans.
1134
1331
  *
@@ -1139,6 +1336,13 @@ var ModelSpanTracker = class {
1139
1336
  return stream.pipeThrough(
1140
1337
  new web.TransformStream({
1141
1338
  transform: (chunk, controller) => {
1339
+ switch (chunk.type) {
1340
+ case "text-delta":
1341
+ case "tool-call-delta":
1342
+ case "reasoning-delta":
1343
+ this.#captureCompletionStartTime();
1344
+ break;
1345
+ }
1142
1346
  controller.enqueue(chunk);
1143
1347
  switch (chunk.type) {
1144
1348
  case "text-start":
@@ -1172,6 +1376,19 @@ var ModelSpanTracker = class {
1172
1376
  case "start":
1173
1377
  case "finish":
1174
1378
  break;
1379
+ case "tool-output":
1380
+ this.#handleToolOutputChunk(chunk);
1381
+ break;
1382
+ case "tool-result": {
1383
+ const toolCallId = chunk.payload?.toolCallId;
1384
+ if (toolCallId && this.#streamedToolCallIds.has(toolCallId)) {
1385
+ this.#streamedToolCallIds.delete(toolCallId);
1386
+ break;
1387
+ }
1388
+ const { args, ...cleanPayload } = chunk.payload || {};
1389
+ this.#createEventSpan(chunk.type, cleanPayload);
1390
+ break;
1391
+ }
1175
1392
  // Default: auto-create event span for all other chunk types
1176
1393
  default: {
1177
1394
  let outputPayload = chunk.payload;
@@ -1226,6 +1443,16 @@ function isSpanInternal(spanType, flags) {
1226
1443
  return false;
1227
1444
  }
1228
1445
  }
1446
+ function getExternalParentId(options) {
1447
+ if (!options.parent) {
1448
+ return void 0;
1449
+ }
1450
+ if (options.parent.isInternal) {
1451
+ return options.parent.getParentSpanId(false);
1452
+ } else {
1453
+ return options.parent.id;
1454
+ }
1455
+ }
1229
1456
  var BaseSpan = class {
1230
1457
  name;
1231
1458
  type;
@@ -1240,6 +1467,7 @@ var BaseSpan = class {
1240
1467
  output;
1241
1468
  errorInfo;
1242
1469
  metadata;
1470
+ tags;
1243
1471
  traceState;
1244
1472
  /** Parent span ID (for root spans that are children of external spans) */
1245
1473
  parentSpanId;
@@ -1254,6 +1482,7 @@ var BaseSpan = class {
1254
1482
  this.isEvent = options.isEvent ?? false;
1255
1483
  this.isInternal = isSpanInternal(this.type, options.tracingPolicy?.internal);
1256
1484
  this.traceState = options.traceState;
1485
+ this.tags = !options.parent && options.tags?.length ? options.tags : void 0;
1257
1486
  if (this.isEvent) {
1258
1487
  this.output = deepClean(options.output);
1259
1488
  } else {
@@ -1316,12 +1545,36 @@ var BaseSpan = class {
1316
1545
  errorInfo: this.errorInfo,
1317
1546
  isEvent: this.isEvent,
1318
1547
  isRootSpan: this.isRootSpan,
1319
- parentSpanId: this.getParentSpanId(includeInternalSpans)
1548
+ parentSpanId: this.getParentSpanId(includeInternalSpans),
1549
+ // Tags are only included for root spans
1550
+ ...this.isRootSpan && this.tags?.length ? { tags: this.tags } : {}
1320
1551
  };
1321
1552
  }
1322
1553
  get externalTraceId() {
1323
1554
  return this.isValid ? this.traceId : void 0;
1324
1555
  }
1556
+ /**
1557
+ * Execute an async function within this span's tracing context.
1558
+ * Delegates to the bridge if available.
1559
+ */
1560
+ async executeInContext(fn) {
1561
+ const bridge = this.observabilityInstance.getBridge();
1562
+ if (bridge?.executeInContext) {
1563
+ return bridge.executeInContext(this.id, fn);
1564
+ }
1565
+ return fn();
1566
+ }
1567
+ /**
1568
+ * Execute a synchronous function within this span's tracing context.
1569
+ * Delegates to the bridge if available.
1570
+ */
1571
+ executeInContextSync(fn) {
1572
+ const bridge = this.observabilityInstance.getBridge();
1573
+ if (bridge?.executeInContextSync) {
1574
+ return bridge.executeInContextSync(this.id, fn);
1575
+ }
1576
+ return fn();
1577
+ }
1325
1578
  };
1326
1579
  var DEFAULT_KEYS_TO_STRIP = /* @__PURE__ */ new Set([
1327
1580
  "logger",
@@ -1347,6 +1600,9 @@ function deepClean(value, options = {}, _seen = /* @__PURE__ */ new WeakSet(), _
1347
1600
  return "[Circular]";
1348
1601
  }
1349
1602
  _seen.add(value);
1603
+ if (value instanceof Date) {
1604
+ return value;
1605
+ }
1350
1606
  if (Array.isArray(value)) {
1351
1607
  return value.map((item) => deepClean(item, options, _seen, _depth + 1));
1352
1608
  }
@@ -1368,27 +1624,30 @@ var DefaultSpan = class extends BaseSpan {
1368
1624
  traceId;
1369
1625
  constructor(options, observabilityInstance) {
1370
1626
  super(options, observabilityInstance);
1371
- this.id = generateSpanId();
1627
+ const bridge = observabilityInstance.getBridge();
1628
+ if (bridge && !this.isInternal) {
1629
+ const bridgeIds = bridge.createSpan(options);
1630
+ if (bridgeIds) {
1631
+ this.id = bridgeIds.spanId;
1632
+ this.traceId = bridgeIds.traceId;
1633
+ this.parentSpanId = bridgeIds.parentSpanId;
1634
+ return;
1635
+ }
1636
+ }
1372
1637
  if (options.parent) {
1373
1638
  this.traceId = options.parent.traceId;
1374
- } else if (options.traceId) {
1375
- if (isValidTraceId(options.traceId)) {
1376
- this.traceId = options.traceId;
1377
- } else {
1378
- console.error(
1379
- `[Mastra Tracing] Invalid traceId: must be 1-32 hexadecimal characters, got "${options.traceId}". Generating new trace ID.`
1380
- );
1381
- this.traceId = generateTraceId();
1382
- }
1383
- } else {
1384
- this.traceId = generateTraceId();
1639
+ this.parentSpanId = options.parent.id;
1640
+ this.id = generateSpanId();
1641
+ return;
1385
1642
  }
1386
- if (!options.parent && options.parentSpanId) {
1643
+ this.traceId = getOrCreateTraceId(options);
1644
+ this.id = generateSpanId();
1645
+ if (options.parentSpanId) {
1387
1646
  if (isValidSpanId(options.parentSpanId)) {
1388
1647
  this.parentSpanId = options.parentSpanId;
1389
1648
  } else {
1390
1649
  console.error(
1391
- `[Mastra Tracing] Invalid parentSpanId: must be 1-16 hexadecimal characters, got "${options.parentSpanId}". Ignoring parent span ID.`
1650
+ `[Mastra Tracing] Invalid parentSpanId: must be 1-16 hexadecimal characters, got "${options.parentSpanId}". Ignoring.`
1392
1651
  );
1393
1652
  }
1394
1653
  }
@@ -1493,6 +1752,18 @@ function isValidTraceId(traceId) {
1493
1752
  function isValidSpanId(spanId) {
1494
1753
  return /^[0-9a-f]{1,16}$/i.test(spanId);
1495
1754
  }
1755
+ function getOrCreateTraceId(options) {
1756
+ if (options.traceId) {
1757
+ if (isValidTraceId(options.traceId)) {
1758
+ return options.traceId;
1759
+ } else {
1760
+ console.error(
1761
+ `[Mastra Tracing] Invalid traceId: must be 1-32 hexadecimal characters, got "${options.traceId}". Generating new trace ID.`
1762
+ );
1763
+ }
1764
+ }
1765
+ return generateTraceId();
1766
+ }
1496
1767
 
1497
1768
  // src/spans/no-op.ts
1498
1769
  var NoOpSpan = class extends BaseSpan {
@@ -1525,13 +1796,17 @@ var BaseObservabilityInstance = class extends base.MastraBase {
1525
1796
  sampling: config.sampling ?? { type: "always" /* ALWAYS */ },
1526
1797
  exporters: config.exporters ?? [],
1527
1798
  spanOutputProcessors: config.spanOutputProcessors ?? [],
1799
+ bridge: config.bridge ?? void 0,
1528
1800
  includeInternalSpans: config.includeInternalSpans ?? false,
1529
1801
  requestContextKeys: config.requestContextKeys ?? []
1530
1802
  };
1803
+ if (this.config.bridge?.init) {
1804
+ this.config.bridge.init({ config: this.config });
1805
+ }
1531
1806
  }
1532
1807
  /**
1533
1808
  * Override setLogger to add Observability specific initialization log
1534
- * and propagate logger to exporters
1809
+ * and propagate logger to exporters and bridge
1535
1810
  */
1536
1811
  __setLogger(logger) {
1537
1812
  super.__setLogger(logger);
@@ -1540,8 +1815,11 @@ var BaseObservabilityInstance = class extends base.MastraBase {
1540
1815
  exporter.__setLogger(logger);
1541
1816
  }
1542
1817
  });
1818
+ if (this.config.bridge?.__setLogger) {
1819
+ this.config.bridge.__setLogger(logger);
1820
+ }
1543
1821
  this.logger.debug(
1544
- `[Observability] Initialized [service=${this.config.serviceName}] [instance=${this.config.name}] [sampling=${this.config.sampling.type}]`
1822
+ `[Observability] Initialized [service=${this.config.serviceName}] [instance=${this.config.name}] [sampling=${this.config.sampling?.type}] [bridge=${!!this.config.bridge}]`
1545
1823
  );
1546
1824
  }
1547
1825
  // ============================================================================
@@ -1570,11 +1848,15 @@ var BaseObservabilityInstance = class extends base.MastraBase {
1570
1848
  } else {
1571
1849
  traceState = this.computeTraceState(tracingOptions);
1572
1850
  }
1573
- const enrichedMetadata = this.extractMetadataFromRequestContext(requestContext, metadata, traceState);
1851
+ const tracingMetadata = !options.parent ? tracingOptions?.metadata : void 0;
1852
+ const mergedMetadata = metadata || tracingMetadata ? { ...metadata, ...tracingMetadata } : void 0;
1853
+ const enrichedMetadata = this.extractMetadataFromRequestContext(requestContext, mergedMetadata, traceState);
1854
+ const tags = !options.parent ? tracingOptions?.tags : void 0;
1574
1855
  const span = this.createSpan({
1575
1856
  ...rest,
1576
1857
  metadata: enrichedMetadata,
1577
- traceState
1858
+ traceState,
1859
+ tags
1578
1860
  });
1579
1861
  if (span.isEvent) {
1580
1862
  this.emitSpanEnded(span);
@@ -1608,6 +1890,12 @@ var BaseObservabilityInstance = class extends base.MastraBase {
1608
1890
  getSpanOutputProcessors() {
1609
1891
  return [...this.spanOutputProcessors];
1610
1892
  }
1893
+ /**
1894
+ * Get the bridge instance if configured
1895
+ */
1896
+ getBridge() {
1897
+ return this.config.bridge;
1898
+ }
1611
1899
  /**
1612
1900
  * Get the logger instance (for exporters and other components)
1613
1901
  */
@@ -1652,7 +1940,9 @@ var BaseObservabilityInstance = class extends base.MastraBase {
1652
1940
  */
1653
1941
  shouldSample(options) {
1654
1942
  const { sampling } = this.config;
1655
- switch (sampling.type) {
1943
+ switch (sampling?.type) {
1944
+ case void 0:
1945
+ return true;
1656
1946
  case "always" /* ALWAYS */:
1657
1947
  return true;
1658
1948
  case "never" /* NEVER */:
@@ -1784,17 +2074,21 @@ var BaseObservabilityInstance = class extends base.MastraBase {
1784
2074
  }
1785
2075
  }
1786
2076
  /**
1787
- * Export tracing event through all exporters (realtime mode)
2077
+ * Export tracing event through all exporters and bridge (realtime mode)
1788
2078
  */
1789
2079
  async exportTracingEvent(event) {
1790
- const exportPromises = this.exporters.map(async (exporter) => {
2080
+ const targets = [
2081
+ ...this.exporters
2082
+ ];
2083
+ if (this.config.bridge) {
2084
+ targets.push(this.config.bridge);
2085
+ }
2086
+ const exportPromises = targets.map(async (target) => {
1791
2087
  try {
1792
- if (exporter.exportTracingEvent) {
1793
- await exporter.exportTracingEvent(event);
1794
- this.logger.debug(`[Observability] Event exported [exporter=${exporter.name}] [type=${event.type}]`);
1795
- }
2088
+ await target.exportTracingEvent(event);
2089
+ this.logger.debug(`[Observability] Event exported [target=${target.name}] [type=${event.type}]`);
1796
2090
  } catch (error) {
1797
- this.logger.error(`[Observability] Export error [exporter=${exporter.name}]`, error);
2091
+ this.logger.error(`[Observability] Export error [target=${target.name}]`, error);
1798
2092
  }
1799
2093
  });
1800
2094
  await Promise.allSettled(exportPromises);
@@ -1818,6 +2112,9 @@ var BaseObservabilityInstance = class extends base.MastraBase {
1818
2112
  ...this.exporters.map((e) => e.shutdown()),
1819
2113
  ...this.spanOutputProcessors.map((p) => p.shutdown())
1820
2114
  ];
2115
+ if (this.config.bridge) {
2116
+ shutdownPromises.push(this.config.bridge.shutdown());
2117
+ }
1821
2118
  await Promise.allSettled(shutdownPromises);
1822
2119
  this.logger.info(`[Observability] Shutdown completed [name=${this.name}]`);
1823
2120
  }
@@ -1964,9 +2261,16 @@ var SensitiveDataFilter = class {
1964
2261
  /**
1965
2262
  * Recursively filter objects/arrays for sensitive keys.
1966
2263
  * Handles circular references by replacing with a marker.
2264
+ * Also attempts to parse and redact JSON strings.
1967
2265
  */
1968
2266
  deepFilter(obj, seen = /* @__PURE__ */ new WeakSet()) {
1969
2267
  if (obj === null || typeof obj !== "object") {
2268
+ if (typeof obj === "string") {
2269
+ const trimmed = obj.trim();
2270
+ if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
2271
+ return this.redactJsonString(obj);
2272
+ }
2273
+ }
1970
2274
  return obj;
1971
2275
  }
1972
2276
  if (seen.has(obj)) {
@@ -2019,6 +2323,22 @@ var SensitiveDataFilter = class {
2019
2323
  return normalizedKey === sensitiveField;
2020
2324
  });
2021
2325
  }
2326
+ /**
2327
+ * Attempt to parse a string as JSON and redact sensitive fields within it.
2328
+ * If parsing fails or no sensitive data is found, returns the original string.
2329
+ */
2330
+ redactJsonString(str) {
2331
+ try {
2332
+ const parsed = JSON.parse(str);
2333
+ if (parsed && typeof parsed === "object") {
2334
+ const filtered = this.deepFilter(parsed, /* @__PURE__ */ new WeakSet());
2335
+ return JSON.stringify(filtered);
2336
+ }
2337
+ return str;
2338
+ } catch {
2339
+ return str;
2340
+ }
2341
+ }
2022
2342
  /**
2023
2343
  * Redact a sensitive value.
2024
2344
  * - Full style: replaces with a fixed token.
@@ -2171,6 +2491,11 @@ var Observability = class extends base.MastraBase {
2171
2491
  }
2172
2492
  };
2173
2493
 
2494
+ // src/tracing-options.ts
2495
+ function buildTracingOptions(...updaters) {
2496
+ return updaters.reduce((opts, updater) => updater(opts), {});
2497
+ }
2498
+
2174
2499
  exports.BaseExporter = BaseExporter;
2175
2500
  exports.BaseObservabilityInstance = BaseObservabilityInstance;
2176
2501
  exports.BaseSpan = BaseSpan;
@@ -2184,7 +2509,10 @@ exports.NoOpSpan = NoOpSpan;
2184
2509
  exports.Observability = Observability;
2185
2510
  exports.SamplingStrategyType = SamplingStrategyType;
2186
2511
  exports.SensitiveDataFilter = SensitiveDataFilter;
2512
+ exports.TestExporter = TestExporter;
2513
+ exports.buildTracingOptions = buildTracingOptions;
2187
2514
  exports.deepClean = deepClean;
2515
+ exports.getExternalParentId = getExternalParentId;
2188
2516
  exports.observabilityConfigValueSchema = observabilityConfigValueSchema;
2189
2517
  exports.observabilityInstanceConfigSchema = observabilityInstanceConfigSchema;
2190
2518
  exports.observabilityRegistryConfigSchema = observabilityRegistryConfigSchema;