@mastra/observability 1.21.0 → 1.21.1-alpha.1

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
@@ -2193,6 +2193,9 @@ function isDefined(value) {
2193
2193
  * - Google/Gemini: cachedContentTokenCount, thoughtsTokenCount in providerMetadata.google.usageMetadata
2194
2194
  * - OpenRouter: Uses OpenAI-compatible structure (cache tokens in usage)
2195
2195
  *
2196
+ * Kept in sync with `packages/core/src/observability/usage.ts` (duplicated so exporters do not need a newer core).
2197
+ * Change both copies together; `usage.parity.test.ts` fails if they drift.
2198
+ *
2196
2199
  * @param usage - The LanguageModelV2Usage from AI SDK response
2197
2200
  * @param providerMetadata - Optional provider-specific metadata
2198
2201
  * @returns UsageStats with inputDetails and outputDetails
@@ -2638,7 +2641,7 @@ var ModelSpanTracker = class {
2638
2641
  * Returns undefined if no step span is active.
2639
2642
  */
2640
2643
  exportCurrentStep() {
2641
- return this.#currentStepSpan?.exportSpan();
2644
+ return this.#currentStepSpan?.snapshot();
2642
2645
  }
2643
2646
  /**
2644
2647
  * Get the pending step finish payload (captured when defer mode is enabled).
@@ -3169,6 +3172,28 @@ function mergeMetadata$1(parentMetadata, metadata) {
3169
3172
  }
3170
3173
  //#endregion
3171
3174
  //#region src/spans/base.ts
3175
+ const LINEAGE_KEYS = [
3176
+ "parentEntityType",
3177
+ "parentEntityId",
3178
+ "parentEntityName",
3179
+ "parentEntityVersionId",
3180
+ "rootEntityType",
3181
+ "rootEntityId",
3182
+ "rootEntityName",
3183
+ "rootEntityVersionId"
3184
+ ];
3185
+ /** Copy only the lineage fields that have values, so spans don't carry undefined keys */
3186
+ function ownEntityVersionId(span) {
3187
+ return span instanceof BaseSpan ? span.ownEntityVersionId : void 0;
3188
+ }
3189
+ function definedLineage(source) {
3190
+ const lineage = {};
3191
+ for (const key of LINEAGE_KEYS) {
3192
+ const value = source[key];
3193
+ if (value !== void 0 && value !== null) lineage[key] = value;
3194
+ }
3195
+ return lineage;
3196
+ }
3172
3197
  /**
3173
3198
  * Determines if a span type should be considered internal based on flags.
3174
3199
  * Returns false if flags are undefined.
@@ -3290,6 +3315,17 @@ var BaseSpan = class BaseSpan {
3290
3315
  isExcluded;
3291
3316
  /** Cached canonical correlation context for this live span */
3292
3317
  correlationContext;
3318
+ /**
3319
+ * Parent and root entity restored by `rebuildSpan()`. A rebuilt span has no
3320
+ * live `parent`, so its original ancestors are only known from the cached export.
3321
+ */
3322
+ restoredLineage;
3323
+ /**
3324
+ * Version declared for this span's own entity, or inherited from a parent span
3325
+ * of the same entity. Unlike `metadata.entityVersionId`, never an ancestor's
3326
+ * version for a different entity.
3327
+ */
3328
+ ownEntityVersionId;
3293
3329
  /** Child spans that have started but not yet ended */
3294
3330
  #openChildren;
3295
3331
  /** The span whose open-child set currently tracks this span: the parent, or a live ancestor after promotion */
@@ -3327,6 +3363,9 @@ var BaseSpan = class BaseSpan {
3327
3363
  this.entityType = options.entityType ?? entityParent?.entityType;
3328
3364
  this.entityId = options.entityId ?? entityParent?.entityId;
3329
3365
  this.entityName = options.entityName ?? entityParent?.entityName;
3366
+ const declaredVersion = options.metadata?.entityVersionId;
3367
+ const sameEntityParent = entityParent instanceof BaseSpan && entityParent.entityType === this.entityType && entityParent.entityId === this.entityId;
3368
+ this.ownEntityVersionId = typeof declaredVersion === "string" ? declaredVersion : sameEntityParent ? entityParent.ownEntityVersionId : void 0;
3330
3369
  if (this.isExcluded) {
3331
3370
  this.attributes = {};
3332
3371
  return;
@@ -3475,18 +3514,62 @@ var BaseSpan = class BaseSpan {
3475
3514
  current = current.parent;
3476
3515
  }
3477
3516
  }
3478
- /** Build and cache the canonical correlation context for this live span. */
3517
+ /**
3518
+ * Restore the parent and root entity a span had before it was exported and
3519
+ * rebuilt (durable execution), so it and its new children keep their lineage.
3520
+ */
3521
+ restoreEntityLineage(cached) {
3522
+ this.ownEntityVersionId = cached.ownEntityVersionId ?? void 0;
3523
+ const lineage = definedLineage(cached);
3524
+ if (Object.keys(lineage).length > 0) {
3525
+ this.restoredLineage = lineage;
3526
+ this.correlationContext = void 0;
3527
+ }
3528
+ }
3529
+ /**
3530
+ * Build and cache the canonical correlation context for this live span.
3531
+ *
3532
+ * Parent entity: the nearest ancestor that is not internal or excluded, the
3533
+ * same rule used for entity inheritance. Root entity: the top of the
3534
+ * in-process parent chain, which is not necessarily the distributed trace's
3535
+ * root when the trace continues from another service. The root walk does not
3536
+ * skip internal spans, so the root can name an entity whose span is not
3537
+ * exported. Logs, metrics, scores and feedback share these values.
3538
+ *
3539
+ * When the top of the live chain was rebuilt, its original ancestors only
3540
+ * survive in the restored lineage, so it supplies the root and, when no live
3541
+ * ancestor qualifies, the parent.
3542
+ */
3479
3543
  getCorrelationContext() {
3480
3544
  if (this.correlationContext) return this.correlationContext;
3481
3545
  const metadata = this.metadata ?? {};
3482
3546
  const getMetadataString = (key) => typeof metadata[key] === "string" ? metadata[key] : void 0;
3483
- const getSpanMetadataString = (span, key) => {
3484
- const m = span?.metadata;
3485
- return m && typeof m[key] === "string" ? m[key] : void 0;
3486
- };
3487
3547
  const parentSpan = this.getParentSpan(false);
3488
3548
  let rootSpan = this;
3489
3549
  while (rootSpan.parent) rootSpan = rootSpan.parent;
3550
+ const restored = rootSpan instanceof BaseSpan ? rootSpan.restoredLineage : void 0;
3551
+ const parentLineage = parentSpan ? {
3552
+ parentEntityType: parentSpan.entityType,
3553
+ parentEntityId: parentSpan.entityId,
3554
+ parentEntityName: parentSpan.entityName,
3555
+ parentEntityVersionId: ownEntityVersionId(parentSpan)
3556
+ } : {
3557
+ parentEntityType: restored?.parentEntityType,
3558
+ parentEntityId: restored?.parentEntityId,
3559
+ parentEntityName: restored?.parentEntityName,
3560
+ parentEntityVersionId: restored?.parentEntityVersionId
3561
+ };
3562
+ const rootLineage = restored ? {
3563
+ rootEntityType: restored.rootEntityType,
3564
+ rootEntityId: restored.rootEntityId,
3565
+ rootEntityName: restored.rootEntityName,
3566
+ rootEntityVersionId: restored.rootEntityVersionId
3567
+ } : {
3568
+ rootEntityType: rootSpan.entityType,
3569
+ rootEntityId: rootSpan.entityId,
3570
+ rootEntityName: rootSpan.entityName,
3571
+ rootEntityVersionId: ownEntityVersionId(rootSpan)
3572
+ };
3490
3573
  const rootTags = rootSpan.tags?.length ? [...rootSpan.tags] : void 0;
3491
3574
  this.correlationContext = {
3492
3575
  traceId: this.traceId,
@@ -3496,14 +3579,8 @@ var BaseSpan = class BaseSpan {
3496
3579
  entityId: this.entityId,
3497
3580
  entityName: this.entityName,
3498
3581
  entityVersionId: getMetadataString("entityVersionId"),
3499
- parentEntityType: parentSpan?.entityType,
3500
- parentEntityId: parentSpan?.entityId,
3501
- parentEntityName: parentSpan?.entityName,
3502
- parentEntityVersionId: getSpanMetadataString(parentSpan, "entityVersionId"),
3503
- rootEntityType: rootSpan.entityType,
3504
- rootEntityId: rootSpan.entityId,
3505
- rootEntityName: rootSpan.entityName,
3506
- rootEntityVersionId: getSpanMetadataString(rootSpan, "entityVersionId"),
3582
+ ...parentLineage,
3583
+ ...rootLineage,
3507
3584
  userId: getMetadataString("userId"),
3508
3585
  organizationId: getMetadataString("organizationId"),
3509
3586
  resourceId: getMetadataString("resourceId"),
@@ -3524,6 +3601,7 @@ var BaseSpan = class BaseSpan {
3524
3601
  const hideOutput = this.traceState?.hideOutput ?? false;
3525
3602
  const nestedUnderParent = this.traceState?.nestedUnderParent ?? false;
3526
3603
  const links = validLinks(this.links);
3604
+ const correlation = this.getCorrelationContext();
3527
3605
  return {
3528
3606
  id: this.id,
3529
3607
  traceId: this.traceId,
@@ -3543,6 +3621,7 @@ var BaseSpan = class BaseSpan {
3543
3621
  isEvent: this.isEvent,
3544
3622
  isRootSpan: this.isRootSpan,
3545
3623
  ...this.isInternal ? { isInternal: true } : {},
3624
+ ...definedLineage(correlation),
3546
3625
  parentSpanId: this.getParentSpanId(includeInternalSpans),
3547
3626
  externalParentSpanId: this.getExportedExternalParentSpanId(includeInternalSpans),
3548
3627
  ...nestedUnderParent ? { nestedUnderParent: true } : {},
@@ -3550,6 +3629,14 @@ var BaseSpan = class BaseSpan {
3550
3629
  ...this.isRootSpan && !nestedUnderParent && this.tags?.length ? { tags: this.tags } : {}
3551
3630
  };
3552
3631
  }
3632
+ /** Returns the span saved for a later `rebuildSpan()`, with its trace state */
3633
+ snapshot() {
3634
+ return {
3635
+ ...this.exportSpan(),
3636
+ ownEntityVersionId: this.ownEntityVersionId ?? null,
3637
+ ...this.traceState ? { traceState: this.traceState } : {}
3638
+ };
3639
+ }
3553
3640
  get externalTraceId() {
3554
3641
  return this.isValid ? this.traceId : void 0;
3555
3642
  }
@@ -5156,7 +5243,7 @@ var BaseObservabilityInstance = class extends MastraBase {
5156
5243
  * - Can have end(), update(), error() called on it
5157
5244
  * - Will emit SPAN_ENDED or SPAN_UPDATED when those methods are called
5158
5245
  *
5159
- * @param cached - The exported span data to rebuild from
5246
+ * @param cached - The span snapshot to rebuild from
5160
5247
  * @returns A span that can have lifecycle methods called on it
5161
5248
  */
5162
5249
  rebuildSpan(cached) {
@@ -5176,15 +5263,27 @@ var BaseObservabilityInstance = class extends MastraBase {
5176
5263
  entityId: cached.entityId,
5177
5264
  entityName: cached.entityName,
5178
5265
  tracingPolicy: cached.isInternal ? { internal: InternalSpans.ALL } : void 0,
5179
- traceState: cached.nestedUnderParent ? {
5180
- requestContextKeys: [],
5181
- nestedUnderParent: true
5182
- } : void 0
5266
+ traceState: this.rebuildTraceState(cached)
5183
5267
  });
5268
+ if (span instanceof BaseSpan) span.restoreEntityLineage(cached);
5184
5269
  this.wireSpanLifecycle(span);
5185
5270
  return span;
5186
5271
  }
5187
5272
  /**
5273
+ * Restore the trace-level rules for a rebuilt span. Spans saved with
5274
+ * `exportSpan()` before `snapshot()` existed fall back to the instance's
5275
+ * configured keys, so an in-flight durable run keeps at least those across an upgrade.
5276
+ */
5277
+ rebuildTraceState(cached) {
5278
+ if (cached.traceState) return cached.traceState;
5279
+ const fallback = this.computeTraceState();
5280
+ if (!cached.nestedUnderParent) return fallback;
5281
+ return {
5282
+ requestContextKeys: fallback?.requestContextKeys ?? [],
5283
+ nestedUnderParent: true
5284
+ };
5285
+ }
5286
+ /**
5188
5287
  * Get current configuration
5189
5288
  */
5190
5289
  getConfig() {