@mastra/observability 1.0.0-beta.1 → 1.0.0-beta.3

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.d.ts CHANGED
@@ -11,4 +11,5 @@ export * from './spans/index.js';
11
11
  export * from './exporters/index.js';
12
12
  export * from './span_processors/index.js';
13
13
  export * from './model-tracing.js';
14
+ export * from './tracing-options.js';
14
15
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAG1C,cAAc,UAAU,CAAC;AAGzB,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC;AACxB,cAAc,aAAa,CAAC;AAC5B,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAG1C,cAAc,UAAU,CAAC;AAGzB,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC;AACxB,cAAc,aAAa,CAAC;AAC5B,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAGhC,cAAc,mBAAmB,CAAC"}
package/dist/index.js CHANGED
@@ -35,18 +35,38 @@ var observabilityInstanceConfigSchema = z.object({
35
35
  serviceName: z.string().min(1, "Service name is required"),
36
36
  sampling: samplingStrategySchema.optional(),
37
37
  exporters: z.array(z.any()).optional(),
38
+ bridge: z.any().optional(),
38
39
  spanOutputProcessors: z.array(z.any()).optional(),
39
40
  includeInternalSpans: z.boolean().optional(),
40
41
  requestContextKeys: z.array(z.string()).optional()
41
- });
42
+ }).refine(
43
+ (data) => {
44
+ const hasExporters = data.exporters && data.exporters.length > 0;
45
+ const hasBridge = !!data.bridge;
46
+ return hasExporters || hasBridge;
47
+ },
48
+ {
49
+ message: "At least one exporter or a bridge is required"
50
+ }
51
+ );
42
52
  var observabilityConfigValueSchema = z.object({
43
53
  serviceName: z.string().min(1, "Service name is required"),
44
54
  sampling: samplingStrategySchema.optional(),
45
55
  exporters: z.array(z.any()).optional(),
56
+ bridge: z.any().optional(),
46
57
  spanOutputProcessors: z.array(z.any()).optional(),
47
58
  includeInternalSpans: z.boolean().optional(),
48
59
  requestContextKeys: z.array(z.string()).optional()
49
- });
60
+ }).refine(
61
+ (data) => {
62
+ const hasExporters = data.exporters && data.exporters.length > 0;
63
+ const hasBridge = !!data.bridge;
64
+ return hasExporters || hasBridge;
65
+ },
66
+ {
67
+ message: "At least one exporter or a bridge is required"
68
+ }
69
+ );
50
70
  var observabilityRegistryConfigSchema = z.object({
51
71
  default: z.object({
52
72
  enabled: z.boolean().optional()
@@ -73,6 +93,18 @@ var observabilityRegistryConfigSchema = z.object({
73
93
  {
74
94
  message: 'A "configSelector" function is required when multiple configs are specified to determine which config to use.'
75
95
  }
96
+ ).refine(
97
+ (data) => {
98
+ if (data.configSelector) {
99
+ const isDefaultEnabled = data.default?.enabled === true;
100
+ const hasConfigs = data.configs && typeof data.configs === "object" && !Array.isArray(data.configs) ? Object.keys(data.configs).length > 0 : false;
101
+ return isDefaultEnabled || hasConfigs;
102
+ }
103
+ return true;
104
+ },
105
+ {
106
+ message: 'A "configSelector" requires at least one config or default observability to be configured.'
107
+ }
76
108
  );
77
109
  var BaseExporter = class {
78
110
  /** Mastra logger instance */
@@ -893,6 +925,27 @@ var DefaultExporter = class extends BaseExporter {
893
925
  this.logger.info("DefaultExporter shutdown complete");
894
926
  }
895
927
  };
928
+
929
+ // src/exporters/test.ts
930
+ var TestExporter = class extends BaseExporter {
931
+ name = "tracing-test-exporter";
932
+ #events = [];
933
+ constructor(config = {}) {
934
+ super(config);
935
+ }
936
+ async _exportTracingEvent(event) {
937
+ this.#events.push(event);
938
+ }
939
+ clearEvents() {
940
+ this.#events = [];
941
+ }
942
+ get events() {
943
+ return this.#events;
944
+ }
945
+ async shutdown() {
946
+ this.logger.info("TestExporter shutdown");
947
+ }
948
+ };
896
949
  var ModelSpanTracker = class {
897
950
  #modelSpan;
898
951
  #currentStepSpan;
@@ -900,9 +953,30 @@ var ModelSpanTracker = class {
900
953
  #accumulator = {};
901
954
  #stepIndex = 0;
902
955
  #chunkSequence = 0;
956
+ /** Tracks whether completionStartTime has been captured for this generation */
957
+ #completionStartTimeCaptured = false;
958
+ /** Tracks tool output accumulators by toolCallId for consolidating sub-agent streams */
959
+ #toolOutputAccumulators = /* @__PURE__ */ new Map();
960
+ /** Tracks toolCallIds that had streaming output (to skip redundant tool-result spans) */
961
+ #streamedToolCallIds = /* @__PURE__ */ new Set();
903
962
  constructor(modelSpan) {
904
963
  this.#modelSpan = modelSpan;
905
964
  }
965
+ /**
966
+ * Capture the completion start time (time to first token) when the first content chunk arrives.
967
+ * This is used by observability providers like Langfuse to calculate TTFT metrics.
968
+ */
969
+ #captureCompletionStartTime() {
970
+ if (this.#completionStartTimeCaptured || !this.#modelSpan) {
971
+ return;
972
+ }
973
+ this.#completionStartTimeCaptured = true;
974
+ this.#modelSpan.update({
975
+ attributes: {
976
+ completionStartTime: /* @__PURE__ */ new Date()
977
+ }
978
+ });
979
+ }
906
980
  /**
907
981
  * Get the tracing context for creating child spans.
908
982
  * Returns the current step span if active, otherwise the model span.
@@ -1127,6 +1201,77 @@ var ModelSpanTracker = class {
1127
1201
  break;
1128
1202
  }
1129
1203
  }
1204
+ /**
1205
+ * Handle tool-output chunks from sub-agents.
1206
+ * Consolidates streaming text/reasoning deltas into a single span per tool call.
1207
+ */
1208
+ #handleToolOutputChunk(chunk) {
1209
+ if (chunk.type !== "tool-output") return;
1210
+ const payload = chunk.payload;
1211
+ const { output, toolCallId, toolName } = payload;
1212
+ let acc = this.#toolOutputAccumulators.get(toolCallId);
1213
+ if (!acc) {
1214
+ if (!this.#currentStepSpan) {
1215
+ this.#startStepSpan();
1216
+ }
1217
+ acc = {
1218
+ toolName: toolName || "unknown",
1219
+ toolCallId,
1220
+ text: "",
1221
+ reasoning: "",
1222
+ sequenceNumber: this.#chunkSequence++,
1223
+ // Name the span 'tool-result' for consistency (tool-call → tool-result)
1224
+ span: this.#currentStepSpan?.createChildSpan({
1225
+ name: `chunk: 'tool-result'`,
1226
+ type: SpanType.MODEL_CHUNK,
1227
+ attributes: {
1228
+ chunkType: "tool-result",
1229
+ sequenceNumber: this.#chunkSequence - 1
1230
+ }
1231
+ })
1232
+ };
1233
+ this.#toolOutputAccumulators.set(toolCallId, acc);
1234
+ }
1235
+ if (output && typeof output === "object" && "type" in output) {
1236
+ const innerType = output.type;
1237
+ switch (innerType) {
1238
+ case "text-delta":
1239
+ if (output.payload?.text) {
1240
+ acc.text += output.payload.text;
1241
+ }
1242
+ break;
1243
+ case "reasoning-delta":
1244
+ if (output.payload?.text) {
1245
+ acc.reasoning += output.payload.text;
1246
+ }
1247
+ break;
1248
+ case "finish":
1249
+ case "workflow-finish":
1250
+ this.#endToolOutputSpan(toolCallId);
1251
+ break;
1252
+ }
1253
+ }
1254
+ }
1255
+ /**
1256
+ * End a tool output span and clean up the accumulator
1257
+ */
1258
+ #endToolOutputSpan(toolCallId) {
1259
+ const acc = this.#toolOutputAccumulators.get(toolCallId);
1260
+ if (!acc) return;
1261
+ const output = {
1262
+ toolCallId: acc.toolCallId,
1263
+ toolName: acc.toolName
1264
+ };
1265
+ if (acc.text) {
1266
+ output.text = acc.text;
1267
+ }
1268
+ if (acc.reasoning) {
1269
+ output.reasoning = acc.reasoning;
1270
+ }
1271
+ acc.span?.end({ output });
1272
+ this.#toolOutputAccumulators.delete(toolCallId);
1273
+ this.#streamedToolCallIds.add(toolCallId);
1274
+ }
1130
1275
  /**
1131
1276
  * Wraps a stream with model tracing transform to track MODEL_STEP and MODEL_CHUNK spans.
1132
1277
  *
@@ -1134,9 +1279,14 @@ var ModelSpanTracker = class {
1134
1279
  * create MODEL_STEP and MODEL_CHUNK spans for each semantic unit in the stream.
1135
1280
  */
1136
1281
  wrapStream(stream) {
1282
+ let captureCompletionStartTime = false;
1137
1283
  return stream.pipeThrough(
1138
1284
  new TransformStream({
1139
1285
  transform: (chunk, controller) => {
1286
+ if (!captureCompletionStartTime) {
1287
+ captureCompletionStartTime = true;
1288
+ this.#captureCompletionStartTime();
1289
+ }
1140
1290
  controller.enqueue(chunk);
1141
1291
  switch (chunk.type) {
1142
1292
  case "text-start":
@@ -1170,6 +1320,19 @@ var ModelSpanTracker = class {
1170
1320
  case "start":
1171
1321
  case "finish":
1172
1322
  break;
1323
+ case "tool-output":
1324
+ this.#handleToolOutputChunk(chunk);
1325
+ break;
1326
+ case "tool-result": {
1327
+ const toolCallId = chunk.payload?.toolCallId;
1328
+ if (toolCallId && this.#streamedToolCallIds.has(toolCallId)) {
1329
+ this.#streamedToolCallIds.delete(toolCallId);
1330
+ break;
1331
+ }
1332
+ const { args, ...cleanPayload } = chunk.payload || {};
1333
+ this.#createEventSpan(chunk.type, cleanPayload);
1334
+ break;
1335
+ }
1173
1336
  // Default: auto-create event span for all other chunk types
1174
1337
  default: {
1175
1338
  let outputPayload = chunk.payload;
@@ -1224,6 +1387,16 @@ function isSpanInternal(spanType, flags) {
1224
1387
  return false;
1225
1388
  }
1226
1389
  }
1390
+ function getExternalParentId(options) {
1391
+ if (!options.parent) {
1392
+ return void 0;
1393
+ }
1394
+ if (options.parent.isInternal) {
1395
+ return options.parent.getParentSpanId(false);
1396
+ } else {
1397
+ return options.parent.id;
1398
+ }
1399
+ }
1227
1400
  var BaseSpan = class {
1228
1401
  name;
1229
1402
  type;
@@ -1238,6 +1411,7 @@ var BaseSpan = class {
1238
1411
  output;
1239
1412
  errorInfo;
1240
1413
  metadata;
1414
+ tags;
1241
1415
  traceState;
1242
1416
  /** Parent span ID (for root spans that are children of external spans) */
1243
1417
  parentSpanId;
@@ -1252,6 +1426,7 @@ var BaseSpan = class {
1252
1426
  this.isEvent = options.isEvent ?? false;
1253
1427
  this.isInternal = isSpanInternal(this.type, options.tracingPolicy?.internal);
1254
1428
  this.traceState = options.traceState;
1429
+ this.tags = !options.parent && options.tags?.length ? options.tags : void 0;
1255
1430
  if (this.isEvent) {
1256
1431
  this.output = deepClean(options.output);
1257
1432
  } else {
@@ -1314,12 +1489,36 @@ var BaseSpan = class {
1314
1489
  errorInfo: this.errorInfo,
1315
1490
  isEvent: this.isEvent,
1316
1491
  isRootSpan: this.isRootSpan,
1317
- parentSpanId: this.getParentSpanId(includeInternalSpans)
1492
+ parentSpanId: this.getParentSpanId(includeInternalSpans),
1493
+ // Tags are only included for root spans
1494
+ ...this.isRootSpan && this.tags?.length ? { tags: this.tags } : {}
1318
1495
  };
1319
1496
  }
1320
1497
  get externalTraceId() {
1321
1498
  return this.isValid ? this.traceId : void 0;
1322
1499
  }
1500
+ /**
1501
+ * Execute an async function within this span's tracing context.
1502
+ * Delegates to the bridge if available.
1503
+ */
1504
+ async executeInContext(fn) {
1505
+ const bridge = this.observabilityInstance.getBridge();
1506
+ if (bridge?.executeInContext) {
1507
+ return bridge.executeInContext(this.id, fn);
1508
+ }
1509
+ return fn();
1510
+ }
1511
+ /**
1512
+ * Execute a synchronous function within this span's tracing context.
1513
+ * Delegates to the bridge if available.
1514
+ */
1515
+ executeInContextSync(fn) {
1516
+ const bridge = this.observabilityInstance.getBridge();
1517
+ if (bridge?.executeInContextSync) {
1518
+ return bridge.executeInContextSync(this.id, fn);
1519
+ }
1520
+ return fn();
1521
+ }
1323
1522
  };
1324
1523
  var DEFAULT_KEYS_TO_STRIP = /* @__PURE__ */ new Set([
1325
1524
  "logger",
@@ -1366,27 +1565,30 @@ var DefaultSpan = class extends BaseSpan {
1366
1565
  traceId;
1367
1566
  constructor(options, observabilityInstance) {
1368
1567
  super(options, observabilityInstance);
1369
- this.id = generateSpanId();
1568
+ const bridge = observabilityInstance.getBridge();
1569
+ if (bridge && !this.isInternal) {
1570
+ const bridgeIds = bridge.createSpan(options);
1571
+ if (bridgeIds) {
1572
+ this.id = bridgeIds.spanId;
1573
+ this.traceId = bridgeIds.traceId;
1574
+ this.parentSpanId = bridgeIds.parentSpanId;
1575
+ return;
1576
+ }
1577
+ }
1370
1578
  if (options.parent) {
1371
1579
  this.traceId = options.parent.traceId;
1372
- } else if (options.traceId) {
1373
- if (isValidTraceId(options.traceId)) {
1374
- this.traceId = options.traceId;
1375
- } else {
1376
- console.error(
1377
- `[Mastra Tracing] Invalid traceId: must be 1-32 hexadecimal characters, got "${options.traceId}". Generating new trace ID.`
1378
- );
1379
- this.traceId = generateTraceId();
1380
- }
1381
- } else {
1382
- this.traceId = generateTraceId();
1580
+ this.parentSpanId = options.parent.id;
1581
+ this.id = generateSpanId();
1582
+ return;
1383
1583
  }
1384
- if (!options.parent && options.parentSpanId) {
1584
+ this.traceId = getOrCreateTraceId(options);
1585
+ this.id = generateSpanId();
1586
+ if (options.parentSpanId) {
1385
1587
  if (isValidSpanId(options.parentSpanId)) {
1386
1588
  this.parentSpanId = options.parentSpanId;
1387
1589
  } else {
1388
1590
  console.error(
1389
- `[Mastra Tracing] Invalid parentSpanId: must be 1-16 hexadecimal characters, got "${options.parentSpanId}". Ignoring parent span ID.`
1591
+ `[Mastra Tracing] Invalid parentSpanId: must be 1-16 hexadecimal characters, got "${options.parentSpanId}". Ignoring.`
1390
1592
  );
1391
1593
  }
1392
1594
  }
@@ -1491,6 +1693,18 @@ function isValidTraceId(traceId) {
1491
1693
  function isValidSpanId(spanId) {
1492
1694
  return /^[0-9a-f]{1,16}$/i.test(spanId);
1493
1695
  }
1696
+ function getOrCreateTraceId(options) {
1697
+ if (options.traceId) {
1698
+ if (isValidTraceId(options.traceId)) {
1699
+ return options.traceId;
1700
+ } else {
1701
+ console.error(
1702
+ `[Mastra Tracing] Invalid traceId: must be 1-32 hexadecimal characters, got "${options.traceId}". Generating new trace ID.`
1703
+ );
1704
+ }
1705
+ }
1706
+ return generateTraceId();
1707
+ }
1494
1708
 
1495
1709
  // src/spans/no-op.ts
1496
1710
  var NoOpSpan = class extends BaseSpan {
@@ -1523,13 +1737,17 @@ var BaseObservabilityInstance = class extends MastraBase {
1523
1737
  sampling: config.sampling ?? { type: "always" /* ALWAYS */ },
1524
1738
  exporters: config.exporters ?? [],
1525
1739
  spanOutputProcessors: config.spanOutputProcessors ?? [],
1740
+ bridge: config.bridge ?? void 0,
1526
1741
  includeInternalSpans: config.includeInternalSpans ?? false,
1527
1742
  requestContextKeys: config.requestContextKeys ?? []
1528
1743
  };
1744
+ if (this.config.bridge?.init) {
1745
+ this.config.bridge.init({ config: this.config });
1746
+ }
1529
1747
  }
1530
1748
  /**
1531
1749
  * Override setLogger to add Observability specific initialization log
1532
- * and propagate logger to exporters
1750
+ * and propagate logger to exporters and bridge
1533
1751
  */
1534
1752
  __setLogger(logger) {
1535
1753
  super.__setLogger(logger);
@@ -1538,8 +1756,11 @@ var BaseObservabilityInstance = class extends MastraBase {
1538
1756
  exporter.__setLogger(logger);
1539
1757
  }
1540
1758
  });
1759
+ if (this.config.bridge?.__setLogger) {
1760
+ this.config.bridge.__setLogger(logger);
1761
+ }
1541
1762
  this.logger.debug(
1542
- `[Observability] Initialized [service=${this.config.serviceName}] [instance=${this.config.name}] [sampling=${this.config.sampling.type}]`
1763
+ `[Observability] Initialized [service=${this.config.serviceName}] [instance=${this.config.name}] [sampling=${this.config.sampling?.type}] [bridge=${!!this.config.bridge}]`
1543
1764
  );
1544
1765
  }
1545
1766
  // ============================================================================
@@ -1568,11 +1789,15 @@ var BaseObservabilityInstance = class extends MastraBase {
1568
1789
  } else {
1569
1790
  traceState = this.computeTraceState(tracingOptions);
1570
1791
  }
1571
- const enrichedMetadata = this.extractMetadataFromRequestContext(requestContext, metadata, traceState);
1792
+ const tracingMetadata = !options.parent ? tracingOptions?.metadata : void 0;
1793
+ const mergedMetadata = metadata || tracingMetadata ? { ...metadata, ...tracingMetadata } : void 0;
1794
+ const enrichedMetadata = this.extractMetadataFromRequestContext(requestContext, mergedMetadata, traceState);
1795
+ const tags = !options.parent ? tracingOptions?.tags : void 0;
1572
1796
  const span = this.createSpan({
1573
1797
  ...rest,
1574
1798
  metadata: enrichedMetadata,
1575
- traceState
1799
+ traceState,
1800
+ tags
1576
1801
  });
1577
1802
  if (span.isEvent) {
1578
1803
  this.emitSpanEnded(span);
@@ -1606,6 +1831,12 @@ var BaseObservabilityInstance = class extends MastraBase {
1606
1831
  getSpanOutputProcessors() {
1607
1832
  return [...this.spanOutputProcessors];
1608
1833
  }
1834
+ /**
1835
+ * Get the bridge instance if configured
1836
+ */
1837
+ getBridge() {
1838
+ return this.config.bridge;
1839
+ }
1609
1840
  /**
1610
1841
  * Get the logger instance (for exporters and other components)
1611
1842
  */
@@ -1650,7 +1881,9 @@ var BaseObservabilityInstance = class extends MastraBase {
1650
1881
  */
1651
1882
  shouldSample(options) {
1652
1883
  const { sampling } = this.config;
1653
- switch (sampling.type) {
1884
+ switch (sampling?.type) {
1885
+ case void 0:
1886
+ return true;
1654
1887
  case "always" /* ALWAYS */:
1655
1888
  return true;
1656
1889
  case "never" /* NEVER */:
@@ -1782,17 +2015,21 @@ var BaseObservabilityInstance = class extends MastraBase {
1782
2015
  }
1783
2016
  }
1784
2017
  /**
1785
- * Export tracing event through all exporters (realtime mode)
2018
+ * Export tracing event through all exporters and bridge (realtime mode)
1786
2019
  */
1787
2020
  async exportTracingEvent(event) {
1788
- const exportPromises = this.exporters.map(async (exporter) => {
2021
+ const targets = [
2022
+ ...this.exporters
2023
+ ];
2024
+ if (this.config.bridge) {
2025
+ targets.push(this.config.bridge);
2026
+ }
2027
+ const exportPromises = targets.map(async (target) => {
1789
2028
  try {
1790
- if (exporter.exportTracingEvent) {
1791
- await exporter.exportTracingEvent(event);
1792
- this.logger.debug(`[Observability] Event exported [exporter=${exporter.name}] [type=${event.type}]`);
1793
- }
2029
+ await target.exportTracingEvent(event);
2030
+ this.logger.debug(`[Observability] Event exported [target=${target.name}] [type=${event.type}]`);
1794
2031
  } catch (error) {
1795
- this.logger.error(`[Observability] Export error [exporter=${exporter.name}]`, error);
2032
+ this.logger.error(`[Observability] Export error [target=${target.name}]`, error);
1796
2033
  }
1797
2034
  });
1798
2035
  await Promise.allSettled(exportPromises);
@@ -1816,6 +2053,9 @@ var BaseObservabilityInstance = class extends MastraBase {
1816
2053
  ...this.exporters.map((e) => e.shutdown()),
1817
2054
  ...this.spanOutputProcessors.map((p) => p.shutdown())
1818
2055
  ];
2056
+ if (this.config.bridge) {
2057
+ shutdownPromises.push(this.config.bridge.shutdown());
2058
+ }
1819
2059
  await Promise.allSettled(shutdownPromises);
1820
2060
  this.logger.info(`[Observability] Shutdown completed [name=${this.name}]`);
1821
2061
  }
@@ -1962,9 +2202,16 @@ var SensitiveDataFilter = class {
1962
2202
  /**
1963
2203
  * Recursively filter objects/arrays for sensitive keys.
1964
2204
  * Handles circular references by replacing with a marker.
2205
+ * Also attempts to parse and redact JSON strings.
1965
2206
  */
1966
2207
  deepFilter(obj, seen = /* @__PURE__ */ new WeakSet()) {
1967
2208
  if (obj === null || typeof obj !== "object") {
2209
+ if (typeof obj === "string") {
2210
+ const trimmed = obj.trim();
2211
+ if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
2212
+ return this.redactJsonString(obj);
2213
+ }
2214
+ }
1968
2215
  return obj;
1969
2216
  }
1970
2217
  if (seen.has(obj)) {
@@ -2017,6 +2264,22 @@ var SensitiveDataFilter = class {
2017
2264
  return normalizedKey === sensitiveField;
2018
2265
  });
2019
2266
  }
2267
+ /**
2268
+ * Attempt to parse a string as JSON and redact sensitive fields within it.
2269
+ * If parsing fails or no sensitive data is found, returns the original string.
2270
+ */
2271
+ redactJsonString(str) {
2272
+ try {
2273
+ const parsed = JSON.parse(str);
2274
+ if (parsed && typeof parsed === "object") {
2275
+ const filtered = this.deepFilter(parsed, /* @__PURE__ */ new WeakSet());
2276
+ return JSON.stringify(filtered);
2277
+ }
2278
+ return str;
2279
+ } catch {
2280
+ return str;
2281
+ }
2282
+ }
2020
2283
  /**
2021
2284
  * Redact a sensitive value.
2022
2285
  * - Full style: replaces with a fixed token.
@@ -2169,6 +2432,11 @@ var Observability = class extends MastraBase {
2169
2432
  }
2170
2433
  };
2171
2434
 
2172
- export { BaseExporter, BaseObservabilityInstance, BaseSpan, CloudExporter, ConsoleExporter, DefaultExporter, DefaultObservabilityInstance, DefaultSpan, ModelSpanTracker, NoOpSpan, Observability, SamplingStrategyType, SensitiveDataFilter, deepClean, observabilityConfigValueSchema, observabilityInstanceConfigSchema, observabilityRegistryConfigSchema, samplingStrategySchema };
2435
+ // src/tracing-options.ts
2436
+ function buildTracingOptions(...updaters) {
2437
+ return updaters.reduce((opts, updater) => updater(opts), {});
2438
+ }
2439
+
2440
+ export { BaseExporter, BaseObservabilityInstance, BaseSpan, CloudExporter, ConsoleExporter, DefaultExporter, DefaultObservabilityInstance, DefaultSpan, ModelSpanTracker, NoOpSpan, Observability, SamplingStrategyType, SensitiveDataFilter, TestExporter, buildTracingOptions, deepClean, getExternalParentId, observabilityConfigValueSchema, observabilityInstanceConfigSchema, observabilityRegistryConfigSchema, samplingStrategySchema };
2173
2441
  //# sourceMappingURL=index.js.map
2174
2442
  //# sourceMappingURL=index.js.map