@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.js CHANGED
@@ -2,6 +2,7 @@ import { createRequire } from "module";
2
2
  import { MastraBase } from "@mastra/core/base";
3
3
  import { ErrorCategory, ErrorDomain, MastraError } from "@mastra/core/error";
4
4
  import { ConsoleLogger, LogLevel, RegisteredLogger } from "@mastra/core/logger";
5
+ import * as coreObservability from "@mastra/core/observability";
5
6
  import { DEFAULT_BLOCKED_LABELS, EntityType, InternalSpans, SpanType, TracingEventType, noOpLoggerContext } from "@mastra/core/observability";
6
7
  import { fetchWithRetry, getNestedValue, setNestedValue } from "@mastra/core/utils";
7
8
  import { z } from "zod/v4";
@@ -12,7 +13,7 @@ import { TransformStream } from "stream/web";
12
13
  import { coreFeatures } from "@mastra/core/features";
13
14
  import { buildCreateSpanRecord, buildFeedbackRecord, buildLogRecord, buildMetricRecord, buildScoreRecord, buildUpdateSpanRecord } from "@mastra/core/storage";
14
15
  import { createHash } from "crypto";
15
- //#region src/bus/route-event.ts
16
+ // src/bus/route-event.ts
16
17
  /**
17
18
  * Route a single event to the appropriate method on a handler.
18
19
  *
@@ -79,8 +80,7 @@ function catchAsyncResult(result, handlerName, signal, logger) {
79
80
  logger.error(`[Observability] ${signal} handler error [handler=${handlerName}]:`, err);
80
81
  });
81
82
  }
82
- //#endregion
83
- //#region src/bus/base.ts
83
+ // src/bus/base.ts
84
84
  /**
85
85
  * BaseObservabilityEventBus - Generic event bus for observability events.
86
86
  *
@@ -155,8 +155,7 @@ var BaseObservabilityEventBus = class BaseObservabilityEventBus extends MastraBa
155
155
  this.subscribers.clear();
156
156
  }
157
157
  };
158
- //#endregion
159
- //#region src/spans/serialization.ts
158
+ // src/spans/serialization.ts
160
159
  /**
161
160
  * Bounded serialization utilities for AI tracing.
162
161
  *
@@ -522,8 +521,7 @@ function deepClean(value, options = DEFAULT_DEEP_CLEAN_OPTIONS) {
522
521
  }
523
522
  return helper(value, 0);
524
523
  }
525
- //#endregion
526
- //#region src/bus/observability-bus.ts
524
+ // src/bus/observability-bus.ts
527
525
  /**
528
526
  * Apply deepClean() to non-tracing observability events. Tracing events are
529
527
  * already deep-cleaned at span construction time (see spans/base.ts and
@@ -732,8 +730,7 @@ var ObservabilityBus = class extends BaseObservabilityEventBus {
732
730
  await super.shutdown();
733
731
  }
734
732
  };
735
- //#endregion
736
- //#region src/config.ts
733
+ // src/config.ts
737
734
  /**
738
735
  * Sampling strategy types
739
736
  */
@@ -860,8 +857,7 @@ const observabilityRegistryConfigSchema = z.object({
860
857
  }
861
858
  return true;
862
859
  }, { message: "A \"configSelector\" requires at least one config or default observability to be configured." });
863
- //#endregion
864
- //#region src/ids.ts
860
+ // src/ids.ts
865
861
  /**
866
862
  * Signal and span id helpers.
867
863
  *
@@ -929,8 +925,7 @@ function resolveExportedSpanId(span) {
929
925
  if (!span) return void 0;
930
926
  return typeof span.getExportedSpanId === "function" ? span.getExportedSpanId() : span.id;
931
927
  }
932
- //#endregion
933
- //#region src/context/logger.ts
928
+ // src/context/logger.ts
934
929
  /** Numeric priority used to compare log levels against the configured minimum. */
935
930
  const LOG_LEVEL_PRIORITY = {
936
931
  debug: 0,
@@ -998,8 +993,7 @@ var LoggerContextImpl = class {
998
993
  this.config.observabilityBus.emit(event);
999
994
  }
1000
995
  };
1001
- //#endregion
1002
- //#region src/context/metrics.ts
996
+ // src/context/metrics.ts
1003
997
  /**
1004
998
  * User-facing metric emission API. All metrics are routed through
1005
999
  * ObservabilityBus.emit() after validation and cardinality filtering.
@@ -1074,14 +1068,12 @@ function cloneCostContext(costContext) {
1074
1068
  costMetadata: costContext.costMetadata ? structuredClone(costContext.costMetadata) : void 0
1075
1069
  };
1076
1070
  }
1077
- //#endregion
1078
- //#region src/model-id.ts
1071
+ // src/model-id.ts
1079
1072
  /** Return the first model id that contains non-whitespace characters. */
1080
1073
  function resolveModelId(...candidates) {
1081
1074
  return candidates.find((candidate) => candidate?.trim());
1082
1075
  }
1083
- //#endregion
1084
- //#region src/metrics/pricing-model.ts
1076
+ // src/metrics/pricing-model.ts
1085
1077
  var PricingTier = class {
1086
1078
  index;
1087
1079
  when;
@@ -1139,8 +1131,7 @@ var PricingModel = class {
1139
1131
  return this.tiers[0] ?? null;
1140
1132
  }
1141
1133
  };
1142
- //#endregion
1143
- //#region src/metrics/models-dev.ts
1134
+ // src/metrics/models-dev.ts
1144
1135
  /** Same catalog the model router reads in `@mastra/core` (`gateways/models-dev.ts`). */
1145
1136
  const MODELS_DEV_API_URL = "https://models.dev/api.json";
1146
1137
  /**
@@ -1271,8 +1262,7 @@ function sortedEntries(value) {
1271
1262
  function isRecord(value) {
1272
1263
  return typeof value === "object" && value !== null && !Array.isArray(value);
1273
1264
  }
1274
- //#endregion
1275
- //#region src/metrics/pricing-registry.ts
1265
+ // src/metrics/pricing-registry.ts
1276
1266
  const DATA_FILE_NAME = "pricing-data.json";
1277
1267
  const REFRESH_INTERVAL_MS = 864e5;
1278
1268
  const MIN_REFRESH_ATTEMPT_INTERVAL_MS = 36e5;
@@ -1623,8 +1613,7 @@ function stripDateSuffix(model) {
1623
1613
  stripped = model.replace(/-\d{2}-20\d{2}$/, "");
1624
1614
  return stripped;
1625
1615
  }
1626
- //#endregion
1627
- //#region src/metrics/types.ts
1616
+ // src/metrics/types.ts
1628
1617
  const PricingMeter = {
1629
1618
  INPUT_TOKENS: "input_tokens",
1630
1619
  INPUT_AUDIO_TOKENS: "input_audio_tokens",
@@ -1660,8 +1649,7 @@ const TokenMetrics = {
1660
1649
  OUTPUT_AUDIO: "mastra_model_output_audio_tokens",
1661
1650
  OUTPUT_IMAGE: "mastra_model_output_image_tokens"
1662
1651
  };
1663
- //#endregion
1664
- //#region src/metrics/usage-metrics.ts
1652
+ // src/metrics/usage-metrics.ts
1665
1653
  function getTokenMetricSamples(usage) {
1666
1654
  const samples = [];
1667
1655
  const pushIfDefined = (name, value) => {
@@ -1695,8 +1683,7 @@ function getTokenMetricSamples(usage) {
1695
1683
  }
1696
1684
  return samples;
1697
1685
  }
1698
- //#endregion
1699
- //#region src/metrics/estimator.ts
1686
+ // src/metrics/estimator.ts
1700
1687
  function estimateCosts(args, pricingRegistry = PricingRegistry.getGlobal()) {
1701
1688
  const { provider, model, usage } = args;
1702
1689
  const results = /* @__PURE__ */ new Map();
@@ -1928,8 +1915,7 @@ function estimateCostForMeter(args) {
1928
1915
  }
1929
1916
  };
1930
1917
  }
1931
- //#endregion
1932
- //#region src/metrics/auto-extract.ts
1918
+ // src/metrics/auto-extract.ts
1933
1919
  /**
1934
1920
  * Emits metrics derived from live spans.
1935
1921
  */
@@ -2083,8 +2069,7 @@ function getDurationMetricName(span) {
2083
2069
  default: return null;
2084
2070
  }
2085
2071
  }
2086
- //#endregion
2087
- //#region src/metrics/cardinality.ts
2072
+ // src/metrics/cardinality.ts
2088
2073
  /** Matches standard UUID v4 strings (case-insensitive). */
2089
2074
  const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
2090
2075
  var CardinalityFilter = class {
@@ -2115,8 +2100,7 @@ var CardinalityFilter = class {
2115
2100
  return filtered;
2116
2101
  }
2117
2102
  };
2118
- //#endregion
2119
- //#region src/usage.ts
2103
+ // src/usage.ts
2120
2104
  /**
2121
2105
  * For non-BYOK requests, `usage.cost` is the complete OpenRouter charge and
2122
2106
  * `usage.costDetails.upstreamInferenceCost` is only its upstream breakdown. For BYOK requests,
@@ -2301,8 +2285,7 @@ function addUsageStats(a, b) {
2301
2285
  outputDetails: mergeOutputDetails(a.outputDetails, b.outputDetails)
2302
2286
  };
2303
2287
  }
2304
- //#endregion
2305
- //#region src/model-tracing.ts
2288
+ // src/model-tracing.ts
2306
2289
  /**
2307
2290
  * Model Span Tracing
2308
2291
  *
@@ -2730,8 +2713,20 @@ var ModelSpanTracker = class {
2730
2713
  * feature flag, or when called without an active step span. Auto-invoked from
2731
2714
  * chunk handlers as a safety net; explicit callers get the most accurate
2732
2715
  * start time.
2716
+ *
2717
+ * `payload.modelId` / `payload.provider` name the model the caller is about
2718
+ * to invoke and are recorded on MODEL_STEP, also when a fallback attempt
2719
+ * reuses the step's open inference span. The safety net never records them:
2720
+ * a replayed step-start chunk names the model of the original call.
2733
2721
  */
2734
2722
  startInference(payload) {
2723
+ if (payload?.modelId && payload.provider) this.#currentStepSpan?.update({ attributes: {
2724
+ model: payload.modelId,
2725
+ provider: payload.provider
2726
+ } });
2727
+ this.#openInference(payload);
2728
+ }
2729
+ #openInference(payload) {
2735
2730
  if (!supportsModelInference()) return;
2736
2731
  if (!this.#currentStepSpan || this.#currentInferenceSpan) return;
2737
2732
  this.#inferenceCompletionStartTime = void 0;
@@ -2856,7 +2851,7 @@ var ModelSpanTracker = class {
2856
2851
  */
2857
2852
  #ensureStepAndInference() {
2858
2853
  if (!this.#currentStepSpan) this.startStep();
2859
- if (!this.#currentInferenceSpan) this.startInference();
2854
+ if (!this.#currentInferenceSpan) this.#openInference();
2860
2855
  }
2861
2856
  /**
2862
2857
  * Create a new chunk span (for multi-part chunks like text-start/delta/end)
@@ -3056,7 +3051,7 @@ var ModelSpanTracker = class {
3056
3051
  this.#inModelResponse = true;
3057
3052
  if (this.#currentStepSpan) this.updateStep(chunk.payload);
3058
3053
  else this.startStep(chunk.payload);
3059
- if (!this.#currentInferenceSpan) this.startInference(chunk.payload);
3054
+ if (!this.#currentInferenceSpan) this.#openInference(chunk.payload);
3060
3055
  break;
3061
3056
  case "step-finish":
3062
3057
  this.#inModelResponse = false;
@@ -3102,8 +3097,7 @@ var ModelSpanTracker = class {
3102
3097
  } }));
3103
3098
  }
3104
3099
  };
3105
- //#endregion
3106
- //#region src/spans/metadata.ts
3100
+ // src/spans/metadata.ts
3107
3101
  /**
3108
3102
  * Shared span-metadata helpers.
3109
3103
  *
@@ -3170,8 +3164,7 @@ function mergeMetadata$1(parentMetadata, metadata) {
3170
3164
  return metadata;
3171
3165
  }
3172
3166
  }
3173
- //#endregion
3174
- //#region src/spans/base.ts
3167
+ // src/spans/base.ts
3175
3168
  const LINEAGE_KEYS = [
3176
3169
  "parentEntityType",
3177
3170
  "parentEntityId",
@@ -3182,10 +3175,10 @@ const LINEAGE_KEYS = [
3182
3175
  "rootEntityName",
3183
3176
  "rootEntityVersionId"
3184
3177
  ];
3185
- /** Copy only the lineage fields that have values, so spans don't carry undefined keys */
3186
3178
  function ownEntityVersionId(span) {
3187
3179
  return span instanceof BaseSpan ? span.ownEntityVersionId : void 0;
3188
3180
  }
3181
+ /** Copy only the lineage fields that have values, so spans don't carry undefined keys */
3189
3182
  function definedLineage(source) {
3190
3183
  const lineage = {};
3191
3184
  for (const key of LINEAGE_KEYS) {
@@ -3264,6 +3257,19 @@ function getExternalParentId(options) {
3264
3257
  if (parent.isInternal || parent.isExcluded) return parent.getParentSpanId(false);
3265
3258
  return parent.id;
3266
3259
  }
3260
+ /**
3261
+ * Vercel AI Gateway reports which provider served a step, and any provider
3262
+ * attempts that failed first, only in `providerMetadata.gateway.routing`.
3263
+ * Read it defensively so a throwing getter never skips the strip below.
3264
+ */
3265
+ function readGatewayRouting(providerMetadata) {
3266
+ try {
3267
+ const gateway = isPlainRecord(providerMetadata) ? providerMetadata.gateway : void 0;
3268
+ return isPlainRecord(gateway) ? gateway.routing : void 0;
3269
+ } catch {
3270
+ return;
3271
+ }
3272
+ }
3267
3273
  var BaseSpan = class BaseSpan {
3268
3274
  name;
3269
3275
  type;
@@ -3391,8 +3397,10 @@ var BaseSpan = class BaseSpan {
3391
3397
  if (this.type !== SpanType.MODEL_STEP) return value;
3392
3398
  try {
3393
3399
  const prepared = { ...value };
3400
+ const gatewayRouting = readGatewayRouting(prepared.providerMetadata);
3394
3401
  delete prepared.providerMetadata;
3395
3402
  delete prepared.experimental_providerMetadata;
3403
+ if (gatewayRouting !== void 0) prepared.providerMetadata = { gateway: { routing: gatewayRouting } };
3396
3404
  return prepared;
3397
3405
  } catch {
3398
3406
  return value;
@@ -3578,7 +3586,7 @@ var BaseSpan = class BaseSpan {
3578
3586
  entityType: this.entityType,
3579
3587
  entityId: this.entityId,
3580
3588
  entityName: this.entityName,
3581
- entityVersionId: getMetadataString("entityVersionId"),
3589
+ entityVersionId: this.ownEntityVersionId,
3582
3590
  ...parentLineage,
3583
3591
  ...rootLineage,
3584
3592
  userId: getMetadataString("userId"),
@@ -3621,6 +3629,7 @@ var BaseSpan = class BaseSpan {
3621
3629
  isEvent: this.isEvent,
3622
3630
  isRootSpan: this.isRootSpan,
3623
3631
  ...this.isInternal ? { isInternal: true } : {},
3632
+ ...correlation.entityVersionId ? { entityVersionId: correlation.entityVersionId } : {},
3624
3633
  ...definedLineage(correlation),
3625
3634
  parentSpanId: this.getParentSpanId(includeInternalSpans),
3626
3635
  externalParentSpanId: this.getExportedExternalParentSpanId(includeInternalSpans),
@@ -3665,8 +3674,7 @@ var BaseSpan = class BaseSpan {
3665
3674
  return fn();
3666
3675
  }
3667
3676
  };
3668
- //#endregion
3669
- //#region src/spans/default.ts
3677
+ // src/spans/default.ts
3670
3678
  /** Every AI SDK `APICallError` (v4, v5 and v6) carries this shared marker symbol. */
3671
3679
  const API_CALL_ERROR_MARKER = Symbol.for("vercel.ai.error.AI_APICallError");
3672
3680
  function isApiCallError(value) {
@@ -3857,8 +3865,7 @@ function getOrCreateTraceId(options) {
3857
3865
  }
3858
3866
  return generateTraceId();
3859
3867
  }
3860
- //#endregion
3861
- //#region src/spans/no-op.ts
3868
+ // src/spans/no-op.ts
3862
3869
  var NoOpSpan = class extends BaseSpan {
3863
3870
  id;
3864
3871
  traceId;
@@ -3877,8 +3884,7 @@ var NoOpSpan = class extends BaseSpan {
3877
3884
  return true;
3878
3885
  }
3879
3886
  };
3880
- //#endregion
3881
- //#region src/exporters/base.ts
3887
+ // src/exporters/base.ts
3882
3888
  /**
3883
3889
  * Base Exporter for Observability
3884
3890
  *
@@ -4040,8 +4046,7 @@ var BaseExporter = class {
4040
4046
  this.logger.info(`${this.name} shutdown complete`);
4041
4047
  }
4042
4048
  };
4043
- //#endregion
4044
- //#region src/exporters/event-buffer.ts
4049
+ // src/exporters/event-buffer.ts
4045
4050
  /**
4046
4051
  * Buffers observability events (creates and updates) for batch flushing.
4047
4052
  * Handles strategy-aware routing of tracing events and tracks created spans
@@ -4052,6 +4057,8 @@ var EventBuffer = class {
4052
4057
  #creates = [];
4053
4058
  #updates = [];
4054
4059
  #allCreatedSpans = /* @__PURE__ */ new Set();
4060
+ /** Spans whose SPAN_STARTED this buffer has seen but whose create isn't confirmed yet */
4061
+ #pendingCreateSpans = /* @__PURE__ */ new Set();
4055
4062
  #firstEventTime;
4056
4063
  #storageStrategy;
4057
4064
  #maxRetries;
@@ -4102,8 +4109,11 @@ var EventBuffer = class {
4102
4109
  case TracingEventType.SPAN_STARTED:
4103
4110
  switch (this.#storageStrategy) {
4104
4111
  case "realtime":
4105
- case "event-sourced":
4106
- case "batch-with-updates": this.pushCreate(event);
4112
+ case "batch-with-updates":
4113
+ if (!event.exportedSpan.isEvent) this.#pendingCreateSpans.add(this.exportedSpanKey(event.exportedSpan));
4114
+ this.pushCreate(event);
4115
+ break;
4116
+ case "event-sourced": this.pushCreate(event);
4107
4117
  }
4108
4118
  break;
4109
4119
  case TracingEventType.SPAN_UPDATED:
@@ -4172,28 +4182,44 @@ var EventBuffer = class {
4172
4182
  buildSpanKey(span) {
4173
4183
  return `${span.traceId}:${span.spanId}`;
4174
4184
  }
4185
+ exportedSpanKey(span) {
4186
+ return this.buildSpanKey({
4187
+ traceId: span.traceId,
4188
+ spanId: span.id
4189
+ });
4190
+ }
4175
4191
  /** Track successfully created spans so updates can verify span existence before flushing. */
4176
4192
  addCreatedSpans(args) {
4177
4193
  if (this.#storageStrategy === "event-sourced" || this.#storageStrategy === "insert-only") return;
4178
- for (const createRecord of args.records) if (!createRecord.isEvent) this.#allCreatedSpans.add(this.buildSpanKey(createRecord));
4194
+ for (const createRecord of args.records) if (!createRecord.isEvent) {
4195
+ const key = this.buildSpanKey(createRecord);
4196
+ this.#pendingCreateSpans.delete(key);
4197
+ this.#allCreatedSpans.add(key);
4198
+ }
4199
+ }
4200
+ /** Stop waiting on creates that were dropped, so their updates aren't held back for them. */
4201
+ forgetDroppedCreates(events) {
4202
+ for (const event of events) if (event.type === TracingEventType.SPAN_STARTED) this.#pendingCreateSpans.delete(this.exportedSpanKey(event.exportedSpan));
4179
4203
  }
4180
4204
  /** Check whether a span's create record has already been flushed to storage. */
4181
4205
  spanExists(span) {
4182
- return this.#allCreatedSpans?.has(this.buildSpanKey({
4183
- traceId: span.traceId,
4184
- spanId: span.id
4185
- }));
4206
+ return this.#allCreatedSpans.has(this.exportedSpanKey(span));
4207
+ }
4208
+ /** Check whether this buffer saw the span's SPAN_STARTED but its create hasn't been confirmed yet. */
4209
+ isAwaitingCreate(span) {
4210
+ return this.#pendingCreateSpans.has(this.exportedSpanKey(span));
4186
4211
  }
4187
4212
  /** Remove completed spans from tracking after their SPAN_ENDED updates are flushed. */
4188
4213
  endFinishedSpans(args) {
4189
4214
  if (this.#storageStrategy === "event-sourced" || this.#storageStrategy === "insert-only") return;
4190
4215
  args.records.forEach((r) => {
4191
- this.#allCreatedSpans.delete(this.buildSpanKey(r));
4216
+ const key = this.buildSpanKey(r);
4217
+ this.#allCreatedSpans.delete(key);
4218
+ this.#pendingCreateSpans.delete(key);
4192
4219
  });
4193
4220
  }
4194
4221
  };
4195
- //#endregion
4196
- //#region src/exporters/default.ts
4222
+ // src/exporters/default.ts
4197
4223
  /**
4198
4224
  * Resolves the final tracing storage strategy based on config and observability store hints
4199
4225
  */
@@ -4631,8 +4657,22 @@ var DefaultExporter = class extends BaseExporter {
4631
4657
  this.logger.info("DefaultExporter shutdown complete");
4632
4658
  }
4633
4659
  };
4634
- //#endregion
4635
- //#region src/exporters/mastra-storage.ts
4660
+ // src/exporters/span-previews.ts
4661
+ const buildSpanPreviews = coreObservability.buildSpanPreviews;
4662
+ /** Older core versions retain their existing storage preview fallback. */
4663
+ function withSpanPreviews(span) {
4664
+ if (!coreFeatures.has("observability-span-previews")) return span;
4665
+ try {
4666
+ const previews = buildSpanPreviews?.(span);
4667
+ return previews ? {
4668
+ ...span,
4669
+ ...previews
4670
+ } : span;
4671
+ } catch {
4672
+ return span;
4673
+ }
4674
+ }
4675
+ // src/exporters/mastra-storage.ts
4636
4676
  /**
4637
4677
  * Resolves the final tracing storage strategy based on config and observability store hints
4638
4678
  */
@@ -4835,6 +4875,7 @@ var MastraStorageExporter = class extends BaseExporter {
4835
4875
  return { failed: true };
4836
4876
  }
4837
4877
  const dropped = this.#eventBuffer.reAddCreates(events);
4878
+ this.#eventBuffer.forgetDroppedCreates(dropped);
4838
4879
  const retryAttempt = this.retryAttempt(events);
4839
4880
  this.logStorageFailure(signal, events, retryAttempt, error);
4840
4881
  this.emitDrop(signal, "retry-exhausted", dropped.length, error);
@@ -4845,7 +4886,15 @@ var MastraStorageExporter = class extends BaseExporter {
4845
4886
  }
4846
4887
  }
4847
4888
  /**
4848
- * Flush span update/end events, deferring any whose span hasn't been created yet.
4889
+ * Flush span update/end events.
4890
+ *
4891
+ * When the store supports conditional updates, only spans this exporter started
4892
+ * and hasn't confirmed the create for are deferred. Everything else, including
4893
+ * spans another exporter instance started (such as a durable agent ending spans
4894
+ * it rebuilt after a crash), goes straight to storage, and updates that didn't
4895
+ * apply are counted as drops. Without conditional updates, updates for spans this
4896
+ * exporter hasn't created are deferred until their create is flushed.
4897
+ *
4849
4898
  * When `isEnd` is true, successfully flushed spans are removed from tracking.
4850
4899
  */
4851
4900
  async flushSpanUpdates(events, deferredUpdates, isEnd) {
@@ -4855,10 +4904,12 @@ var MastraStorageExporter = class extends BaseExporter {
4855
4904
  this.emitDrop("tracing", "unsupported-storage", events.length);
4856
4905
  return { failed: true };
4857
4906
  }
4907
+ const storage = this.#observabilityStorage;
4908
+ const batchUpdateUnendedSpans = typeof storage.batchUpdateUnendedSpans === "function" ? storage.batchUpdateUnendedSpans.bind(storage) : void 0;
4858
4909
  const partials = [];
4859
4910
  for (const event of events) {
4860
4911
  const span = event.exportedSpan;
4861
- if (this.#eventBuffer.spanExists(span)) partials.push({
4912
+ if (batchUpdateUnendedSpans ? !this.#eventBuffer.isAwaitingCreate(span) : this.#eventBuffer.spanExists(span)) partials.push({
4862
4913
  traceId: span.traceId,
4863
4914
  spanId: span.id,
4864
4915
  updates: buildUpdateSpanRecord(span)
@@ -4867,7 +4918,10 @@ var MastraStorageExporter = class extends BaseExporter {
4867
4918
  }
4868
4919
  if (partials.length === 0) return FLUSH_SUCCEEDED;
4869
4920
  try {
4870
- await this.#observabilityStorage.batchUpdateSpans({ records: partials });
4921
+ if (batchUpdateUnendedSpans) {
4922
+ const { updated } = await batchUpdateUnendedSpans({ records: partials });
4923
+ this.emitNotApplied(partials, updated);
4924
+ } else await storage.batchUpdateSpans({ records: partials });
4871
4925
  if (isEnd) this.#eventBuffer.endFinishedSpans({ records: partials });
4872
4926
  return FLUSH_SUCCEEDED;
4873
4927
  } catch (error) {
@@ -4891,6 +4945,27 @@ var MastraStorageExporter = class extends BaseExporter {
4891
4945
  }
4892
4946
  }
4893
4947
  /**
4948
+ * Reports conditional span updates that storage didn't apply: the span wasn't
4949
+ * stored, or it had already ended. Ended spans are immutable, so these are dropped.
4950
+ */
4951
+ emitNotApplied(partials, updated) {
4952
+ const appliedCounts = /* @__PURE__ */ new Map();
4953
+ for (const span of updated) {
4954
+ const key = `${span.traceId}:${span.spanId}`;
4955
+ appliedCounts.set(key, (appliedCounts.get(key) ?? 0) + 1);
4956
+ }
4957
+ let notApplied = 0;
4958
+ for (const partial of partials) {
4959
+ const key = `${partial.traceId}:${partial.spanId}`;
4960
+ const remaining = appliedCounts.get(key) ?? 0;
4961
+ if (remaining > 0) appliedCounts.set(key, remaining - 1);
4962
+ else notApplied++;
4963
+ }
4964
+ if (notApplied === 0) return;
4965
+ this.logger.debug("Span updates not applied: span not stored or already ended", { count: notApplied });
4966
+ this.emitDrop("tracing", "span-not-updatable", notApplied);
4967
+ }
4968
+ /**
4894
4969
  * Runs flushBuffer() and tracks its promise so flush() can wait for writes
4895
4970
  * that a timer or size trigger started on their own.
4896
4971
  */
@@ -5000,7 +5075,10 @@ var MastraStorageExporter = class extends BaseExporter {
5000
5075
  this.logger.debug("Cannot store traces. Observability storage is not initialized");
5001
5076
  return;
5002
5077
  }
5003
- this.#eventBuffer.addEvent(event);
5078
+ this.#eventBuffer.addEvent({
5079
+ ...event,
5080
+ exportedSpan: withSpanPreviews(event.exportedSpan)
5081
+ });
5004
5082
  await this.handleBatchedFlush();
5005
5083
  }
5006
5084
  /**
@@ -5069,8 +5147,7 @@ var MastraStorageExporter = class extends BaseExporter {
5069
5147
  this.logger.info("MastraStorageExporter shutdown complete");
5070
5148
  }
5071
5149
  };
5072
- //#endregion
5073
- //#region src/instances/platform-policy.ts
5150
+ // src/instances/platform-policy.ts
5074
5151
  const MASTRA_BUILT_IN_STORAGE_EXPORTER_NAMES = /* @__PURE__ */ new Set(["mastra-storage-exporter", "mastra-default-observability-exporter"]);
5075
5152
  function isMastraPlatformDeployment() {
5076
5153
  return Boolean(process.env.MASTRA_DEPLOYMENT_ID);
@@ -5078,8 +5155,7 @@ function isMastraPlatformDeployment() {
5078
5155
  function isMastraBuiltInStorageExporter(exporter) {
5079
5156
  return exporter instanceof MastraStorageExporter || exporter instanceof DefaultExporter || MASTRA_BUILT_IN_STORAGE_EXPORTER_NAMES.has(exporter.name);
5080
5157
  }
5081
- //#endregion
5082
- //#region src/instances/base.ts
5158
+ // src/instances/base.ts
5083
5159
  /**
5084
5160
  * BaseObservability - Abstract base class for Observability implementations
5085
5161
  */
@@ -5792,13 +5868,11 @@ var BaseObservabilityInstance = class extends MastraBase {
5792
5868
  this.logger.info(`[Observability] Shutdown completed [name=${this.name}]`);
5793
5869
  }
5794
5870
  };
5795
- //#endregion
5796
- //#region src/client/id.ts
5871
+ // src/client/id.ts
5797
5872
  function generateClientSignalId() {
5798
5873
  return globalThis.crypto.randomUUID();
5799
5874
  }
5800
- //#endregion
5801
- //#region src/client/otlp.ts
5875
+ // src/client/otlp.ts
5802
5876
  /**
5803
5877
  * Hand-rolled OTLP/JSON decoder for the subset we need to ingest from
5804
5878
  * client-side tool execution.
@@ -5986,8 +6060,7 @@ function buildExportedLog(decoded) {
5986
6060
  data: decoded.attributes
5987
6061
  };
5988
6062
  }
5989
- //#endregion
5990
- //#region src/client/w3c.ts
6063
+ // src/client/w3c.ts
5991
6064
  /**
5992
6065
  * Hand-rolled W3C Trace Context and Baggage propagation.
5993
6066
  *
@@ -6058,8 +6131,7 @@ function formatBaggage(entries) {
6058
6131
  }
6059
6132
  return parts.join(",");
6060
6133
  }
6061
- //#endregion
6062
- //#region src/client/proxy.ts
6134
+ // src/client/proxy.ts
6063
6135
  const DEFAULT_LIMITS = {
6064
6136
  maxSpans: 10,
6065
6137
  maxLogs: 10,
@@ -6197,8 +6269,7 @@ function validateParentLinks(spans, rootParent) {
6197
6269
  function createClientObservabilityProxy(options) {
6198
6270
  return new ClientObservabilityProxyImpl(options);
6199
6271
  }
6200
- //#endregion
6201
- //#region src/exporters/tracking.ts
6272
+ // src/exporters/tracking.ts
6202
6273
  /**
6203
6274
  * TrackingExporter base class for vendor-specific observability exporters.
6204
6275
  *
@@ -7279,8 +7350,7 @@ var TrackingExporter = class extends BaseExporter {
7279
7350
  await super.shutdown();
7280
7351
  }
7281
7352
  };
7282
- //#endregion
7283
- //#region src/exporters/span-formatters.ts
7353
+ // src/exporters/span-formatters.ts
7284
7354
  /**
7285
7355
  * Chains multiple span formatters into a single formatter.
7286
7356
  *
@@ -7317,8 +7387,7 @@ function chainFormatters(formatters) {
7317
7387
  return currentSpan;
7318
7388
  };
7319
7389
  }
7320
- //#endregion
7321
- //#region src/exporters/auth-failure-cooldown.ts
7390
+ // src/exporters/auth-failure-cooldown.ts
7322
7391
  const AUTH_FAILURE_STATUSES = /* @__PURE__ */ new Set([401, 403]);
7323
7392
  const AUTH_COOLDOWN_BASE_MS = 6e4;
7324
7393
  const AUTH_COOLDOWN_MAX_MS = 9e5;
@@ -7399,8 +7468,7 @@ var AuthFailureCooldown = class {
7399
7468
  });
7400
7469
  }
7401
7470
  };
7402
- //#endregion
7403
- //#region src/exporters/cloud.ts
7471
+ // src/exporters/cloud.ts
7404
7472
  const SIGNAL_PUBLISH_SUFFIXES$1 = {
7405
7473
  traces: "/spans/publish",
7406
7474
  logs: "/logs/publish",
@@ -7820,8 +7888,7 @@ var CloudExporter = class extends BaseExporter {
7820
7888
  this.logger.info("CloudExporter shutdown complete");
7821
7889
  }
7822
7890
  };
7823
- //#endregion
7824
- //#region src/exporters/console.ts
7891
+ // src/exporters/console.ts
7825
7892
  var ConsoleExporter = class extends BaseExporter {
7826
7893
  name = "tracing-console-exporter";
7827
7894
  constructor(config = {}) {
@@ -7884,8 +7951,7 @@ var ConsoleExporter = class extends BaseExporter {
7884
7951
  this.logger.info("ConsoleExporter shutdown");
7885
7952
  }
7886
7953
  };
7887
- //#endregion
7888
- //#region src/exporters/mastra-platform.ts
7954
+ // src/exporters/mastra-platform.ts
7889
7955
  const SIGNAL_PUBLISH_SUFFIXES = {
7890
7956
  traces: "/spans/publish",
7891
7957
  logs: "/logs/publish",
@@ -8116,7 +8182,7 @@ var MastraPlatformExporter = class extends BaseExporter {
8116
8182
  }
8117
8183
  formatSpan(span) {
8118
8184
  return {
8119
- ...span,
8185
+ ...withSpanPreviews(span),
8120
8186
  spanId: span.id,
8121
8187
  spanType: span.type,
8122
8188
  startedAt: span.startTime,
@@ -8416,8 +8482,7 @@ var MastraPlatformExporter = class extends BaseExporter {
8416
8482
  this.logger.info("MastraPlatformExporter shutdown complete");
8417
8483
  }
8418
8484
  };
8419
- //#endregion
8420
- //#region src/exporters/test.ts
8485
+ // src/exporters/test.ts
8421
8486
  /**
8422
8487
  * Test Exporter for Observability
8423
8488
  *
@@ -9463,8 +9528,7 @@ var TestExporter = class extends BaseExporter {
9463
9528
  * @deprecated Use `TestExporter` instead. This alias will be removed in a future version.
9464
9529
  */
9465
9530
  const JsonExporter = TestExporter;
9466
- //#endregion
9467
- //#region src/instances/default.ts
9531
+ // src/instances/default.ts
9468
9532
  var DefaultObservabilityInstance = class extends BaseObservabilityInstance {
9469
9533
  constructor(config) {
9470
9534
  super(config);
@@ -9473,8 +9537,7 @@ var DefaultObservabilityInstance = class extends BaseObservabilityInstance {
9473
9537
  return new DefaultSpan(options, this);
9474
9538
  }
9475
9539
  };
9476
- //#endregion
9477
- //#region src/recorded.ts
9540
+ // src/recorded.ts
9478
9541
  function nullToUndefined(value) {
9479
9542
  return value ?? void 0;
9480
9543
  }
@@ -9778,8 +9841,7 @@ function hydrateRecordedTrace(args) {
9778
9841
  debugRecordedAnnotationUnavailable
9779
9842
  });
9780
9843
  }
9781
- //#endregion
9782
- //#region src/registry.ts
9844
+ // src/registry.ts
9783
9845
  /**
9784
9846
  * Registry for Observability instances.
9785
9847
  */
@@ -9868,8 +9930,7 @@ var ObservabilityRegistry = class {
9868
9930
  return new Map(this.#instances);
9869
9931
  }
9870
9932
  };
9871
- //#endregion
9872
- //#region src/span_processors/sensitive-data-filter.ts
9933
+ // src/span_processors/sensitive-data-filter.ts
9873
9934
  /**
9874
9935
  * Maximum number of traces to keep indexed-redaction state for.
9875
9936
  * Spans never signal trace completion, so state is evicted least-recently-used.
@@ -10079,8 +10140,7 @@ var SensitiveDataFilter = class {
10079
10140
  this.traceStates.clear();
10080
10141
  }
10081
10142
  };
10082
- //#endregion
10083
- //#region src/default.ts
10143
+ // src/default.ts
10084
10144
  /**
10085
10145
  * Type guard to check if an object is a BaseObservability instance
10086
10146
  */
@@ -10415,8 +10475,7 @@ var Observability = class extends MastraBase {
10415
10475
  if (handlerResults.length > 0) await Promise.allSettled(handlerResults);
10416
10476
  }
10417
10477
  };
10418
- //#endregion
10419
- //#region src/features.ts
10478
+ // src/features.ts
10420
10479
  /**
10421
10480
  * Feature flags for @mastra/observability
10422
10481
  *
@@ -10455,8 +10514,7 @@ var Observability = class extends MastraBase {
10455
10514
  * ```
10456
10515
  */
10457
10516
  const observabilityFeatures = /* @__PURE__ */ new Set(["model-inference-span", "internal-usage-rollup"]);
10458
- //#endregion
10459
- //#region src/tracing-options.ts
10517
+ // src/tracing-options.ts
10460
10518
  /**
10461
10519
  * Builds TracingOptions by composing one or more updater functions.
10462
10520
  *
@@ -10477,7 +10535,6 @@ const observabilityFeatures = /* @__PURE__ */ new Set(["model-inference-span", "
10477
10535
  function buildTracingOptions(...updaters) {
10478
10536
  return updaters.reduce((opts, updater) => updater(opts), {});
10479
10537
  }
10480
- //#endregion
10481
10538
  export { BaseExporter, BaseObservabilityEventBus, BaseObservabilityInstance, BaseSpan, CardinalityFilter, CloudExporter, ConsoleExporter, DEFAULT_DEEP_CLEAN_OPTIONS, DEFAULT_LIMITS, DefaultExporter, DefaultObservabilityInstance, DefaultSpan, JsonExporter, LoggerContextImpl, MastraPlatformExporter, MastraStorageExporter, MetricsContextImpl, ModelSpanTracker, NoOpSpan, OBSERVABILITY_CAPABILITIES_HEADER, Observability, ObservabilityBus, QUOTA_PAUSE_CAPABILITY, SamplingStrategyType, SensitiveDataFilter, TestExporter, TraceData, TrackingExporter, buildExportedLog, buildExportedSpan, buildTracingOptions, chainFormatters, createClientObservabilityProxy, decodeResourceLogs, decodeResourceSpans, deepClean, formatBaggage, formatTraceparent, getExternalParentId, isSerializedMap, mergeSerializationOptions, observabilityConfigValueSchema, observabilityFeatures, observabilityInstanceConfigSchema, observabilityRegistryConfigSchema, otlpSeverityToLogLevel, parseBaggage, parseTraceparent, reconstructSerializedMap, routeToHandler, samplingStrategySchema, serializationOptionsSchema, truncateString };
10482
10539
 
10483
10540
  //# sourceMappingURL=index.js.map