@mastra/observability 1.21.1-alpha.1 → 1.22.0-alpha.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.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- //#region \0rolldown/runtime.js
2
+ // \0rolldown/runtime.js
3
3
  var __create = Object.create;
4
4
  var __defProp = Object.defineProperty;
5
5
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -20,11 +20,11 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
20
20
  value: mod,
21
21
  enumerable: true
22
22
  }) : target, mod));
23
- //#endregion
24
23
  let _mastra_core_base = require("@mastra/core/base");
25
24
  let _mastra_core_error = require("@mastra/core/error");
26
25
  let _mastra_core_logger = require("@mastra/core/logger");
27
26
  let _mastra_core_observability = require("@mastra/core/observability");
27
+ _mastra_core_observability = __toESM(_mastra_core_observability, 1);
28
28
  let _mastra_core_utils = require("@mastra/core/utils");
29
29
  let zod_v4 = require("zod/v4");
30
30
  let fs = require("fs");
@@ -38,7 +38,7 @@ let stream_web = require("stream/web");
38
38
  let _mastra_core_features = require("@mastra/core/features");
39
39
  let _mastra_core_storage = require("@mastra/core/storage");
40
40
  let crypto$1 = require("crypto");
41
- //#region src/bus/route-event.ts
41
+ // src/bus/route-event.ts
42
42
  /**
43
43
  * Route a single event to the appropriate method on a handler.
44
44
  *
@@ -105,8 +105,7 @@ function catchAsyncResult(result, handlerName, signal, logger) {
105
105
  logger.error(`[Observability] ${signal} handler error [handler=${handlerName}]:`, err);
106
106
  });
107
107
  }
108
- //#endregion
109
- //#region src/bus/base.ts
108
+ // src/bus/base.ts
110
109
  /**
111
110
  * BaseObservabilityEventBus - Generic event bus for observability events.
112
111
  *
@@ -181,8 +180,7 @@ var BaseObservabilityEventBus = class BaseObservabilityEventBus extends _mastra_
181
180
  this.subscribers.clear();
182
181
  }
183
182
  };
184
- //#endregion
185
- //#region src/spans/serialization.ts
183
+ // src/spans/serialization.ts
186
184
  /**
187
185
  * Bounded serialization utilities for AI tracing.
188
186
  *
@@ -548,8 +546,7 @@ function deepClean(value, options = DEFAULT_DEEP_CLEAN_OPTIONS) {
548
546
  }
549
547
  return helper(value, 0);
550
548
  }
551
- //#endregion
552
- //#region src/bus/observability-bus.ts
549
+ // src/bus/observability-bus.ts
553
550
  /**
554
551
  * Apply deepClean() to non-tracing observability events. Tracing events are
555
552
  * already deep-cleaned at span construction time (see spans/base.ts and
@@ -758,8 +755,7 @@ var ObservabilityBus = class extends BaseObservabilityEventBus {
758
755
  await super.shutdown();
759
756
  }
760
757
  };
761
- //#endregion
762
- //#region src/config.ts
758
+ // src/config.ts
763
759
  /**
764
760
  * Sampling strategy types
765
761
  */
@@ -886,8 +882,7 @@ const observabilityRegistryConfigSchema = zod_v4.z.object({
886
882
  }
887
883
  return true;
888
884
  }, { message: "A \"configSelector\" requires at least one config or default observability to be configured." });
889
- //#endregion
890
- //#region src/ids.ts
885
+ // src/ids.ts
891
886
  /**
892
887
  * Signal and span id helpers.
893
888
  *
@@ -955,8 +950,7 @@ function resolveExportedSpanId(span) {
955
950
  if (!span) return void 0;
956
951
  return typeof span.getExportedSpanId === "function" ? span.getExportedSpanId() : span.id;
957
952
  }
958
- //#endregion
959
- //#region src/context/logger.ts
953
+ // src/context/logger.ts
960
954
  /** Numeric priority used to compare log levels against the configured minimum. */
961
955
  const LOG_LEVEL_PRIORITY = {
962
956
  debug: 0,
@@ -1024,8 +1018,7 @@ var LoggerContextImpl = class {
1024
1018
  this.config.observabilityBus.emit(event);
1025
1019
  }
1026
1020
  };
1027
- //#endregion
1028
- //#region src/context/metrics.ts
1021
+ // src/context/metrics.ts
1029
1022
  /**
1030
1023
  * User-facing metric emission API. All metrics are routed through
1031
1024
  * ObservabilityBus.emit() after validation and cardinality filtering.
@@ -1100,14 +1093,12 @@ function cloneCostContext(costContext) {
1100
1093
  costMetadata: costContext.costMetadata ? structuredClone(costContext.costMetadata) : void 0
1101
1094
  };
1102
1095
  }
1103
- //#endregion
1104
- //#region src/model-id.ts
1096
+ // src/model-id.ts
1105
1097
  /** Return the first model id that contains non-whitespace characters. */
1106
1098
  function resolveModelId(...candidates) {
1107
1099
  return candidates.find((candidate) => candidate?.trim());
1108
1100
  }
1109
- //#endregion
1110
- //#region src/metrics/pricing-model.ts
1101
+ // src/metrics/pricing-model.ts
1111
1102
  var PricingTier = class {
1112
1103
  index;
1113
1104
  when;
@@ -1165,8 +1156,7 @@ var PricingModel = class {
1165
1156
  return this.tiers[0] ?? null;
1166
1157
  }
1167
1158
  };
1168
- //#endregion
1169
- //#region src/metrics/models-dev.ts
1159
+ // src/metrics/models-dev.ts
1170
1160
  /** Same catalog the model router reads in `@mastra/core` (`gateways/models-dev.ts`). */
1171
1161
  const MODELS_DEV_API_URL = "https://models.dev/api.json";
1172
1162
  /**
@@ -1297,8 +1287,7 @@ function sortedEntries(value) {
1297
1287
  function isRecord(value) {
1298
1288
  return typeof value === "object" && value !== null && !Array.isArray(value);
1299
1289
  }
1300
- //#endregion
1301
- //#region src/metrics/pricing-registry.ts
1290
+ // src/metrics/pricing-registry.ts
1302
1291
  const DATA_FILE_NAME = "pricing-data.json";
1303
1292
  const REFRESH_INTERVAL_MS = 864e5;
1304
1293
  const MIN_REFRESH_ATTEMPT_INTERVAL_MS = 36e5;
@@ -1649,8 +1638,7 @@ function stripDateSuffix(model) {
1649
1638
  stripped = model.replace(/-\d{2}-20\d{2}$/, "");
1650
1639
  return stripped;
1651
1640
  }
1652
- //#endregion
1653
- //#region src/metrics/types.ts
1641
+ // src/metrics/types.ts
1654
1642
  const PricingMeter = {
1655
1643
  INPUT_TOKENS: "input_tokens",
1656
1644
  INPUT_AUDIO_TOKENS: "input_audio_tokens",
@@ -1686,8 +1674,7 @@ const TokenMetrics = {
1686
1674
  OUTPUT_AUDIO: "mastra_model_output_audio_tokens",
1687
1675
  OUTPUT_IMAGE: "mastra_model_output_image_tokens"
1688
1676
  };
1689
- //#endregion
1690
- //#region src/metrics/usage-metrics.ts
1677
+ // src/metrics/usage-metrics.ts
1691
1678
  function getTokenMetricSamples(usage) {
1692
1679
  const samples = [];
1693
1680
  const pushIfDefined = (name, value) => {
@@ -1721,8 +1708,7 @@ function getTokenMetricSamples(usage) {
1721
1708
  }
1722
1709
  return samples;
1723
1710
  }
1724
- //#endregion
1725
- //#region src/metrics/estimator.ts
1711
+ // src/metrics/estimator.ts
1726
1712
  function estimateCosts(args, pricingRegistry = PricingRegistry.getGlobal()) {
1727
1713
  const { provider, model, usage } = args;
1728
1714
  const results = /* @__PURE__ */ new Map();
@@ -1954,8 +1940,7 @@ function estimateCostForMeter(args) {
1954
1940
  }
1955
1941
  };
1956
1942
  }
1957
- //#endregion
1958
- //#region src/metrics/auto-extract.ts
1943
+ // src/metrics/auto-extract.ts
1959
1944
  /**
1960
1945
  * Emits metrics derived from live spans.
1961
1946
  */
@@ -2109,8 +2094,7 @@ function getDurationMetricName(span) {
2109
2094
  default: return null;
2110
2095
  }
2111
2096
  }
2112
- //#endregion
2113
- //#region src/metrics/cardinality.ts
2097
+ // src/metrics/cardinality.ts
2114
2098
  /** Matches standard UUID v4 strings (case-insensitive). */
2115
2099
  const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
2116
2100
  var CardinalityFilter = class {
@@ -2141,8 +2125,7 @@ var CardinalityFilter = class {
2141
2125
  return filtered;
2142
2126
  }
2143
2127
  };
2144
- //#endregion
2145
- //#region src/usage.ts
2128
+ // src/usage.ts
2146
2129
  /**
2147
2130
  * For non-BYOK requests, `usage.cost` is the complete OpenRouter charge and
2148
2131
  * `usage.costDetails.upstreamInferenceCost` is only its upstream breakdown. For BYOK requests,
@@ -2327,8 +2310,7 @@ function addUsageStats(a, b) {
2327
2310
  outputDetails: mergeOutputDetails(a.outputDetails, b.outputDetails)
2328
2311
  };
2329
2312
  }
2330
- //#endregion
2331
- //#region src/model-tracing.ts
2313
+ // src/model-tracing.ts
2332
2314
  /**
2333
2315
  * Model Span Tracing
2334
2316
  *
@@ -2756,8 +2738,20 @@ var ModelSpanTracker = class {
2756
2738
  * feature flag, or when called without an active step span. Auto-invoked from
2757
2739
  * chunk handlers as a safety net; explicit callers get the most accurate
2758
2740
  * start time.
2741
+ *
2742
+ * `payload.modelId` / `payload.provider` name the model the caller is about
2743
+ * to invoke and are recorded on MODEL_STEP, also when a fallback attempt
2744
+ * reuses the step's open inference span. The safety net never records them:
2745
+ * a replayed step-start chunk names the model of the original call.
2759
2746
  */
2760
2747
  startInference(payload) {
2748
+ if (payload?.modelId && payload.provider) this.#currentStepSpan?.update({ attributes: {
2749
+ model: payload.modelId,
2750
+ provider: payload.provider
2751
+ } });
2752
+ this.#openInference(payload);
2753
+ }
2754
+ #openInference(payload) {
2761
2755
  if (!supportsModelInference()) return;
2762
2756
  if (!this.#currentStepSpan || this.#currentInferenceSpan) return;
2763
2757
  this.#inferenceCompletionStartTime = void 0;
@@ -2882,7 +2876,7 @@ var ModelSpanTracker = class {
2882
2876
  */
2883
2877
  #ensureStepAndInference() {
2884
2878
  if (!this.#currentStepSpan) this.startStep();
2885
- if (!this.#currentInferenceSpan) this.startInference();
2879
+ if (!this.#currentInferenceSpan) this.#openInference();
2886
2880
  }
2887
2881
  /**
2888
2882
  * Create a new chunk span (for multi-part chunks like text-start/delta/end)
@@ -3082,7 +3076,7 @@ var ModelSpanTracker = class {
3082
3076
  this.#inModelResponse = true;
3083
3077
  if (this.#currentStepSpan) this.updateStep(chunk.payload);
3084
3078
  else this.startStep(chunk.payload);
3085
- if (!this.#currentInferenceSpan) this.startInference(chunk.payload);
3079
+ if (!this.#currentInferenceSpan) this.#openInference(chunk.payload);
3086
3080
  break;
3087
3081
  case "step-finish":
3088
3082
  this.#inModelResponse = false;
@@ -3128,8 +3122,7 @@ var ModelSpanTracker = class {
3128
3122
  } }));
3129
3123
  }
3130
3124
  };
3131
- //#endregion
3132
- //#region src/spans/metadata.ts
3125
+ // src/spans/metadata.ts
3133
3126
  /**
3134
3127
  * Shared span-metadata helpers.
3135
3128
  *
@@ -3196,8 +3189,7 @@ function mergeMetadata$1(parentMetadata, metadata) {
3196
3189
  return metadata;
3197
3190
  }
3198
3191
  }
3199
- //#endregion
3200
- //#region src/spans/base.ts
3192
+ // src/spans/base.ts
3201
3193
  const LINEAGE_KEYS = [
3202
3194
  "parentEntityType",
3203
3195
  "parentEntityId",
@@ -3208,10 +3200,10 @@ const LINEAGE_KEYS = [
3208
3200
  "rootEntityName",
3209
3201
  "rootEntityVersionId"
3210
3202
  ];
3211
- /** Copy only the lineage fields that have values, so spans don't carry undefined keys */
3212
3203
  function ownEntityVersionId(span) {
3213
3204
  return span instanceof BaseSpan ? span.ownEntityVersionId : void 0;
3214
3205
  }
3206
+ /** Copy only the lineage fields that have values, so spans don't carry undefined keys */
3215
3207
  function definedLineage(source) {
3216
3208
  const lineage = {};
3217
3209
  for (const key of LINEAGE_KEYS) {
@@ -3290,6 +3282,19 @@ function getExternalParentId(options) {
3290
3282
  if (parent.isInternal || parent.isExcluded) return parent.getParentSpanId(false);
3291
3283
  return parent.id;
3292
3284
  }
3285
+ /**
3286
+ * Vercel AI Gateway reports which provider served a step, and any provider
3287
+ * attempts that failed first, only in `providerMetadata.gateway.routing`.
3288
+ * Read it defensively so a throwing getter never skips the strip below.
3289
+ */
3290
+ function readGatewayRouting(providerMetadata) {
3291
+ try {
3292
+ const gateway = isPlainRecord(providerMetadata) ? providerMetadata.gateway : void 0;
3293
+ return isPlainRecord(gateway) ? gateway.routing : void 0;
3294
+ } catch {
3295
+ return;
3296
+ }
3297
+ }
3293
3298
  var BaseSpan = class BaseSpan {
3294
3299
  name;
3295
3300
  type;
@@ -3417,8 +3422,10 @@ var BaseSpan = class BaseSpan {
3417
3422
  if (this.type !== _mastra_core_observability.SpanType.MODEL_STEP) return value;
3418
3423
  try {
3419
3424
  const prepared = { ...value };
3425
+ const gatewayRouting = readGatewayRouting(prepared.providerMetadata);
3420
3426
  delete prepared.providerMetadata;
3421
3427
  delete prepared.experimental_providerMetadata;
3428
+ if (gatewayRouting !== void 0) prepared.providerMetadata = { gateway: { routing: gatewayRouting } };
3422
3429
  return prepared;
3423
3430
  } catch {
3424
3431
  return value;
@@ -3604,7 +3611,7 @@ var BaseSpan = class BaseSpan {
3604
3611
  entityType: this.entityType,
3605
3612
  entityId: this.entityId,
3606
3613
  entityName: this.entityName,
3607
- entityVersionId: getMetadataString("entityVersionId"),
3614
+ entityVersionId: this.ownEntityVersionId,
3608
3615
  ...parentLineage,
3609
3616
  ...rootLineage,
3610
3617
  userId: getMetadataString("userId"),
@@ -3647,6 +3654,7 @@ var BaseSpan = class BaseSpan {
3647
3654
  isEvent: this.isEvent,
3648
3655
  isRootSpan: this.isRootSpan,
3649
3656
  ...this.isInternal ? { isInternal: true } : {},
3657
+ ...correlation.entityVersionId ? { entityVersionId: correlation.entityVersionId } : {},
3650
3658
  ...definedLineage(correlation),
3651
3659
  parentSpanId: this.getParentSpanId(includeInternalSpans),
3652
3660
  externalParentSpanId: this.getExportedExternalParentSpanId(includeInternalSpans),
@@ -3691,8 +3699,7 @@ var BaseSpan = class BaseSpan {
3691
3699
  return fn();
3692
3700
  }
3693
3701
  };
3694
- //#endregion
3695
- //#region src/spans/default.ts
3702
+ // src/spans/default.ts
3696
3703
  /** Every AI SDK `APICallError` (v4, v5 and v6) carries this shared marker symbol. */
3697
3704
  const API_CALL_ERROR_MARKER = Symbol.for("vercel.ai.error.AI_APICallError");
3698
3705
  function isApiCallError(value) {
@@ -3883,8 +3890,7 @@ function getOrCreateTraceId(options) {
3883
3890
  }
3884
3891
  return generateTraceId();
3885
3892
  }
3886
- //#endregion
3887
- //#region src/spans/no-op.ts
3893
+ // src/spans/no-op.ts
3888
3894
  var NoOpSpan = class extends BaseSpan {
3889
3895
  id;
3890
3896
  traceId;
@@ -3903,8 +3909,7 @@ var NoOpSpan = class extends BaseSpan {
3903
3909
  return true;
3904
3910
  }
3905
3911
  };
3906
- //#endregion
3907
- //#region src/exporters/base.ts
3912
+ // src/exporters/base.ts
3908
3913
  /**
3909
3914
  * Base Exporter for Observability
3910
3915
  *
@@ -4066,8 +4071,7 @@ var BaseExporter = class {
4066
4071
  this.logger.info(`${this.name} shutdown complete`);
4067
4072
  }
4068
4073
  };
4069
- //#endregion
4070
- //#region src/exporters/event-buffer.ts
4074
+ // src/exporters/event-buffer.ts
4071
4075
  /**
4072
4076
  * Buffers observability events (creates and updates) for batch flushing.
4073
4077
  * Handles strategy-aware routing of tracing events and tracks created spans
@@ -4078,6 +4082,8 @@ var EventBuffer = class {
4078
4082
  #creates = [];
4079
4083
  #updates = [];
4080
4084
  #allCreatedSpans = /* @__PURE__ */ new Set();
4085
+ /** Spans whose SPAN_STARTED this buffer has seen but whose create isn't confirmed yet */
4086
+ #pendingCreateSpans = /* @__PURE__ */ new Set();
4081
4087
  #firstEventTime;
4082
4088
  #storageStrategy;
4083
4089
  #maxRetries;
@@ -4128,8 +4134,11 @@ var EventBuffer = class {
4128
4134
  case _mastra_core_observability.TracingEventType.SPAN_STARTED:
4129
4135
  switch (this.#storageStrategy) {
4130
4136
  case "realtime":
4131
- case "event-sourced":
4132
- case "batch-with-updates": this.pushCreate(event);
4137
+ case "batch-with-updates":
4138
+ if (!event.exportedSpan.isEvent) this.#pendingCreateSpans.add(this.exportedSpanKey(event.exportedSpan));
4139
+ this.pushCreate(event);
4140
+ break;
4141
+ case "event-sourced": this.pushCreate(event);
4133
4142
  }
4134
4143
  break;
4135
4144
  case _mastra_core_observability.TracingEventType.SPAN_UPDATED:
@@ -4198,28 +4207,44 @@ var EventBuffer = class {
4198
4207
  buildSpanKey(span) {
4199
4208
  return `${span.traceId}:${span.spanId}`;
4200
4209
  }
4210
+ exportedSpanKey(span) {
4211
+ return this.buildSpanKey({
4212
+ traceId: span.traceId,
4213
+ spanId: span.id
4214
+ });
4215
+ }
4201
4216
  /** Track successfully created spans so updates can verify span existence before flushing. */
4202
4217
  addCreatedSpans(args) {
4203
4218
  if (this.#storageStrategy === "event-sourced" || this.#storageStrategy === "insert-only") return;
4204
- for (const createRecord of args.records) if (!createRecord.isEvent) this.#allCreatedSpans.add(this.buildSpanKey(createRecord));
4219
+ for (const createRecord of args.records) if (!createRecord.isEvent) {
4220
+ const key = this.buildSpanKey(createRecord);
4221
+ this.#pendingCreateSpans.delete(key);
4222
+ this.#allCreatedSpans.add(key);
4223
+ }
4224
+ }
4225
+ /** Stop waiting on creates that were dropped, so their updates aren't held back for them. */
4226
+ forgetDroppedCreates(events) {
4227
+ for (const event of events) if (event.type === _mastra_core_observability.TracingEventType.SPAN_STARTED) this.#pendingCreateSpans.delete(this.exportedSpanKey(event.exportedSpan));
4205
4228
  }
4206
4229
  /** Check whether a span's create record has already been flushed to storage. */
4207
4230
  spanExists(span) {
4208
- return this.#allCreatedSpans?.has(this.buildSpanKey({
4209
- traceId: span.traceId,
4210
- spanId: span.id
4211
- }));
4231
+ return this.#allCreatedSpans.has(this.exportedSpanKey(span));
4232
+ }
4233
+ /** Check whether this buffer saw the span's SPAN_STARTED but its create hasn't been confirmed yet. */
4234
+ isAwaitingCreate(span) {
4235
+ return this.#pendingCreateSpans.has(this.exportedSpanKey(span));
4212
4236
  }
4213
4237
  /** Remove completed spans from tracking after their SPAN_ENDED updates are flushed. */
4214
4238
  endFinishedSpans(args) {
4215
4239
  if (this.#storageStrategy === "event-sourced" || this.#storageStrategy === "insert-only") return;
4216
4240
  args.records.forEach((r) => {
4217
- this.#allCreatedSpans.delete(this.buildSpanKey(r));
4241
+ const key = this.buildSpanKey(r);
4242
+ this.#allCreatedSpans.delete(key);
4243
+ this.#pendingCreateSpans.delete(key);
4218
4244
  });
4219
4245
  }
4220
4246
  };
4221
- //#endregion
4222
- //#region src/exporters/default.ts
4247
+ // src/exporters/default.ts
4223
4248
  /**
4224
4249
  * Resolves the final tracing storage strategy based on config and observability store hints
4225
4250
  */
@@ -4657,8 +4682,22 @@ var DefaultExporter = class extends BaseExporter {
4657
4682
  this.logger.info("DefaultExporter shutdown complete");
4658
4683
  }
4659
4684
  };
4660
- //#endregion
4661
- //#region src/exporters/mastra-storage.ts
4685
+ // src/exporters/span-previews.ts
4686
+ const buildSpanPreviews = _mastra_core_observability.buildSpanPreviews;
4687
+ /** Older core versions retain their existing storage preview fallback. */
4688
+ function withSpanPreviews(span) {
4689
+ if (!_mastra_core_features.coreFeatures.has("observability-span-previews")) return span;
4690
+ try {
4691
+ const previews = buildSpanPreviews?.(span);
4692
+ return previews ? {
4693
+ ...span,
4694
+ ...previews
4695
+ } : span;
4696
+ } catch {
4697
+ return span;
4698
+ }
4699
+ }
4700
+ // src/exporters/mastra-storage.ts
4662
4701
  /**
4663
4702
  * Resolves the final tracing storage strategy based on config and observability store hints
4664
4703
  */
@@ -4861,6 +4900,7 @@ var MastraStorageExporter = class extends BaseExporter {
4861
4900
  return { failed: true };
4862
4901
  }
4863
4902
  const dropped = this.#eventBuffer.reAddCreates(events);
4903
+ this.#eventBuffer.forgetDroppedCreates(dropped);
4864
4904
  const retryAttempt = this.retryAttempt(events);
4865
4905
  this.logStorageFailure(signal, events, retryAttempt, error);
4866
4906
  this.emitDrop(signal, "retry-exhausted", dropped.length, error);
@@ -4871,7 +4911,15 @@ var MastraStorageExporter = class extends BaseExporter {
4871
4911
  }
4872
4912
  }
4873
4913
  /**
4874
- * Flush span update/end events, deferring any whose span hasn't been created yet.
4914
+ * Flush span update/end events.
4915
+ *
4916
+ * When the store supports conditional updates, only spans this exporter started
4917
+ * and hasn't confirmed the create for are deferred. Everything else, including
4918
+ * spans another exporter instance started (such as a durable agent ending spans
4919
+ * it rebuilt after a crash), goes straight to storage, and updates that didn't
4920
+ * apply are counted as drops. Without conditional updates, updates for spans this
4921
+ * exporter hasn't created are deferred until their create is flushed.
4922
+ *
4875
4923
  * When `isEnd` is true, successfully flushed spans are removed from tracking.
4876
4924
  */
4877
4925
  async flushSpanUpdates(events, deferredUpdates, isEnd) {
@@ -4881,10 +4929,12 @@ var MastraStorageExporter = class extends BaseExporter {
4881
4929
  this.emitDrop("tracing", "unsupported-storage", events.length);
4882
4930
  return { failed: true };
4883
4931
  }
4932
+ const storage = this.#observabilityStorage;
4933
+ const batchUpdateUnendedSpans = typeof storage.batchUpdateUnendedSpans === "function" ? storage.batchUpdateUnendedSpans.bind(storage) : void 0;
4884
4934
  const partials = [];
4885
4935
  for (const event of events) {
4886
4936
  const span = event.exportedSpan;
4887
- if (this.#eventBuffer.spanExists(span)) partials.push({
4937
+ if (batchUpdateUnendedSpans ? !this.#eventBuffer.isAwaitingCreate(span) : this.#eventBuffer.spanExists(span)) partials.push({
4888
4938
  traceId: span.traceId,
4889
4939
  spanId: span.id,
4890
4940
  updates: (0, _mastra_core_storage.buildUpdateSpanRecord)(span)
@@ -4893,7 +4943,10 @@ var MastraStorageExporter = class extends BaseExporter {
4893
4943
  }
4894
4944
  if (partials.length === 0) return FLUSH_SUCCEEDED;
4895
4945
  try {
4896
- await this.#observabilityStorage.batchUpdateSpans({ records: partials });
4946
+ if (batchUpdateUnendedSpans) {
4947
+ const { updated } = await batchUpdateUnendedSpans({ records: partials });
4948
+ this.emitNotApplied(partials, updated);
4949
+ } else await storage.batchUpdateSpans({ records: partials });
4897
4950
  if (isEnd) this.#eventBuffer.endFinishedSpans({ records: partials });
4898
4951
  return FLUSH_SUCCEEDED;
4899
4952
  } catch (error) {
@@ -4917,6 +4970,27 @@ var MastraStorageExporter = class extends BaseExporter {
4917
4970
  }
4918
4971
  }
4919
4972
  /**
4973
+ * Reports conditional span updates that storage didn't apply: the span wasn't
4974
+ * stored, or it had already ended. Ended spans are immutable, so these are dropped.
4975
+ */
4976
+ emitNotApplied(partials, updated) {
4977
+ const appliedCounts = /* @__PURE__ */ new Map();
4978
+ for (const span of updated) {
4979
+ const key = `${span.traceId}:${span.spanId}`;
4980
+ appliedCounts.set(key, (appliedCounts.get(key) ?? 0) + 1);
4981
+ }
4982
+ let notApplied = 0;
4983
+ for (const partial of partials) {
4984
+ const key = `${partial.traceId}:${partial.spanId}`;
4985
+ const remaining = appliedCounts.get(key) ?? 0;
4986
+ if (remaining > 0) appliedCounts.set(key, remaining - 1);
4987
+ else notApplied++;
4988
+ }
4989
+ if (notApplied === 0) return;
4990
+ this.logger.debug("Span updates not applied: span not stored or already ended", { count: notApplied });
4991
+ this.emitDrop("tracing", "span-not-updatable", notApplied);
4992
+ }
4993
+ /**
4920
4994
  * Runs flushBuffer() and tracks its promise so flush() can wait for writes
4921
4995
  * that a timer or size trigger started on their own.
4922
4996
  */
@@ -5026,7 +5100,10 @@ var MastraStorageExporter = class extends BaseExporter {
5026
5100
  this.logger.debug("Cannot store traces. Observability storage is not initialized");
5027
5101
  return;
5028
5102
  }
5029
- this.#eventBuffer.addEvent(event);
5103
+ this.#eventBuffer.addEvent({
5104
+ ...event,
5105
+ exportedSpan: withSpanPreviews(event.exportedSpan)
5106
+ });
5030
5107
  await this.handleBatchedFlush();
5031
5108
  }
5032
5109
  /**
@@ -5095,8 +5172,7 @@ var MastraStorageExporter = class extends BaseExporter {
5095
5172
  this.logger.info("MastraStorageExporter shutdown complete");
5096
5173
  }
5097
5174
  };
5098
- //#endregion
5099
- //#region src/instances/platform-policy.ts
5175
+ // src/instances/platform-policy.ts
5100
5176
  const MASTRA_BUILT_IN_STORAGE_EXPORTER_NAMES = /* @__PURE__ */ new Set(["mastra-storage-exporter", "mastra-default-observability-exporter"]);
5101
5177
  function isMastraPlatformDeployment() {
5102
5178
  return Boolean(process.env.MASTRA_DEPLOYMENT_ID);
@@ -5104,8 +5180,7 @@ function isMastraPlatformDeployment() {
5104
5180
  function isMastraBuiltInStorageExporter(exporter) {
5105
5181
  return exporter instanceof MastraStorageExporter || exporter instanceof DefaultExporter || MASTRA_BUILT_IN_STORAGE_EXPORTER_NAMES.has(exporter.name);
5106
5182
  }
5107
- //#endregion
5108
- //#region src/instances/base.ts
5183
+ // src/instances/base.ts
5109
5184
  /**
5110
5185
  * BaseObservability - Abstract base class for Observability implementations
5111
5186
  */
@@ -5818,13 +5893,11 @@ var BaseObservabilityInstance = class extends _mastra_core_base.MastraBase {
5818
5893
  this.logger.info(`[Observability] Shutdown completed [name=${this.name}]`);
5819
5894
  }
5820
5895
  };
5821
- //#endregion
5822
- //#region src/client/id.ts
5896
+ // src/client/id.ts
5823
5897
  function generateClientSignalId() {
5824
5898
  return globalThis.crypto.randomUUID();
5825
5899
  }
5826
- //#endregion
5827
- //#region src/client/otlp.ts
5900
+ // src/client/otlp.ts
5828
5901
  /**
5829
5902
  * Hand-rolled OTLP/JSON decoder for the subset we need to ingest from
5830
5903
  * client-side tool execution.
@@ -6012,8 +6085,7 @@ function buildExportedLog(decoded) {
6012
6085
  data: decoded.attributes
6013
6086
  };
6014
6087
  }
6015
- //#endregion
6016
- //#region src/client/w3c.ts
6088
+ // src/client/w3c.ts
6017
6089
  /**
6018
6090
  * Hand-rolled W3C Trace Context and Baggage propagation.
6019
6091
  *
@@ -6084,8 +6156,7 @@ function formatBaggage(entries) {
6084
6156
  }
6085
6157
  return parts.join(",");
6086
6158
  }
6087
- //#endregion
6088
- //#region src/client/proxy.ts
6159
+ // src/client/proxy.ts
6089
6160
  const DEFAULT_LIMITS = {
6090
6161
  maxSpans: 10,
6091
6162
  maxLogs: 10,
@@ -6223,8 +6294,7 @@ function validateParentLinks(spans, rootParent) {
6223
6294
  function createClientObservabilityProxy(options) {
6224
6295
  return new ClientObservabilityProxyImpl(options);
6225
6296
  }
6226
- //#endregion
6227
- //#region src/exporters/tracking.ts
6297
+ // src/exporters/tracking.ts
6228
6298
  /**
6229
6299
  * TrackingExporter base class for vendor-specific observability exporters.
6230
6300
  *
@@ -7305,8 +7375,7 @@ var TrackingExporter = class extends BaseExporter {
7305
7375
  await super.shutdown();
7306
7376
  }
7307
7377
  };
7308
- //#endregion
7309
- //#region src/exporters/span-formatters.ts
7378
+ // src/exporters/span-formatters.ts
7310
7379
  /**
7311
7380
  * Chains multiple span formatters into a single formatter.
7312
7381
  *
@@ -7343,8 +7412,7 @@ function chainFormatters(formatters) {
7343
7412
  return currentSpan;
7344
7413
  };
7345
7414
  }
7346
- //#endregion
7347
- //#region src/exporters/auth-failure-cooldown.ts
7415
+ // src/exporters/auth-failure-cooldown.ts
7348
7416
  const AUTH_FAILURE_STATUSES = /* @__PURE__ */ new Set([401, 403]);
7349
7417
  const AUTH_COOLDOWN_BASE_MS = 6e4;
7350
7418
  const AUTH_COOLDOWN_MAX_MS = 9e5;
@@ -7425,8 +7493,7 @@ var AuthFailureCooldown = class {
7425
7493
  });
7426
7494
  }
7427
7495
  };
7428
- //#endregion
7429
- //#region src/exporters/cloud.ts
7496
+ // src/exporters/cloud.ts
7430
7497
  const SIGNAL_PUBLISH_SUFFIXES$1 = {
7431
7498
  traces: "/spans/publish",
7432
7499
  logs: "/logs/publish",
@@ -7846,8 +7913,7 @@ var CloudExporter = class extends BaseExporter {
7846
7913
  this.logger.info("CloudExporter shutdown complete");
7847
7914
  }
7848
7915
  };
7849
- //#endregion
7850
- //#region src/exporters/console.ts
7916
+ // src/exporters/console.ts
7851
7917
  var ConsoleExporter = class extends BaseExporter {
7852
7918
  name = "tracing-console-exporter";
7853
7919
  constructor(config = {}) {
@@ -7910,8 +7976,7 @@ var ConsoleExporter = class extends BaseExporter {
7910
7976
  this.logger.info("ConsoleExporter shutdown");
7911
7977
  }
7912
7978
  };
7913
- //#endregion
7914
- //#region src/exporters/mastra-platform.ts
7979
+ // src/exporters/mastra-platform.ts
7915
7980
  const SIGNAL_PUBLISH_SUFFIXES = {
7916
7981
  traces: "/spans/publish",
7917
7982
  logs: "/logs/publish",
@@ -8142,7 +8207,7 @@ var MastraPlatformExporter = class extends BaseExporter {
8142
8207
  }
8143
8208
  formatSpan(span) {
8144
8209
  return {
8145
- ...span,
8210
+ ...withSpanPreviews(span),
8146
8211
  spanId: span.id,
8147
8212
  spanType: span.type,
8148
8213
  startedAt: span.startTime,
@@ -8442,8 +8507,7 @@ var MastraPlatformExporter = class extends BaseExporter {
8442
8507
  this.logger.info("MastraPlatformExporter shutdown complete");
8443
8508
  }
8444
8509
  };
8445
- //#endregion
8446
- //#region src/exporters/test.ts
8510
+ // src/exporters/test.ts
8447
8511
  /**
8448
8512
  * Test Exporter for Observability
8449
8513
  *
@@ -9489,8 +9553,7 @@ var TestExporter = class extends BaseExporter {
9489
9553
  * @deprecated Use `TestExporter` instead. This alias will be removed in a future version.
9490
9554
  */
9491
9555
  const JsonExporter = TestExporter;
9492
- //#endregion
9493
- //#region src/instances/default.ts
9556
+ // src/instances/default.ts
9494
9557
  var DefaultObservabilityInstance = class extends BaseObservabilityInstance {
9495
9558
  constructor(config) {
9496
9559
  super(config);
@@ -9499,8 +9562,7 @@ var DefaultObservabilityInstance = class extends BaseObservabilityInstance {
9499
9562
  return new DefaultSpan(options, this);
9500
9563
  }
9501
9564
  };
9502
- //#endregion
9503
- //#region src/recorded.ts
9565
+ // src/recorded.ts
9504
9566
  function nullToUndefined(value) {
9505
9567
  return value ?? void 0;
9506
9568
  }
@@ -9804,8 +9866,7 @@ function hydrateRecordedTrace(args) {
9804
9866
  debugRecordedAnnotationUnavailable
9805
9867
  });
9806
9868
  }
9807
- //#endregion
9808
- //#region src/registry.ts
9869
+ // src/registry.ts
9809
9870
  /**
9810
9871
  * Registry for Observability instances.
9811
9872
  */
@@ -9894,8 +9955,7 @@ var ObservabilityRegistry = class {
9894
9955
  return new Map(this.#instances);
9895
9956
  }
9896
9957
  };
9897
- //#endregion
9898
- //#region src/span_processors/sensitive-data-filter.ts
9958
+ // src/span_processors/sensitive-data-filter.ts
9899
9959
  /**
9900
9960
  * Maximum number of traces to keep indexed-redaction state for.
9901
9961
  * Spans never signal trace completion, so state is evicted least-recently-used.
@@ -10105,8 +10165,7 @@ var SensitiveDataFilter = class {
10105
10165
  this.traceStates.clear();
10106
10166
  }
10107
10167
  };
10108
- //#endregion
10109
- //#region src/default.ts
10168
+ // src/default.ts
10110
10169
  /**
10111
10170
  * Type guard to check if an object is a BaseObservability instance
10112
10171
  */
@@ -10441,8 +10500,7 @@ var Observability = class extends _mastra_core_base.MastraBase {
10441
10500
  if (handlerResults.length > 0) await Promise.allSettled(handlerResults);
10442
10501
  }
10443
10502
  };
10444
- //#endregion
10445
- //#region src/features.ts
10503
+ // src/features.ts
10446
10504
  /**
10447
10505
  * Feature flags for @mastra/observability
10448
10506
  *
@@ -10481,8 +10539,7 @@ var Observability = class extends _mastra_core_base.MastraBase {
10481
10539
  * ```
10482
10540
  */
10483
10541
  const observabilityFeatures = /* @__PURE__ */ new Set(["model-inference-span", "internal-usage-rollup"]);
10484
- //#endregion
10485
- //#region src/tracing-options.ts
10542
+ // src/tracing-options.ts
10486
10543
  /**
10487
10544
  * Builds TracingOptions by composing one or more updater functions.
10488
10545
  *
@@ -10503,7 +10560,6 @@ const observabilityFeatures = /* @__PURE__ */ new Set(["model-inference-span", "
10503
10560
  function buildTracingOptions(...updaters) {
10504
10561
  return updaters.reduce((opts, updater) => updater(opts), {});
10505
10562
  }
10506
- //#endregion
10507
10563
  exports.BaseExporter = BaseExporter;
10508
10564
  exports.BaseObservabilityEventBus = BaseObservabilityEventBus;
10509
10565
  exports.BaseObservabilityInstance = BaseObservabilityInstance;