@mastra/client-js 1.56.0 → 1.57.0-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.
Files changed (36) hide show
  1. package/dist/client.d.ts +7 -1
  2. package/dist/client.d.ts.map +1 -1
  3. package/dist/docs/SKILL.md +5 -3
  4. package/dist/docs/assets/SOURCE_MAP.json +1 -1
  5. package/dist/docs/references/docs-connections-a2a.md +1 -1
  6. package/dist/docs/references/docs-harness-agent-controller.md +7 -7
  7. package/dist/docs/references/docs-harness-schedules.md +5 -5
  8. package/dist/docs/references/docs-server-mastra-client.md +1 -1
  9. package/dist/docs/references/docs-signals-notifications.md +233 -0
  10. package/dist/docs/references/docs-signals-sending-signals.md +166 -0
  11. package/dist/docs/references/docs-signals-thread-streams.md +94 -0
  12. package/dist/docs/references/docs-studio-editor.md +39 -23
  13. package/dist/docs/references/reference-client-js-agent-controller.md +2 -2
  14. package/dist/docs/references/reference-client-js-agents.md +18 -2
  15. package/dist/docs/references/reference-client-js-datasets.md +1 -1
  16. package/dist/docs/references/reference-client-js-observability.md +26 -1
  17. package/dist/docs/references/reference-observability-tracing-advanced-queries.md +92 -11
  18. package/dist/index.cjs +148 -22
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.d.ts +1 -1
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +148 -22
  23. package/dist/index.js.map +1 -1
  24. package/dist/resources/agent.d.ts +10 -3
  25. package/dist/resources/agent.d.ts.map +1 -1
  26. package/dist/resources/observability-route-types.d.ts +2 -0
  27. package/dist/resources/observability-route-types.d.ts.map +1 -1
  28. package/dist/resources/observability.d.ts +7 -1
  29. package/dist/resources/observability.d.ts.map +1 -1
  30. package/dist/route-types.generated.d.ts +437 -359
  31. package/dist/route-types.generated.d.ts.map +1 -1
  32. package/dist/utils/process-mastra-stream.d.ts.map +1 -1
  33. package/dist/utils/sse-event-splitter.d.ts +3 -0
  34. package/dist/utils/sse-event-splitter.d.ts.map +1 -0
  35. package/package.json +8 -8
  36. package/dist/docs/references/docs-harness-signals.md +0 -546
@@ -10,8 +10,9 @@ Advanced trace queries select completed traces with a structured time range and
10
10
  | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | ------------------------------------------ |
11
11
  | Query traces | List matching traces, paginate through them, and poll for new ones. | `queryTraces()` | `POST /api/observability/traces/query` |
12
12
  | Aggregate traces | Count matching traces and compute measures, such as error rate, latency percentiles, tokens, and cost, optionally grouped by dimensions and time buckets. | `aggregateTraces()` | `POST /api/observability/traces/aggregate` |
13
+ | Aggregate spans | Compute the same measures over individual spans, such as every agent run including nested ones, grouped by span dimensions. | `aggregateSpans()` | `POST /api/observability/spans/aggregate` |
13
14
 
14
- Both APIs accept the same [trace selection](#select-traces). The filters, operators, and limits on this page apply to each. Use the aggregate API to find the group that needs attention, such as an agent with a high error rate, then send the same predicate plus the group's dimensions to the query API to list the traces behind it. Each query result represents a trace. Use its `traceId` to [retrieve the full trace](https://mastra.ai/reference/client-js/observability).
15
+ The trace APIs accept the same [trace selection](#select-traces). The filters, operators, and limits on this page apply to each. Use the aggregate API to find the group that needs attention, such as an agent with a high error rate, then send the same predicate plus the group's dimensions to the query API to list the traces behind it. Each query result represents a trace. Use its `traceId` to [retrieve the full trace](https://mastra.ai/reference/client-js/observability).
15
16
 
16
17
  The endpoints use the same authentication as other observability routes and require the `observability:read` permission. Every request requires a time range.
17
18
 
@@ -25,6 +26,7 @@ The configured observability store must support each API. Call `getObservability
25
26
  | -------------------------------- | -------------------------------------- | -------------------------------------------------------------------------------------- |
26
27
  | `traceQuery` | `trace-query` | `queryTraces()` |
27
28
  | `traceAggregate` | `trace-aggregate` | `aggregateTraces()` |
29
+ | `spanAggregate` | `span-aggregate` | `aggregateSpans()` |
28
30
  | `traceQueryRootDuration` | `trace-query-root-duration` | Trace-scope `durationMs` predicates |
29
31
  | `traceQueryTimeToFirstChunk` | `trace-query-time-to-first-chunk` | `timeToFirstChunkMs` predicates and results |
30
32
  | `traceQueryTimeToFirstChunkSort` | `trace-query-time-to-first-chunk-sort` | `orderBy` on `timeToFirstChunkMs` |
@@ -34,7 +36,7 @@ The configured observability store must support each API. Call `getObservability
34
36
  | `traceQueryDiscovery` | `trace-query-discovery` | [Field and value discovery](#discover-fields-and-values) |
35
37
  | `traceQueryTenantScope` | `trace-query-tenant-scope` | [Trusted tenant scope](#trusted-tenant-scope) |
36
38
 
37
- A missing `traceAggregate` value means the server doesn't support aggregation. Servers without the aggregate route return `404`, and stores without the feature return `501 TRACE_AGGREGATE_UNSUPPORTED`. Against a store without a sub-feature, a request that uses a gated field returns `501` before storage runs. Root span `name`, `spanType`, and `entityId` are the exception and return `422` with a `field_not_allowed` issue.
39
+ A missing `traceAggregate` value means the server doesn't support aggregation. Servers without the aggregate route return `404`, and stores without the feature return `501 TRACE_AGGREGATE_UNSUPPORTED`. A missing `spanAggregate` value means the same for span aggregation, and stores without the feature return `501 SPAN_AGGREGATE_UNSUPPORTED`. Against a store without a sub-feature, a request that uses a gated field returns `501` before storage runs. Root span `name`, `spanType`, and `entityId` are the exception and return `422` with a `field_not_allowed` issue.
38
40
 
39
41
  ## Select traces
40
42
 
@@ -212,7 +214,7 @@ const selectedModel = {
212
214
  {
213
215
  op: 'eq',
214
216
  left: { path: 'model' },
215
- right: { literal: 'claude-sonnet-4-6' },
217
+ right: { literal: 'claude-sonnet-5-5' },
216
218
  },
217
219
  { op: 'eq', left: { path: 'provider' }, right: { literal: 'anthropic' } },
218
220
  ],
@@ -700,14 +702,14 @@ Token and cost measures read the model usage metrics recorded for each trace, so
700
702
 
701
703
  A trace that recorded at least one usage metric is usage-bearing. `tokens.<kind>.sum` adds the tokens of a group's usage-bearing traces, and `tokens.<kind>.avg` divides that sum by the number of usage-bearing traces, not by `count`. Both are `null` when the group has no usage-bearing traces.
702
704
 
703
- Cost comes from estimated cost recorded on total input and total output token metrics. `cost.sum` adds the cost of traces with at least one priced metric, and `cost.avg` divides by the number of those traces. A metric whose pricing failed, including a partial cost, isn't priced.
705
+ Cost comes from estimated cost recorded on total input and total output token metrics. `cost.sum` adds the cost of traces with at least one priced metric, and `cost.avg` divides by the number of those traces. A partly priced call, where Mastra could price some token types but not others, adds the cost of the priced token types. A metric with any other pricing failure adds no cost.
704
706
 
705
707
  When you request any `cost.*` measure, every row also includes a `cost` object:
706
708
 
707
- | Field | Description |
708
- | ---------- | ------------------------------------------------------------------------------------------------------------------------------ |
709
- | `coverage` | Fraction of usage-bearing traces whose usage was fully priced, from 0 to 1. `null` when the group has no usage-bearing traces. |
710
- | `unit` | Cost unit of the priced metrics, such as `USD`. `'mixed'` when they use more than one unit, and `null` when none are priced. |
709
+ | Field | Description |
710
+ | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
711
+ | `coverage` | Fraction of usage-bearing traces whose usage was fully priced, from 0 to 1. A trace with a partly priced call isn't fully priced, so it lowers coverage even though its known cost is in `cost.sum`. `null` when the group has no usage-bearing traces. |
712
+ | `unit` | Cost unit of the priced metrics, such as `USD`. `'mixed'` when they use more than one unit, and `null` when none are priced. |
711
713
 
712
714
  `cost.sum` and `cost.avg` are `null` when the group has no priced traces or `unit` is `'mixed'`. A coverage below 1 means the cost understates the group's spend. The `cost` object isn't a measure, so `having` and `orderBy` can't reference it.
713
715
 
@@ -803,11 +805,90 @@ PostgreSQL and ClickHouse stop aggregations after the same `traceQueryTimeoutMs`
803
805
  | `501` | The installed Core or configured observability store doesn't support aggregation or a requested field. Returns `TRACE_AGGREGATE_UNSUPPORTED`. |
804
806
  | `504` | A PostgreSQL or ClickHouse aggregation exceeded its execution timeout. Returns `TRACE_QUERY_EXECUTION_TIMEOUT` without partial results. |
805
807
 
808
+ ## Aggregate spans
809
+
810
+ Use `aggregateSpans()` or `POST /api/observability/spans/aggregate` to compute measures over individual spans instead of traces. Trace aggregation only sees each trace's root span, so an agent run nested inside a workflow isn't counted. Span aggregation counts every span that matches, at any depth.
811
+
812
+ The request has the same shape as [`aggregateTraces()`](#aggregate-request-fields), with the same measures, `having`, `orderBy`, `interval`, limits, and errors. The differences are in which records are selected and which dimensions are available.
813
+
814
+ ### Aggregate spans with the client SDK
815
+
816
+ This request counts agent runs by status per day, including nested runs:
817
+
818
+ ```typescript
819
+ const { capabilities } = await mastraClient.getObservabilityCapabilities()
820
+
821
+ if (capabilities.spanAggregate) {
822
+ const { rows } = await mastraClient.aggregateSpans({
823
+ timeRange: {
824
+ from: '2026-08-01T00:00:00.000Z',
825
+ to: '2026-08-08T00:00:00.000Z',
826
+ },
827
+ where: { op: 'eq', left: { path: 'spanType' }, right: { literal: 'agent_run' } },
828
+ groupBy: ['status'],
829
+ interval: '1d',
830
+ measures: ['count', 'duration.p95'],
831
+ })
832
+ }
833
+ ```
834
+
835
+ Send the same body to `POST /api/observability/spans/aggregate` to use HTTP directly.
836
+
837
+ ### Span selection
838
+
839
+ Aggregation selects the spans that [`querySpans()`](https://mastra.ai/reference/client-js/observability) would return for the same `timeRange` and `where`:
840
+
841
+ - `timeRange` bounds each span's `startedAt`, and can span up to 365 days.
842
+ - `where` accepts the `querySpans()` fields and operators, such as `spanType`, `name`, `status`, `durationMs`, entity and parent/root entity fields, and context identifiers.
843
+ - Only completed spans count. When a span was exported more than once, only its latest copy counts.
844
+
845
+ `count` equals the number of spans `querySpans()` pages through for the same request. Buckets use each span's `startedAt`. `duration.*` measures read the span's own duration, and `errorCount` and `errorRate` read whether the span itself recorded an error.
846
+
847
+ On ClickHouse, a request whose `where` limits `spanType` to agent, workflow, processor, or scorer runs, RAG ingestion, or tool calls reads the smaller branch table instead of every span. That path can differ from `querySpans()`:
848
+
849
+ - Spans written before an upgrade from `@mastra/clickhouse` 1.4, 1.5, or 1.6 aren't in the branch table, so older windows can count fewer spans.
850
+ - After ClickHouse merges the copies of a span exported more than once, the branch table keeps the copy written last, not the one that ended last. Counts don't change, but the `status` and duration of a retried span can.
851
+
852
+ ### Span dimensions
853
+
854
+ | Dimension | Notes |
855
+ | --------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
856
+ | `name`, `spanType`, `status` | The span's own name, type, and status. `status` is `error` when the span recorded an error and `success` otherwise. |
857
+ | `entityType`, `entityId`, `entityName` | The entity that produced the span. |
858
+ | `parentEntityType`, `parentEntityName`, `rootEntityType`, `rootEntityName` | The calling entity and the trace's root entity, such as the agent that made a model call. |
859
+ | `environment`, `serviceName`, `executionSource` | Deployment attributes recorded on the span. |
860
+ | `threadId`, `resourceId`, `userId`, `sessionId`, `organizationId`, `experimentId` | Context identifiers recorded on the span. |
861
+
862
+ `countDistinct.<field>` accepts these dimensions and `traceId`, so `countDistinct.traceId` counts the traces the selected spans belong to. Span aggregation can't group by `metadata.<key>`, identity fields such as `traceId`, `spanId`, or `runId`, or by model and provider.
863
+
864
+ ### Span tokens and cost
865
+
866
+ Token and cost measures use the usage metrics recorded by each span, matched by `traceId` and `spanId`. The [trace token rules](#token-and-cost-measures) apply per span: metrics from `timeRange.from` on count, retried exports count once, and averages divide by usage-bearing spans.
867
+
868
+ Usage isn't added up to parent spans. Model calls record usage on `model_generation` spans, so an `agent_run` span has no tokens of its own unless a model call under it wasn't exported. When a `model_generation` span isn't exported, its usage is recorded on the nearest exported ancestor instead. For token totals per agent, select `model_generation` spans and group by the calling agent:
869
+
870
+ ```typescript
871
+ const tokensByAgent = await mastraClient.aggregateSpans({
872
+ timeRange: {
873
+ from: '2026-08-01T00:00:00.000Z',
874
+ to: '2026-08-08T00:00:00.000Z',
875
+ },
876
+ where: { op: 'eq', left: { path: 'spanType' }, right: { literal: 'model_generation' } },
877
+ groupBy: ['parentEntityName'],
878
+ measures: ['tokens.total.sum', 'cost.sum'],
879
+ orderBy: { field: 'cost.sum', direction: 'desc' },
880
+ })
881
+ ```
882
+
883
+ ### List the spans behind a group
884
+
885
+ Aggregation never returns span records. To inspect a group, send the same `timeRange` and `where` to `querySpans()`, and add the group's dimension values as `eq` conditions. For example, add `{ op: 'eq', left: { path: 'status' }, right: { literal: 'error' } }` to list the failed agent runs from the first example.
886
+
806
887
  ## Trusted tenant scope
807
888
 
808
- Hosts that serve more than one tenant supply a trusted scope outside the query document. The server reads the reserved `organizationId` request-context key, which only server-side authentication can set, and passes `{ organizationId }` to the planner. The scope is carried on the trusted plan and ANDed into every scan: current roots, related spans, the usage metrics read by token and cost measures, and both discovery routes. A related record from another tenant that shares a `traceId` never qualifies a trace.
889
+ Hosts that serve more than one tenant supply a trusted scope outside the query document. The server reads the reserved `organizationId` request-context key, which only server-side authentication can set, and passes `{ organizationId }` to the planner. The scope is carried on the trusted plan and ANDed into every scan: current roots, related spans, the usage metrics read by token and cost measures, span aggregation, and both discovery routes. A related record from another tenant that shares a `traceId` never qualifies a trace.
809
890
 
810
- Hosts that call storage directly can pass a fuller scope to `planTraceQuery()`, `planTraceAggregate()`, `planTraceQueryObservedFields()`, and `planTraceQueryValues()`:
891
+ Hosts that call storage directly can pass a fuller scope to `planTraceQuery()`, `planTraceAggregate()`, `planSpanAggregate()`, `planTraceQueryObservedFields()`, and `planTraceQueryValues()`:
811
892
 
812
893
  ```typescript
813
894
  const plan = planTraceQuery(parseTraceQueryRequest(request), {
@@ -938,7 +1019,7 @@ Both routes require `observability:read` and use the configured observability st
938
1019
 
939
1020
  ## Limitations
940
1021
 
941
- Both APIs consider completed traces only and don't include running traces. Trace queries can't return custom projections, embedded evidence, or summaries, and numbered trace pages include a total count. Aggregation can't group by span fields, `traceId`, or `attributes.*`, compute numeric measures over metadata, paginate groups, or zero-fill empty buckets. The CLI doesn't expose a trace aggregation command, so use the SDK or the HTTP endpoint.
1022
+ The query and aggregate APIs consider completed traces and spans only, not running ones. Trace queries can't return custom projections, embedded evidence, or summaries, and numbered trace pages include a total count. Trace aggregation can't group by span fields other than the root span's `name`, `spanType`, and `entityId`, or by `traceId` or `attributes.*`. Aggregation can't compute numeric measures over metadata, paginate groups, or zero-fill empty buckets. Span aggregation can't group by `model` or `provider`, and it attributes tokens only to the span that recorded them. The CLI's `mastra api trace aggregate` covers trace aggregation, and `mastra api span query` and `mastra api span aggregate` cover span queries and span aggregation.
942
1023
 
943
1024
  ## Related
944
1025
 
package/dist/index.cjs CHANGED
@@ -1080,11 +1080,51 @@ function processClientTools(clientTools) {
1080
1080
  }));
1081
1081
  }
1082
1082
  //#endregion
1083
+ //#region src/utils/sse-event-splitter.ts
1084
+ /** Split SSE events without rescanning or copying an unfinished event on each read. */
1085
+ function createSseEventSplitter() {
1086
+ let parts = [];
1087
+ let pendingNewline = false;
1088
+ return (text, isFinal = false) => {
1089
+ const events = [];
1090
+ let start = 0;
1091
+ if (text) {
1092
+ if (pendingNewline) {
1093
+ if (text.startsWith("\n")) {
1094
+ events.push(parts.join(""));
1095
+ parts = [];
1096
+ start = 1;
1097
+ } else parts.push("\n");
1098
+ pendingNewline = false;
1099
+ }
1100
+ let separator;
1101
+ while ((separator = text.indexOf("\n\n", start)) !== -1) {
1102
+ parts.push(text.slice(start, separator));
1103
+ events.push(parts.join(""));
1104
+ parts = [];
1105
+ start = separator + 2;
1106
+ }
1107
+ if (start < text.length) {
1108
+ pendingNewline = text.endsWith("\n");
1109
+ const end = pendingNewline ? text.length - 1 : text.length;
1110
+ if (start < end) parts.push(text.slice(start, end));
1111
+ }
1112
+ }
1113
+ if (isFinal) {
1114
+ if (pendingNewline) parts.push("\n");
1115
+ events.push(parts.join(""));
1116
+ parts = [];
1117
+ pendingNewline = false;
1118
+ }
1119
+ return events;
1120
+ };
1121
+ }
1122
+ //#endregion
1083
1123
  //#region src/utils/process-mastra-stream.ts
1084
1124
  async function sharedProcessMastraStream({ stream, onChunk, signal }) {
1085
1125
  const reader = stream.getReader();
1086
1126
  const decoder = new TextDecoder();
1087
- let buffer = "";
1127
+ const splitEvents = createSseEventSplitter();
1088
1128
  const abort = () => void reader.cancel();
1089
1129
  if (signal?.aborted) abort();
1090
1130
  else signal?.addEventListener("abort", abort, { once: true });
@@ -1092,9 +1132,7 @@ async function sharedProcessMastraStream({ stream, onChunk, signal }) {
1092
1132
  while (true) {
1093
1133
  const { done, value } = await reader.read();
1094
1134
  if (done) break;
1095
- buffer += decoder.decode(value, { stream: true });
1096
- const lines = buffer.split("\n\n");
1097
- buffer = lines.pop() || "";
1135
+ const lines = splitEvents(decoder.decode(value, { stream: true }));
1098
1136
  for (const line of lines) if (line.startsWith("data: ")) {
1099
1137
  const data = line.slice(6);
1100
1138
  if (data === "[DONE]") return;
@@ -1894,10 +1932,18 @@ var Agent = class extends BaseResource {
1894
1932
  };
1895
1933
  return streamResponse;
1896
1934
  }
1897
- async abortThread(params) {
1935
+ /** Stops execution of a run, including runs without a memory thread. */
1936
+ async abortRun(runId, options) {
1937
+ return this.request(`/agents/${this.agentId}/runs/${encodeURIComponent(runId)}/abort`, {
1938
+ method: "POST",
1939
+ signal: options?.signal
1940
+ });
1941
+ }
1942
+ async abortThread(params, options) {
1898
1943
  const { resourceId, threadId, clearPendingSignals, expectedRunId } = params;
1899
1944
  return this.request(`/agents/${this.agentId}/threads/abort`, {
1900
1945
  method: "POST",
1946
+ signal: options?.signal,
1901
1947
  body: {
1902
1948
  resourceId,
1903
1949
  threadId,
@@ -2631,10 +2677,11 @@ var Agent = class extends BaseResource {
2631
2677
  }
2632
2678
  /**
2633
2679
  * Creates the consumer-facing stream for the streaming methods. Cancelling the
2634
- * returned stream (or aborting `externalSignal`) aborts the underlying request
2635
- * and stops any client-tool continuations.
2680
+ * returned stream or the client-wide signal detaches the HTTP observer and
2681
+ * stops any client-tool continuations. Per-call execution cancellation is
2682
+ * handled separately by processStreamResponse.
2636
2683
  */
2637
- createCancellableStream(externalSignal) {
2684
+ createCancellableStream() {
2638
2685
  const abortController = new AbortController();
2639
2686
  let readableController;
2640
2687
  return {
@@ -2647,10 +2694,63 @@ var Agent = class extends BaseResource {
2647
2694
  }
2648
2695
  }),
2649
2696
  controller: readableController,
2650
- signal: mergeAbortSignals(abortController.signal, externalSignal, this.options.abortSignal)
2697
+ signal: mergeAbortSignals(abortController.signal, this.options.abortSignal)
2651
2698
  };
2652
2699
  }
2653
2700
  async processStreamResponse(processedParams, controller, route = "stream", signal) {
2701
+ const suppliedRunId = processedParams.runId;
2702
+ processedParams = {
2703
+ ...processedParams,
2704
+ runId: processedParams.runId ?? (0, _lukeed_uuid.v4)()
2705
+ };
2706
+ const callerSignal = processedParams.abortSignal;
2707
+ callerSignal?.throwIfAborted();
2708
+ const transportAbort = new AbortController();
2709
+ const cancellationAbort = new AbortController();
2710
+ const detachSignal = signal;
2711
+ signal = mergeAbortSignals(detachSignal, transportAbort.signal);
2712
+ let cancellationTimer;
2713
+ const stopCancellation = () => cancellationAbort.abort();
2714
+ const cancelExecution = () => {
2715
+ cancellationTimer = setTimeout(() => cancellationAbort.abort(/* @__PURE__ */ new Error("Agent run cancellation was not acknowledged")), 1e4);
2716
+ (async () => {
2717
+ try {
2718
+ while (true) {
2719
+ cancellationAbort.signal.throwIfAborted();
2720
+ if ((await this.abortRun(processedParams.runId, { signal: cancellationAbort.signal })).aborted) return;
2721
+ await new Promise((resolve, reject) => {
2722
+ const onAbort = () => {
2723
+ clearTimeout(timer);
2724
+ reject(cancellationAbort.signal.reason);
2725
+ };
2726
+ const timer = setTimeout(() => {
2727
+ cancellationAbort.signal.removeEventListener("abort", onAbort);
2728
+ resolve();
2729
+ }, 100);
2730
+ cancellationAbort.signal.addEventListener("abort", onAbort, { once: true });
2731
+ if (cancellationAbort.signal.aborted) onAbort();
2732
+ });
2733
+ }
2734
+ } catch (error) {
2735
+ if (!cancellationAbort.signal.aborted && !(error instanceof Error && "status" in error && (error.status === 404 || error.status === 501))) try {
2736
+ controller.error(error);
2737
+ } catch {}
2738
+ } finally {
2739
+ clearTimeout(cancellationTimer);
2740
+ transportAbort.abort(callerSignal?.reason);
2741
+ }
2742
+ })();
2743
+ };
2744
+ const cleanupCancellation = () => {
2745
+ callerSignal?.removeEventListener("abort", cancelExecution);
2746
+ detachSignal?.removeEventListener("abort", stopCancellation);
2747
+ clearTimeout(cancellationTimer);
2748
+ stopCancellation();
2749
+ };
2750
+ detachSignal?.addEventListener("abort", stopCancellation, { once: true });
2751
+ if (detachSignal?.aborted) stopCancellation();
2752
+ callerSignal?.addEventListener("abort", cancelExecution, { once: true });
2753
+ if (callerSignal?.aborted) cancelExecution();
2654
2754
  const { memory } = processedParams ?? {};
2655
2755
  const { resource, thread } = memory ?? {};
2656
2756
  const threadId = processedParams.threadId ?? (typeof thread === "string" ? thread : thread?.id);
@@ -2667,9 +2767,15 @@ var Agent = class extends BaseResource {
2667
2767
  body: requestBody,
2668
2768
  stream: true,
2669
2769
  signal
2770
+ }).catch((error) => {
2771
+ cleanupCancellation();
2772
+ throw error;
2670
2773
  });
2671
- if (!response.body) throw new Error("No response body");
2672
- const isAborted = () => Boolean(signal?.aborted);
2774
+ if (!response.body) {
2775
+ cleanupCancellation();
2776
+ throw new Error("No response body");
2777
+ }
2778
+ const isAborted = () => Boolean(signal?.aborted || callerSignal?.aborted);
2673
2779
  const isAbortError = (error) => isAborted() || error?.name === "AbortError";
2674
2780
  const safeClose = () => {
2675
2781
  try {
@@ -2682,18 +2788,16 @@ var Agent = class extends BaseResource {
2682
2788
  };
2683
2789
  try {
2684
2790
  let messages = [];
2685
- let streamRunId = processedParams.runId;
2791
+ let streamRunId = suppliedRunId;
2686
2792
  let finishedHistory;
2687
2793
  let suspended = false;
2688
2794
  const resumedToolCallIds = /* @__PURE__ */ new Set();
2689
2795
  const releaseOpenStream = streamRunId ? this.trackOpenStream(streamRunId) : () => {};
2690
2796
  const [streamForController, streamForProcessing] = response.body.tee();
2691
2797
  const decoder = new TextDecoder();
2692
- let pendingText = "";
2798
+ const splitEvents = createSseEventSplitter();
2693
2799
  const enqueueReadableText = (text, isFinal = false) => {
2694
- pendingText += text;
2695
- const lines = pendingText.split("\n\n");
2696
- pendingText = isFinal ? "" : lines.pop() ?? "";
2800
+ const lines = splitEvents(text, isFinal);
2697
2801
  for (const line of lines) {
2698
2802
  if (!line.startsWith("data: ") || !line.includes("\"type\":\"finish\"")) continue;
2699
2803
  try {
@@ -2799,6 +2903,7 @@ var Agent = class extends BaseResource {
2799
2903
  payload: {
2800
2904
  toolCallId: toolCall.toolCallId,
2801
2905
  toolName: toolCall.toolName,
2906
+ args: toolCall.args,
2802
2907
  result,
2803
2908
  isError: false,
2804
2909
  providerExecuted: false
@@ -2899,10 +3004,12 @@ var Agent = class extends BaseResource {
2899
3004
  } catch {}
2900
3005
  safeClose();
2901
3006
  }).finally(() => {
3007
+ cleanupCancellation();
2902
3008
  signal?.removeEventListener("abort", onAbort);
2903
3009
  releaseOpenStream();
2904
3010
  });
2905
3011
  } catch (error) {
3012
+ cleanupCancellation();
2906
3013
  if (!isAbortError(error)) console.error("Error processing stream response:", error);
2907
3014
  }
2908
3015
  return response;
@@ -3000,7 +3107,7 @@ var Agent = class extends BaseResource {
3000
3107
  clientTools: processClientTools(params.clientToolsResolver?.() ?? params.clientTools),
3001
3108
  structuredOutput
3002
3109
  };
3003
- const { readable, controller: readableController, signal } = this.createCancellableStream(params.abortSignal);
3110
+ const { readable, controller: readableController, signal } = this.createCancellableStream();
3004
3111
  const response = await this.processStreamResponse(processedParams, readableController, "stream", signal);
3005
3112
  const streamResponse = new Response(readable, {
3006
3113
  status: response.status,
@@ -3031,7 +3138,7 @@ var Agent = class extends BaseResource {
3031
3138
  clientTools: processClientTools(params.clientToolsResolver?.() ?? params.clientTools),
3032
3139
  structuredOutput
3033
3140
  };
3034
- const { readable, controller: readableController, signal } = this.createCancellableStream(params.abortSignal);
3141
+ const { readable, controller: readableController, signal } = this.createCancellableStream();
3035
3142
  const response = await this.processStreamResponse(processedParams, readableController, "stream-until-idle", signal);
3036
3143
  const streamResponse = new Response(readable, {
3037
3144
  status: response.status,
@@ -3075,7 +3182,7 @@ var Agent = class extends BaseResource {
3075
3182
  requestContext: parseClientRequestContext(requestContext),
3076
3183
  clientTools: processClientTools(executableClientTools)
3077
3184
  };
3078
- const { readable, controller: readableController, signal } = this.createCancellableStream(params.abortSignal);
3185
+ const { readable, controller: readableController, signal } = this.createCancellableStream();
3079
3186
  const response = await this.processStreamResponse(processedParams, readableController, "approve-tool-call", signal).catch((error) => {
3080
3187
  releaseHold({ force: true });
3081
3188
  throw error;
@@ -3112,7 +3219,7 @@ var Agent = class extends BaseResource {
3112
3219
  requestContext: parseClientRequestContext(requestContext),
3113
3220
  clientTools: processClientTools(executableClientTools)
3114
3221
  };
3115
- const { readable, controller: readableController, signal } = this.createCancellableStream(params.abortSignal);
3222
+ const { readable, controller: readableController, signal } = this.createCancellableStream();
3116
3223
  const response = await this.processStreamResponse(processedParams, readableController, "decline-tool-call", signal).catch((error) => {
3117
3224
  releaseHold({ force: true });
3118
3225
  throw error;
@@ -3188,7 +3295,7 @@ var Agent = class extends BaseResource {
3188
3295
  schema: (0, _mastra_schema_compat_schema.standardSchemaToJSONSchema)((0, _mastra_schema_compat_schema.toStandardSchema)(options.structuredOutput.schema))
3189
3296
  } : void 0
3190
3297
  };
3191
- const { readable, controller: readableController, signal } = this.createCancellableStream(options.abortSignal);
3298
+ const { readable, controller: readableController, signal } = this.createCancellableStream();
3192
3299
  const response = await this.processStreamResponse(processedParams, readableController, "resume-stream", signal).catch((error) => {
3193
3300
  releaseHold({ force: true });
3194
3301
  throw error;
@@ -3257,7 +3364,7 @@ var Agent = class extends BaseResource {
3257
3364
  schema: (0, _mastra_schema_compat_schema.standardSchemaToJSONSchema)((0, _mastra_schema_compat_schema.toStandardSchema)(options.structuredOutput.schema))
3258
3365
  } : void 0
3259
3366
  };
3260
- const { readable, controller: readableController, signal } = this.createCancellableStream(options.abortSignal);
3367
+ const { readable, controller: readableController, signal } = this.createCancellableStream();
3261
3368
  const response = await this.processStreamResponse(processedParams, readableController, "resume-stream-until-idle", signal).catch((error) => {
3262
3369
  releaseHold({ force: true });
3263
3370
  throw error;
@@ -5290,6 +5397,17 @@ var Observability = class extends BaseResource {
5290
5397
  body: params
5291
5398
  });
5292
5399
  }
5400
+ /**
5401
+ * Groups and measures individual completed spans selected by a time range and optional span-query predicate.
5402
+ * Token and cost measures count usage recorded on each span itself. Check `capabilities.spanAggregate` from
5403
+ * `getObservabilityCapabilities()` first; servers without it return 404 or 501.
5404
+ */
5405
+ aggregateSpans(params) {
5406
+ return this.request("/observability/spans/aggregate", {
5407
+ method: "POST",
5408
+ body: params
5409
+ });
5410
+ }
5293
5411
  /** Returns canonical and observed fields available to the advanced trace-query grammar. */
5294
5412
  getTraceQueryFields(params, options) {
5295
5413
  return this.request("/observability/traces/query/fields", {
@@ -8154,6 +8272,14 @@ var MastraClient = class extends BaseResource {
8154
8272
  aggregateTraces(params) {
8155
8273
  return this.observability.aggregateTraces(params);
8156
8274
  }
8275
+ /**
8276
+ * Groups and measures individual completed spans selected by a time range and optional span-query predicate.
8277
+ * Token and cost measures count usage recorded on each span itself. Check `capabilities.spanAggregate` from
8278
+ * `getObservabilityCapabilities()` first; servers without it return 404 or 501.
8279
+ */
8280
+ aggregateSpans(params) {
8281
+ return this.observability.aggregateSpans(params);
8282
+ }
8157
8283
  /** Returns canonical and observed fields available to the advanced trace-query grammar. */
8158
8284
  getTraceQueryFields(params, options) {
8159
8285
  return this.observability.getTraceQueryFields(params, options);