@mastra/observability 1.17.5 → 1.17.6-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/dist/index.js CHANGED
@@ -1767,7 +1767,38 @@ function getProvidedCostContext(attrs, usage) {
1767
1767
  });
1768
1768
  return contexts;
1769
1769
  }
1770
+ /**
1771
+ * Entity types the processor runner assigns to the spans it creates, one per
1772
+ * pipeline phase.
1773
+ */
1774
+ const PROCESSOR_ENTITY_TYPES = /* @__PURE__ */ new Set([
1775
+ EntityType.INPUT_PROCESSOR,
1776
+ EntityType.INPUT_STEP_PROCESSOR,
1777
+ EntityType.OUTPUT_PROCESSOR,
1778
+ EntityType.OUTPUT_STEP_PROCESSOR,
1779
+ EntityType.TOOL_RESULT_PROCESSOR
1780
+ ]);
1781
+ /**
1782
+ * Whether this span measures a processor running, for `mastra_processor_duration_ms`.
1783
+ *
1784
+ * A processor may declare a domain span type (e.g. the skills processor emits
1785
+ * `SKILL_ACTION`), so `PROCESSOR_RUN` alone no longer identifies one. A
1786
+ * processor entity type alone is not enough either: spans that are not
1787
+ * processors borrow one — observational memory wraps its observer and reflector
1788
+ * model calls in a `GENERIC` span tagged `OUTPUT_STEP_PROCESSOR`, and counting
1789
+ * those model-call durations would swamp a metric measuring processor overhead.
1790
+ *
1791
+ * So require the pipeline attribute the runner stamps on every processor span
1792
+ * it creates ('legacy' from the runner, 'workflow' from processor workflows),
1793
+ * which spans merely borrowing the entity type do not set.
1794
+ */
1795
+ function isProcessorSpan(span) {
1796
+ if (!span.entityType || !PROCESSOR_ENTITY_TYPES.has(span.entityType)) return false;
1797
+ if (span.type === SpanType.PROCESSOR_RUN) return true;
1798
+ return Boolean(span.attributes?.processorExecutor);
1799
+ }
1770
1800
  function getDurationMetricName(span) {
1801
+ if (isProcessorSpan(span)) return "mastra_processor_duration_ms";
1771
1802
  switch (span.type) {
1772
1803
  case SpanType.AGENT_RUN: return "mastra_agent_duration_ms";
1773
1804
  case SpanType.TOOL_CALL:
@@ -2826,6 +2857,8 @@ var BaseSpan = class BaseSpan {
2826
2857
  correlationContext;
2827
2858
  /** Child spans that have started but not yet ended */
2828
2859
  #openChildren;
2860
+ /** The span whose open-child set currently tracks this span: the parent, or a live ancestor after promotion */
2861
+ #openChildrenOwner;
2829
2862
  /**
2830
2863
  * Subclasses can override to unconditionally mark the span as excluded.
2831
2864
  * NoOpSpan uses this because it is never exported regardless of config.
@@ -2848,7 +2881,10 @@ var BaseSpan = class BaseSpan {
2848
2881
  this.tracingPolicy = options.tracingPolicy;
2849
2882
  this.traceState = options.traceState;
2850
2883
  const parent = options.parent;
2851
- if (!this.isEvent && parent instanceof BaseSpan && parent.isValid) (parent.#openChildren ??= /* @__PURE__ */ new Set()).add(this);
2884
+ if (!this.isEvent && parent instanceof BaseSpan && parent.isValid) {
2885
+ (parent.#openChildren ??= /* @__PURE__ */ new Set()).add(this);
2886
+ this.#openChildrenOwner = parent;
2887
+ }
2852
2888
  this.tags = !options.parent && options.tags?.length ? options.tags : void 0;
2853
2889
  const entityParent = this.getParentSpan(false);
2854
2890
  this.entityType = options.entityType ?? entityParent?.entityType;
@@ -2891,10 +2927,34 @@ var BaseSpan = class BaseSpan {
2891
2927
  if (this.#openChildren) for (const child of [...this.#openChildren]) child.endTree(options);
2892
2928
  this.end(options);
2893
2929
  }
2894
- /** Release this span from its parent's open-child set once it has ended */
2930
+ /** Close any still-open descendant spans without applying options to them */
2931
+ endOpenDescendants() {
2932
+ if (this.#openChildren) for (const child of [...this.#openChildren]) child.endTree();
2933
+ }
2934
+ /**
2935
+ * Release this span from its parent's open-child set once it has ended.
2936
+ * Children still open at that moment are handed to the nearest ancestor
2937
+ * that has not ended, so a later endTree() on that ancestor can still
2938
+ * reach them (e.g. a model span that finalizes on abort before the
2939
+ * agent run's terminal handler closes the tree).
2940
+ */
2895
2941
  detachFromParent() {
2896
- const parent = this.parent;
2897
- if (parent instanceof BaseSpan) parent.#openChildren?.delete(this);
2942
+ const owner = this.#openChildrenOwner;
2943
+ if (owner) {
2944
+ owner.#openChildren?.delete(this);
2945
+ this.#openChildrenOwner = void 0;
2946
+ }
2947
+ if (this.#openChildren?.size) {
2948
+ let ancestor = this.parent;
2949
+ while (ancestor instanceof BaseSpan && ancestor.endTime) ancestor = ancestor.parent;
2950
+ if (ancestor instanceof BaseSpan) {
2951
+ for (const child of this.#openChildren) {
2952
+ (ancestor.#openChildren ??= /* @__PURE__ */ new Set()).add(child);
2953
+ child.#openChildrenOwner = ancestor;
2954
+ }
2955
+ this.#openChildren.clear();
2956
+ }
2957
+ }
2898
2958
  }
2899
2959
  createChildSpan(options) {
2900
2960
  return this.observabilityInstance.startSpan({
@@ -3117,6 +3177,7 @@ var DefaultSpan = class extends BaseSpan {
3117
3177
  end(options) {
3118
3178
  if (this.isEvent) return;
3119
3179
  if (this.endTime) return;
3180
+ if (options?.endTree) this.endOpenDescendants();
3120
3181
  this.endTime = /* @__PURE__ */ new Date();
3121
3182
  this.detachFromParent();
3122
3183
  if (options?.metadata) this.metadata = {
@@ -3132,7 +3193,7 @@ var DefaultSpan = class extends BaseSpan {
3132
3193
  }
3133
3194
  error(options) {
3134
3195
  if (this.isEvent) return;
3135
- const { error, endSpan = true, attributes, metadata } = options;
3196
+ const { error, endSpan = true, endTree, attributes, metadata } = options;
3136
3197
  if (metadata) this.metadata = {
3137
3198
  ...this.metadata,
3138
3199
  ...deepClean(this.prepareSpanMetadata(metadata), this.deepCleanOptions)
@@ -3156,7 +3217,8 @@ var DefaultSpan = class extends BaseSpan {
3156
3217
  ...deepClean(attributes, this.deepCleanOptions)
3157
3218
  };
3158
3219
  }
3159
- if (endSpan) this.end();
3220
+ if (endTree) this.end({ endTree: true });
3221
+ else if (endSpan) this.end();
3160
3222
  else this.update({});
3161
3223
  }
3162
3224
  update(options) {