@mastra/observability 1.17.0 → 1.17.1-alpha.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # @mastra/observability
2
2
 
3
+ ## 1.17.1-alpha.0
4
+
5
+ ### Patch Changes
6
+
7
+ - Fixed span serialization so internal tracing fields are removed only from framework-owned payloads while preserving user data with the same key names. ([#21332](https://github.com/mastra-ai/mastra/pull/21332))
8
+
9
+ - Advertise quota-pause support to the Mastra platform. `MastraPlatformExporter` now sends `x-mastra-observability-capabilities: quota-pause-v1` on every request (batch uploads for all five signal types, plus the traces recovery probe), letting the platform respond with `402 Payment Required` to clients that understand the quota-pause contract while shielding legacy clients from retry loops. No configuration change is required: ([#21447](https://github.com/mastra-ai/mastra/pull/21447))
10
+
11
+ ```ts
12
+ import { MastraPlatformExporter } from '@mastra/observability';
13
+
14
+ const exporter = new MastraPlatformExporter({
15
+ accessToken: process.env.MASTRA_PLATFORM_ACCESS_TOKEN,
16
+ });
17
+ ```
18
+
19
+ - Updated dependencies [[`15101bb`](https://github.com/mastra-ai/mastra/commit/15101bb53c0d934f31af6b8813b88191e382a5e5), [`c2c3deb`](https://github.com/mastra-ai/mastra/commit/c2c3debcf670c7082d0a5e553aa99818a864698c), [`33374ba`](https://github.com/mastra-ai/mastra/commit/33374ba359e4fb13eaa918ae925fe167a3c55414), [`c5f964d`](https://github.com/mastra-ai/mastra/commit/c5f964d3f77064e978f8066ec506eed77ba5c63c), [`f8f653f`](https://github.com/mastra-ai/mastra/commit/f8f653f10980d01a73706cc3c8689ca5e40ce808)]:
20
+ - @mastra/core@1.60.0-alpha.1
21
+
3
22
  ## 1.17.0
4
23
 
5
24
  ### Minor Changes
@@ -14,6 +14,8 @@ export interface MastraPlatformExporterConfig extends BaseExporterConfig {
14
14
  scoresEndpoint?: string;
15
15
  feedbackEndpoint?: string;
16
16
  }
17
+ export declare const OBSERVABILITY_CAPABILITIES_HEADER = "x-mastra-observability-capabilities";
18
+ export declare const QUOTA_PAUSE_CAPABILITY = "quota-pause-v1";
17
19
  export declare class MastraPlatformExporter extends BaseExporter {
18
20
  name: string;
19
21
  private readonly platformConfig;
@@ -1 +1 @@
1
- {"version":3,"file":"mastra-platform.d.ts","sourceRoot":"","sources":["../../src/exporters/mastra-platform.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,YAAY,EAEZ,QAAQ,EACR,WAAW,EACX,UAAU,EACV,aAAa,EACd,MAAM,4BAA4B,CAAC;AAGpC,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAC;AAEjD,MAAM,WAAW,4BAA6B,SAAQ,kBAAkB;IACtE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAkOD,qBAAa,sBAAuB,SAAQ,YAAY;IACtD,IAAI,SAA8B;IAElC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAmC;IAClE,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAsB;IAC1D,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,UAAU,CAA+B;IACjD,OAAO,CAAC,eAAe,CAA4B;IACnD,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,eAAe,CAA+B;IACtD,OAAO,CAAC,yBAAyB,CAAwC;IACzE,OAAO,CAAC,YAAY,CAAS;gBAEjB,MAAM,GAAE,4BAAiC;cAyErC,mBAAmB,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBjE,UAAU,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAa1C,aAAa,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAahD,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAa9C,eAAe,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAa1D,OAAO,CAAC,WAAW;IAQnB,OAAO,CAAC,cAAc;IAOtB,OAAO,CAAC,iBAAiB;IAOzB,OAAO,CAAC,gBAAgB;IAOxB,OAAO,CAAC,mBAAmB;IAO3B,OAAO,CAAC,eAAe;IAMvB,OAAO,CAAC,UAAU;IAelB,OAAO,CAAC,SAAS;IAMjB,OAAO,CAAC,YAAY;IAMpB,OAAO,CAAC,WAAW;IAMnB,OAAO,CAAC,cAAc;YAMR,mBAAmB;IAYjC,OAAO,CAAC,WAAW;IAenB,OAAO,CAAC,aAAa;YAoBP,WAAW;IA4EzB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAO3B,OAAO,CAAC,gBAAgB;YAIV,WAAW;IAoDzB;;;;;OAKG;IACH,OAAO,CAAC,eAAe;IAsBvB,OAAO,CAAC,kBAAkB;IAkB1B;;;;;OAKG;YACW,gBAAgB;YAoDhB,gBAAgB;IAmC9B,OAAO,CAAC,WAAW;IAUnB;;;;OAIG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA2BtB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAsChC"}
1
+ {"version":3,"file":"mastra-platform.d.ts","sourceRoot":"","sources":["../../src/exporters/mastra-platform.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,YAAY,EAEZ,QAAQ,EACR,WAAW,EACX,UAAU,EACV,aAAa,EACd,MAAM,4BAA4B,CAAC;AAGpC,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAC;AAEjD,MAAM,WAAW,4BAA6B,SAAQ,kBAAkB;IACtE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAyBD,eAAO,MAAM,iCAAiC,wCAAwC,CAAC;AACvF,eAAO,MAAM,sBAAsB,mBAAmB,CAAC;AA6MvD,qBAAa,sBAAuB,SAAQ,YAAY;IACtD,IAAI,SAA8B;IAElC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAmC;IAClE,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAsB;IAC1D,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,UAAU,CAA+B;IACjD,OAAO,CAAC,eAAe,CAA4B;IACnD,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,eAAe,CAA+B;IACtD,OAAO,CAAC,yBAAyB,CAAwC;IACzE,OAAO,CAAC,YAAY,CAAS;gBAEjB,MAAM,GAAE,4BAAiC;cAyErC,mBAAmB,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBjE,UAAU,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAa1C,aAAa,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAahD,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAa9C,eAAe,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAa1D,OAAO,CAAC,WAAW;IAQnB,OAAO,CAAC,cAAc;IAOtB,OAAO,CAAC,iBAAiB;IAOzB,OAAO,CAAC,gBAAgB;IAOxB,OAAO,CAAC,mBAAmB;IAO3B,OAAO,CAAC,eAAe;IAMvB,OAAO,CAAC,UAAU;IAelB,OAAO,CAAC,SAAS;IAMjB,OAAO,CAAC,YAAY;IAMpB,OAAO,CAAC,WAAW;IAMnB,OAAO,CAAC,cAAc;YAMR,mBAAmB;IAYjC,OAAO,CAAC,WAAW;IAenB,OAAO,CAAC,aAAa;YAoBP,WAAW;IA4EzB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAQ3B,OAAO,CAAC,gBAAgB;YAIV,WAAW;IAoDzB;;;;;OAKG;IACH,OAAO,CAAC,eAAe;IAsBvB,OAAO,CAAC,kBAAkB;IAkB1B;;;;;OAKG;YACW,gBAAgB;YAoDhB,gBAAgB;IAmC9B,OAAO,CAAC,WAAW;IAUnB;;;;OAIG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA2BtB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAsChC"}
package/dist/index.cjs CHANGED
@@ -210,21 +210,15 @@ var BaseObservabilityEventBus = class BaseObservabilityEventBus extends _mastra_
210
210
  * }
211
211
  * ```
212
212
  */
213
- /**
214
- * Default keys to strip from objects during deep cleaning.
215
- * These are typically internal/sensitive fields that shouldn't be traced.
216
- */
217
- const DEFAULT_KEYS_TO_STRIP = /* @__PURE__ */ new Set([
218
- "logger",
219
- "experimental_providerMetadata",
220
- "providerMetadata",
221
- "steps",
222
- "tracingContext",
223
- "execute",
224
- "validate"
225
- ]);
213
+ const FUNCTION_KEYS_TO_STRIP = /* @__PURE__ */ new Set(["execute", "validate"]);
214
+ const LOGGER_METHODS = [
215
+ "debug",
216
+ "info",
217
+ "warn",
218
+ "error"
219
+ ];
226
220
  const DEFAULT_DEEP_CLEAN_OPTIONS = Object.freeze({
227
- keysToStrip: DEFAULT_KEYS_TO_STRIP,
221
+ keysToStrip: [],
228
222
  maxDepth: 8,
229
223
  maxStringLength: 128 * 1024,
230
224
  maxArrayLength: 50,
@@ -237,7 +231,7 @@ const DEFAULT_DEEP_CLEAN_OPTIONS = Object.freeze({
237
231
  function mergeSerializationOptions(userOptions) {
238
232
  if (!userOptions) return DEFAULT_DEEP_CLEAN_OPTIONS;
239
233
  return {
240
- keysToStrip: DEFAULT_KEYS_TO_STRIP,
234
+ keysToStrip: DEFAULT_DEEP_CLEAN_OPTIONS.keysToStrip,
241
235
  maxDepth: userOptions.maxDepth ?? DEFAULT_DEEP_CLEAN_OPTIONS.maxDepth,
242
236
  maxStringLength: userOptions.maxStringLength ?? DEFAULT_DEEP_CLEAN_OPTIONS.maxStringLength,
243
237
  maxArrayLength: userOptions.maxArrayLength ?? DEFAULT_DEEP_CLEAN_OPTIONS.maxArrayLength,
@@ -263,6 +257,47 @@ function getMapKeyType(key) {
263
257
  if (key instanceof Error) return "error";
264
258
  return typeof key;
265
259
  }
260
+ function hasOnlyKnownKeys(value, keys) {
261
+ try {
262
+ return Object.keys(value).every((key) => keys.includes(key));
263
+ } catch {
264
+ return false;
265
+ }
266
+ }
267
+ function isSpanLike(value) {
268
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
269
+ const span = value;
270
+ try {
271
+ return typeof span.id === "string" && typeof span.traceId === "string" && typeof span.type === "string" && typeof span.name === "string";
272
+ } catch {
273
+ return false;
274
+ }
275
+ }
276
+ function isTracingContextLike(value) {
277
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
278
+ const context = value;
279
+ if (!hasOnlyKnownKeys(context, ["currentSpan"])) return false;
280
+ try {
281
+ return context.currentSpan === void 0 || isSpanLike(context.currentSpan);
282
+ } catch {
283
+ return false;
284
+ }
285
+ }
286
+ function isLoggerLike(value) {
287
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
288
+ const logger = value;
289
+ try {
290
+ return LOGGER_METHODS.some((method) => typeof logger[method] === "function");
291
+ } catch {
292
+ return false;
293
+ }
294
+ }
295
+ function shouldStripEntry(key, value, stripSet) {
296
+ if (stripSet.has(key)) return true;
297
+ if (key === "tracingContext" && isTracingContextLike(value)) return true;
298
+ if (key === "logger") return typeof value === "function" || isLoggerLike(value);
299
+ return FUNCTION_KEYS_TO_STRIP.has(key) && typeof value === "function";
300
+ }
266
301
  function restoreSerializedMapKey(keyType, key) {
267
302
  switch (keyType) {
268
303
  case "undefined": return;
@@ -365,7 +400,7 @@ function deepClean(value, options = DEFAULT_DEEP_CLEAN_OPTIONS) {
365
400
  let mapKeyCount = 0;
366
401
  let omittedMapEntries = 0;
367
402
  for (const [mapKey, mapVal] of val) {
368
- if (typeof mapKey === "string" && stripSet.has(mapKey)) continue;
403
+ if (typeof mapKey === "string" && shouldStripEntry(mapKey, mapVal, stripSet)) continue;
369
404
  if (mapKeyCount >= maxObjectKeys) {
370
405
  omittedMapEntries++;
371
406
  continue;
@@ -441,15 +476,34 @@ function deepClean(value, options = DEFAULT_DEEP_CLEAN_OPTIONS) {
441
476
  }
442
477
  if (looksLikeJsonSchema) return val;
443
478
  const cleaned = {};
444
- const keys = Object.keys(val).filter((key) => !stripSet.has(key));
479
+ let keys;
480
+ try {
481
+ keys = Object.keys(val);
482
+ } catch (error) {
483
+ return formatSerializationError(error);
484
+ }
445
485
  let keyCount = 0;
446
486
  for (const key of keys) {
487
+ if (stripSet.has(key)) continue;
488
+ let rawValue;
489
+ try {
490
+ rawValue = val[key];
491
+ } catch (error) {
492
+ if (keyCount >= maxObjectKeys) {
493
+ cleaned["__truncated"] = `${keys.length - keyCount} more keys omitted`;
494
+ break;
495
+ }
496
+ cleaned[key] = formatSerializationError(error);
497
+ keyCount++;
498
+ continue;
499
+ }
500
+ if (shouldStripEntry(key, rawValue, stripSet)) continue;
447
501
  if (keyCount >= maxObjectKeys) {
448
502
  cleaned["__truncated"] = `${keys.length - keyCount} more keys omitted`;
449
503
  break;
450
504
  }
451
505
  try {
452
- cleaned[key] = helper(val[key], depth + 1);
506
+ cleaned[key] = helper(rawValue, depth + 1);
453
507
  keyCount++;
454
508
  } catch (error) {
455
509
  cleaned[key] = formatSerializationError(error);
@@ -2462,6 +2516,49 @@ var ModelSpanTracker = class {
2462
2516
  }
2463
2517
  };
2464
2518
  //#endregion
2519
+ //#region src/spans/metadata.ts
2520
+ /**
2521
+ * Shared span-metadata helpers.
2522
+ *
2523
+ * Used by both the observability instance (`instances/base.ts`) and the span
2524
+ * base class (`spans/base.ts`) so the plain-record check and the
2525
+ * descriptor-preserving merge stay in a single place. Both feed the same span
2526
+ * metadata pipeline, so keeping one implementation avoids divergence.
2527
+ */
2528
+ /**
2529
+ * Returns true only for plain object records (prototype is `Object.prototype`
2530
+ * or `null`). Maps, Dates, class instances, and arrays return false so callers
2531
+ * can preserve their original shape instead of shallow-copying them into `{}`.
2532
+ */
2533
+ function isPlainRecord(value) {
2534
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
2535
+ try {
2536
+ const prototype = Object.getPrototypeOf(value);
2537
+ return prototype === Object.prototype || prototype === null;
2538
+ } catch {
2539
+ return false;
2540
+ }
2541
+ }
2542
+ /**
2543
+ * Merges two metadata values while preserving property descriptors (so getters
2544
+ * are copied as accessors rather than eagerly invoked). Only plain records are
2545
+ * merged; if either side is non-plain the second argument is returned as-is,
2546
+ * matching the previous per-module behavior.
2547
+ */
2548
+ function mergeMetadata$1(parentMetadata, metadata) {
2549
+ if (!parentMetadata) return metadata;
2550
+ if (!metadata) return parentMetadata;
2551
+ if (!isPlainRecord(parentMetadata) || !isPlainRecord(metadata)) return metadata;
2552
+ try {
2553
+ const merged = {};
2554
+ Object.defineProperties(merged, Object.getOwnPropertyDescriptors(parentMetadata));
2555
+ Object.defineProperties(merged, Object.getOwnPropertyDescriptors(metadata));
2556
+ return merged;
2557
+ } catch {
2558
+ return metadata;
2559
+ }
2560
+ }
2561
+ //#endregion
2465
2562
  //#region src/spans/base.ts
2466
2563
  /**
2467
2564
  * Determines if a span type should be considered internal based on flags.
@@ -2586,10 +2683,7 @@ var BaseSpan = class {
2586
2683
  this.type = options.type;
2587
2684
  this.isInternal = isSpanInternal(this.type, options.tracingPolicy?.internal);
2588
2685
  this.isExcluded = this.alwaysExcluded || observabilityConfig.excludeSpanTypes?.includes(this.type) === true || this.isInternal && !observabilityConfig.includeInternalSpans;
2589
- this.metadata = deepClean(options.parent?.metadata || options.metadata ? {
2590
- ...options.parent?.metadata,
2591
- ...options.metadata
2592
- } : void 0, this.deepCleanOptions);
2686
+ this.metadata = deepClean(this.prepareSpanMetadata(mergeMetadata$1(options.parent?.metadata, options.metadata)), this.deepCleanOptions);
2593
2687
  this.parent = options.parent;
2594
2688
  this.startTime = options.startTime ?? /* @__PURE__ */ new Date();
2595
2689
  this.observabilityInstance = observabilityInstance;
@@ -2607,9 +2701,32 @@ var BaseSpan = class {
2607
2701
  }
2608
2702
  this.attributes = deepClean(options.attributes, this.deepCleanOptions) || {};
2609
2703
  if (options.requestContext && options.requestContext.size() > 0) this.requestContext = deepClean(options.requestContext, this.deepCleanOptions);
2610
- if (this.isEvent) this.output = deepClean(options.output, this.deepCleanOptions);
2704
+ if (this.isEvent) this.output = deepClean(this.prepareSpanOutput(options.output), this.deepCleanOptions);
2611
2705
  else this.input = deepClean(options.input, this.deepCleanOptions);
2612
2706
  }
2707
+ prepareSpanOutput(value) {
2708
+ if (!isPlainRecord(value)) return value;
2709
+ if (this.type !== _mastra_core_observability.SpanType.MODEL_STEP && this.type !== _mastra_core_observability.SpanType.MODEL_INFERENCE) return value;
2710
+ try {
2711
+ const prepared = { ...value };
2712
+ delete prepared.steps;
2713
+ return prepared;
2714
+ } catch {
2715
+ return value;
2716
+ }
2717
+ }
2718
+ prepareSpanMetadata(value) {
2719
+ if (!isPlainRecord(value)) return value;
2720
+ if (this.type !== _mastra_core_observability.SpanType.MODEL_STEP) return value;
2721
+ try {
2722
+ const prepared = { ...value };
2723
+ delete prepared.providerMetadata;
2724
+ delete prepared.experimental_providerMetadata;
2725
+ return prepared;
2726
+ } catch {
2727
+ return value;
2728
+ }
2729
+ }
2613
2730
  createChildSpan(options) {
2614
2731
  return this.observabilityInstance.startSpan({
2615
2732
  ...options,
@@ -2816,10 +2933,10 @@ var DefaultSpan = class extends BaseSpan {
2816
2933
  this.endTime = /* @__PURE__ */ new Date();
2817
2934
  if (options?.metadata) this.metadata = {
2818
2935
  ...this.metadata,
2819
- ...deepClean(options.metadata, this.deepCleanOptions)
2936
+ ...deepClean(this.prepareSpanMetadata(options.metadata), this.deepCleanOptions)
2820
2937
  };
2821
2938
  if (this.isExcluded) return;
2822
- if (options?.output !== void 0) this.output = deepClean(options.output, this.deepCleanOptions);
2939
+ if (options?.output !== void 0) this.output = deepClean(this.prepareSpanOutput(options.output), this.deepCleanOptions);
2823
2940
  if (options?.attributes) this.attributes = {
2824
2941
  ...this.attributes,
2825
2942
  ...deepClean(options.attributes, this.deepCleanOptions)
@@ -2830,7 +2947,7 @@ var DefaultSpan = class extends BaseSpan {
2830
2947
  const { error, endSpan = true, attributes, metadata } = options;
2831
2948
  if (metadata) this.metadata = {
2832
2949
  ...this.metadata,
2833
- ...deepClean(metadata, this.deepCleanOptions)
2950
+ ...deepClean(this.prepareSpanMetadata(metadata), this.deepCleanOptions)
2834
2951
  };
2835
2952
  if (!this.isExcluded) {
2836
2953
  this.errorInfo = deepClean(error instanceof _mastra_core_error.MastraError ? {
@@ -2859,11 +2976,11 @@ var DefaultSpan = class extends BaseSpan {
2859
2976
  if (options.name !== void 0) this.name = options.name;
2860
2977
  if (options.metadata) this.metadata = {
2861
2978
  ...this.metadata,
2862
- ...deepClean(options.metadata, this.deepCleanOptions)
2979
+ ...deepClean(this.prepareSpanMetadata(options.metadata), this.deepCleanOptions)
2863
2980
  };
2864
2981
  if (this.isExcluded) return;
2865
2982
  if (options.input !== void 0) this.input = deepClean(options.input, this.deepCleanOptions);
2866
- if (options.output !== void 0) this.output = deepClean(options.output, this.deepCleanOptions);
2983
+ if (options.output !== void 0) this.output = deepClean(this.prepareSpanOutput(options.output), this.deepCleanOptions);
2867
2984
  if (options.attributes) this.attributes = {
2868
2985
  ...this.attributes,
2869
2986
  ...deepClean(options.attributes, this.deepCleanOptions)
@@ -2951,6 +3068,19 @@ var NoOpSpan = class extends BaseSpan {
2951
3068
  /**
2952
3069
  * BaseObservability - Abstract base class for Observability implementations
2953
3070
  */
3071
+ function hasMetadataKey(metadata, key) {
3072
+ if (!metadata || typeof metadata !== "object") return false;
3073
+ try {
3074
+ return Object.prototype.hasOwnProperty.call(Object.getOwnPropertyDescriptors(metadata), key);
3075
+ } catch {
3076
+ return true;
3077
+ }
3078
+ }
3079
+ function injectEnvironmentMetadata(metadata, environment) {
3080
+ if (environment === void 0 || hasMetadataKey(metadata, "environment")) return metadata;
3081
+ if (metadata && !isPlainRecord(metadata)) return metadata;
3082
+ return mergeMetadata$1(metadata, { environment });
3083
+ }
2954
3084
  /**
2955
3085
  * Abstract base class for all Observability implementations in Mastra.
2956
3086
  */
@@ -3046,16 +3176,9 @@ var BaseObservabilityInstance = class extends _mastra_core_base.MastraBase {
3046
3176
  let traceState;
3047
3177
  if (options.parent) traceState = options.parent.traceState;
3048
3178
  else traceState = this.computeTraceState(tracingOptions);
3049
- const tracingMetadata = !options.parent ? tracingOptions?.metadata : void 0;
3050
- const mergedMetadata = metadata || tracingMetadata ? {
3051
- ...metadata,
3052
- ...tracingMetadata
3053
- } : void 0;
3179
+ const mergedMetadata = mergeMetadata$1(metadata, !options.parent ? tracingOptions?.metadata : void 0);
3054
3180
  const enrichedMetadata = this.extractMetadataFromRequestContext(requestContext, mergedMetadata, traceState);
3055
- const finalMetadata = !options.parent && this.#mastraEnvironment !== void 0 && (enrichedMetadata === void 0 || enrichedMetadata.environment === void 0) ? {
3056
- ...enrichedMetadata ?? {},
3057
- environment: this.#mastraEnvironment
3058
- } : enrichedMetadata;
3181
+ const finalMetadata = !options.parent ? injectEnvironmentMetadata(enrichedMetadata, this.#mastraEnvironment) : enrichedMetadata;
3059
3182
  const tags = !options.parent ? tracingOptions?.tags : void 0;
3060
3183
  const traceId = !options.parent ? options.traceId ?? tracingOptions?.traceId : options.traceId;
3061
3184
  const parentSpanId = options.parentSpanId;
@@ -3320,10 +3443,7 @@ var BaseObservabilityInstance = class extends _mastra_core_base.MastraBase {
3320
3443
  if (!requestContext || !traceState || traceState.requestContextKeys.length === 0) return explicitMetadata;
3321
3444
  const extracted = this.extractKeys(requestContext, traceState.requestContextKeys);
3322
3445
  if (Object.keys(extracted).length === 0 && !explicitMetadata) return;
3323
- return {
3324
- ...extracted,
3325
- ...explicitMetadata
3326
- };
3446
+ return mergeMetadata$1(extracted, explicitMetadata);
3327
3447
  }
3328
3448
  /**
3329
3449
  * Extract specific keys from RequestContext
@@ -6370,6 +6490,8 @@ const QUOTA_EXCEEDED_STATUS = 402;
6370
6490
  const OBSERVABILITY_STATUS_HEADER = "x-mastra-observability";
6371
6491
  const OBSERVABILITY_DISABLED_VALUE = "disabled";
6372
6492
  const OBSERVABILITY_RETRY_AFTER_HEADER = "x-mastra-observability-retry-after";
6493
+ const OBSERVABILITY_CAPABILITIES_HEADER = "x-mastra-observability-capabilities";
6494
+ const QUOTA_PAUSE_CAPABILITY = "quota-pause-v1";
6373
6495
  const DEFAULT_QUOTA_PROBE_INTERVAL_SECONDS = 300;
6374
6496
  const MAX_QUOTA_PROBE_INTERVAL_SECONDS = Math.floor(2147483647 / 1e3);
6375
6497
  function isObservabilityDisabled(response) {
@@ -6696,7 +6818,8 @@ var MastraPlatformExporter = class extends BaseExporter {
6696
6818
  buildPublishHeaders() {
6697
6819
  return {
6698
6820
  Authorization: `Bearer ${this.platformConfig.accessToken}`,
6699
- "Content-Type": "application/json"
6821
+ "Content-Type": "application/json",
6822
+ [OBSERVABILITY_CAPABILITIES_HEADER]: QUOTA_PAUSE_CAPABILITY
6700
6823
  };
6701
6824
  }
6702
6825
  buildPublishBody(signal, records) {
@@ -6749,7 +6872,7 @@ var MastraPlatformExporter = class extends BaseExporter {
6749
6872
  this.flushTimer = null;
6750
6873
  }
6751
6874
  this.resetBuffer();
6752
- this.logger.warn(`Mastra observability paused: quota exhausted, dropping telemetry and probing every ${retryAfterSeconds}s`);
6875
+ this.logger.warn(`Mastra observability export paused: platform quota exhausted (OBSERVABILITY_QUOTA_EXCEEDED). Dropping telemetry and retrying in ${retryAfterSeconds} seconds. Check Platform billing/usage to restore telemetry.`);
6753
6876
  this.scheduleQuotaProbe();
6754
6877
  }
6755
6878
  scheduleQuotaProbe() {
@@ -9332,7 +9455,6 @@ exports.CardinalityFilter = CardinalityFilter;
9332
9455
  exports.CloudExporter = CloudExporter;
9333
9456
  exports.ConsoleExporter = ConsoleExporter;
9334
9457
  exports.DEFAULT_DEEP_CLEAN_OPTIONS = DEFAULT_DEEP_CLEAN_OPTIONS;
9335
- exports.DEFAULT_KEYS_TO_STRIP = DEFAULT_KEYS_TO_STRIP;
9336
9458
  exports.DEFAULT_LIMITS = DEFAULT_LIMITS;
9337
9459
  exports.DefaultExporter = DefaultExporter;
9338
9460
  exports.DefaultObservabilityInstance = DefaultObservabilityInstance;
@@ -9344,8 +9466,10 @@ exports.MastraStorageExporter = MastraStorageExporter;
9344
9466
  exports.MetricsContextImpl = MetricsContextImpl;
9345
9467
  exports.ModelSpanTracker = ModelSpanTracker;
9346
9468
  exports.NoOpSpan = NoOpSpan;
9469
+ exports.OBSERVABILITY_CAPABILITIES_HEADER = OBSERVABILITY_CAPABILITIES_HEADER;
9347
9470
  exports.Observability = Observability;
9348
9471
  exports.ObservabilityBus = ObservabilityBus;
9472
+ exports.QUOTA_PAUSE_CAPABILITY = QUOTA_PAUSE_CAPABILITY;
9349
9473
  exports.SamplingStrategyType = SamplingStrategyType;
9350
9474
  exports.SensitiveDataFilter = SensitiveDataFilter;
9351
9475
  exports.TestExporter = TestExporter;