@mastra/client-js 1.56.0-alpha.6 → 1.56.0-alpha.8

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.
@@ -3,7 +3,7 @@ name: mastra-client-js
3
3
  description: Documentation for @mastra/client-js. Use when working with @mastra/client-js APIs, configuration, or implementation.
4
4
  metadata:
5
5
  package: "@mastra/client-js"
6
- version: "1.56.0-alpha.6"
6
+ version: "1.56.0-alpha.8"
7
7
  ---
8
8
 
9
9
  ## When to use
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.56.0-alpha.6",
2
+ "version": "1.56.0-alpha.8",
3
3
  "package": "@mastra/client-js",
4
4
  "exports": {},
5
5
  "modules": {}
@@ -321,6 +321,26 @@ await subscription.processDataStream({
321
321
 
322
322
  Pass `withInitialHistory: true` (or `{ perPage }`) to receive the stored messages as a first `thread-history` chunk instead of fetching history separately. Finished runs aren't replayed after it, so completed answers don't stream again on reconnect.
323
323
 
324
+ In the React SDK, `useChat()` exposes the `hasMore` flag from that first page as `initialHistoryPagination`, so you can decide whether to offer loading older messages without another history request:
325
+
326
+ ```typescript
327
+ import { useChat } from '@mastra/react'
328
+
329
+ const { messages, initialHistoryPagination } = useChat({
330
+ agentId: 'support-agent',
331
+ resourceId: 'user-123',
332
+ threadId: 'thread-abc',
333
+ enableThreadSignals: true,
334
+ withInitialHistory: { perPage: 20 },
335
+ })
336
+
337
+ // { hasMore: true }: older messages exist
338
+ // { hasMore: false }: the page reaches the start of the thread
339
+ // undefined: history is disabled, still loading, or the subscription ended
340
+ ```
341
+
342
+ `initialHistoryPagination` only describes the initial subscription page for the current agent, resource, thread, and `withInitialHistory` value. It isn't changed by live output or `setMessages()`. After you load older pages yourself, use the `hasMore` value from those responses.
343
+
324
344
  `subscribeToThread()` returns the underlying `Response` plus a `processDataStream()` helper. The helper reads the subscription stream until the connection closes or the request is aborted. Pass `reconnect: true` to resubscribe when the transport closes or a reconnect request fails, such as after a proxy idle timeout. If a reconnect request fails with a 4xx status (for example `401` or `403`) or `501`, `processDataStream()` stops reconnecting and rejects with the original `MastraClientError`. `408` and `429` are still retried.
325
345
 
326
346
  **resourceId** (`string`): Resource ID for the memory thread.
@@ -90,7 +90,7 @@ const result = await mastraClient.querySpans({
90
90
  console.log(result.spans)
91
91
  ```
92
92
 
93
- Pages default to 100 rows and allow at most 1,000. The response is `{ spans, page: { next } }`, without a total count. Ordering accepts exactly one of `startedAt` or `endedAt`, ascending or descending, and defaults to descending start time.
93
+ Pages default to 100 rows and allow at most 1,000. The response is `{ spans, page: { next } }`, without a total count. Ordering accepts exactly one of `startedAt`, `endedAt`, or `timeToFirstChunkMs`, ascending or descending, and defaults to descending start time. Spans without a `timeToFirstChunkMs` value sort last in both directions.
94
94
 
95
95
  | Fields | Operators |
96
96
  | -------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
@@ -98,7 +98,7 @@ Pages default to 100 rows and allow at most 1,000. The response is `{ spans, pag
98
98
  | `organizationId`, `resourceId`, `runId`, `sessionId`, `userId`, `requestId`, `serviceName`, `experimentId`, `environment`, `executionSource` | Same string operators |
99
99
  | Entity, parent entity and root entity `Type`, `Id`, `Name`, `VersionId` fields, such as `parentEntityId` | Same string operators |
100
100
  | `name`, `spanType`, `status` | Same string operators; `name` also accepts `matches` / `notMatches` for normalized whole words and phrases |
101
- | `startedAt`, `endedAt`, `durationMs` | Comparisons, membership and presence |
101
+ | `startedAt`, `endedAt`, `durationMs`, `timeToFirstChunkMs` | Comparisons, membership and presence |
102
102
  | `error` | `exists`, `notExists` |
103
103
 
104
104
  Combine predicates with `and`, `or` and `not`. Membership uses `{ op: 'in', value: { path: 'spanType' }, set: ['agent_run', 'tool_call'] }`. A span's status is `error` when it has an error and `success` otherwise.
@@ -21,16 +21,17 @@ For hosted traces, see [Query observability data on Mastra Platform](https://mas
21
21
 
22
22
  The configured observability store must support each API. Call `getObservabilityCapabilities()` or `GET /api/observability/capabilities` and check these flags before showing a feature:
23
23
 
24
- | Capability | Store feature | Enables |
25
- | ---------------------------- | --------------------------------- | -------------------------------------------------------------------------------------- |
26
- | `traceQuery` | `trace-query` | `queryTraces()` |
27
- | `traceAggregate` | `trace-aggregate` | `aggregateTraces()` |
28
- | `traceQueryRootDuration` | `trace-query-root-duration` | Trace-scope `durationMs` predicates |
29
- | `traceQueryTimeToFirstChunk` | `trace-query-time-to-first-chunk` | `timeToFirstChunkMs` predicates and results |
30
- | `traceQueryContextIds` | `trace-query-context-ids` | `runId`, `sessionId`, `userId`, and `organizationId` predicates |
31
- | `traceQueryRootSpanFields` | `trace-query-root-span-fields` | Trace-scope `name` and `spanType` predicates, dimensions, and `countDistinct` measures |
32
- | `traceQueryDiscovery` | `trace-query-discovery` | [Field and value discovery](#discover-fields-and-values) |
33
- | `traceQueryTenantScope` | `trace-query-tenant-scope` | [Trusted tenant scope](#trusted-tenant-scope) |
24
+ | Capability | Store feature | Enables |
25
+ | -------------------------------- | -------------------------------------- | -------------------------------------------------------------------------------------- |
26
+ | `traceQuery` | `trace-query` | `queryTraces()` |
27
+ | `traceAggregate` | `trace-aggregate` | `aggregateTraces()` |
28
+ | `traceQueryRootDuration` | `trace-query-root-duration` | Trace-scope `durationMs` predicates |
29
+ | `traceQueryTimeToFirstChunk` | `trace-query-time-to-first-chunk` | `timeToFirstChunkMs` predicates and results |
30
+ | `traceQueryTimeToFirstChunkSort` | `trace-query-time-to-first-chunk-sort` | `orderBy` on `timeToFirstChunkMs` |
31
+ | `traceQueryContextIds` | `trace-query-context-ids` | `runId`, `sessionId`, `userId`, and `organizationId` predicates |
32
+ | `traceQueryRootSpanFields` | `trace-query-root-span-fields` | Trace-scope `name` and `spanType` predicates, dimensions, and `countDistinct` measures |
33
+ | `traceQueryDiscovery` | `trace-query-discovery` | [Field and value discovery](#discover-fields-and-values) |
34
+ | `traceQueryTenantScope` | `trace-query-tenant-scope` | [Trusted tenant scope](#trusted-tenant-scope) |
34
35
 
35
36
  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` and `spanType` are the exception and return `422` with a `field_not_allowed` issue.
36
37
 
@@ -128,7 +129,13 @@ const slowFirstChunk = {
128
129
 
129
130
  The value is `null` when the span didn't stream a chunk, for example a tool call or a workflow run. A `null` value counts as missing, so it matches `notExists` and never matches ordered predicates. A value of `0` isn't missing. It means the first chunk arrived in the same millisecond the span started.
130
131
 
131
- Trace query results include the root span's `timeToFirstChunkMs`, and `querySpans()` results include each span's value. Sorting by this field isn't supported. Stores advertise support through the `trace-query-time-to-first-chunk` feature, and the server reports it as the `traceQueryTimeToFirstChunk` capability. Against an older store, results omit the field, a predicate on it returns `501` before storage runs, and field discovery omits it.
132
+ Trace query results include the root span's `timeToFirstChunkMs`, and `querySpans()` results include each span's value. Stores advertise support through the `trace-query-time-to-first-chunk` feature, and the server reports it as the `traceQueryTimeToFirstChunk` capability. Against an older store, results omit the field, a predicate on it returns `501` before storage runs, and field discovery omits it.
133
+
134
+ #### Sort by time to first chunk
135
+
136
+ Pass `orderBy: [{ field: 'timeToFirstChunkMs', direction: 'desc' }]` to list the slowest first chunks first. Traces sort by the root span's value. Missing values sort last in both directions, and `0` sorts as a value. Ties break by `traceId` for traces and by span identity for `querySpans()`.
137
+
138
+ Stores advertise the sort through the `trace-query-time-to-first-chunk-sort` feature, and the server reports it as the `traceQueryTimeToFirstChunkSort` capability. Against an older store, the sort returns `501` before storage runs.
132
139
 
133
140
  #### Filter by context identifiers
134
141
 
@@ -437,16 +444,16 @@ curl --request POST \
437
444
 
438
445
  ### Request fields
439
446
 
440
- | Field | Required | Description |
441
- | ------------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
442
- | `timeRange` | Yes | Trace start-time boundary. `from` is inclusive and `to` is exclusive. Both values must be ISO timestamps, and `from` must be earlier than `to`. |
443
- | `where` | No | Recursive trace predicate. Supports scalar conditions and `spans.some` or `spans.none` clauses. |
444
- | `orderBy` | No | One item ordering results by `startedAt` or `endedAt`, in `asc` or `desc` order. Defaults to `startedAt desc`. Not accepted in delta mode. |
445
- | `page` | No | `{ limit, after }`. `limit` defaults to 100 and has a maximum of 1000. Pass the opaque `page.next` value as `after`. |
446
- | `pagination` | No | Numbered pages: `{ page, perPage }`. Defaults to page 0 and 10 results. `perPage` has a maximum of 100. |
447
- | `mode` | No | Set to `'delta'` to poll using a delta cursor instead of `page` or `pagination`. |
448
- | `after` | No | Delta mode only. Opaque `deltaCursor` from a numbered page or previous poll. Omit to establish the current watermark. |
449
- | `limit` | No | Delta mode only. Maximum results per batch. Defaults to 10 and has a maximum of 100. |
447
+ | Field | Required | Description |
448
+ | ------------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
449
+ | `timeRange` | Yes | Trace start-time boundary. `from` is inclusive and `to` is exclusive. Both values must be ISO timestamps, and `from` must be earlier than `to`. |
450
+ | `where` | No | Recursive trace predicate. Supports scalar conditions and `spans.some` or `spans.none` clauses. |
451
+ | `orderBy` | No | One item ordering results by `startedAt`, `endedAt`, or `timeToFirstChunkMs`, in `asc` or `desc` order. Defaults to `startedAt desc`. Not accepted in delta mode. |
452
+ | `page` | No | `{ limit, after }`. `limit` defaults to 100 and has a maximum of 1000. Pass the opaque `page.next` value as `after`. |
453
+ | `pagination` | No | Numbered pages: `{ page, perPage }`. Defaults to page 0 and 10 results. `perPage` has a maximum of 100. |
454
+ | `mode` | No | Set to `'delta'` to poll using a delta cursor instead of `page` or `pagination`. |
455
+ | `after` | No | Delta mode only. Opaque `deltaCursor` from a numbered page or previous poll. Omit to establish the current watermark. |
456
+ | `limit` | No | Delta mode only. Maximum results per batch. Defaults to 10 and has a maximum of 100. |
450
457
 
451
458
  Unknown fields are rejected. Requests can't select a projection, declare joins, request counts or measures, or control authorization. Use [`aggregateTraces()`](#aggregate-traces) for counts and measures. Request bodies are limited to 256 KiB.
452
459
 
package/dist/index.cjs CHANGED
@@ -23,8 +23,8 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
23
23
  //#endregion
24
24
  let _lukeed_uuid = require("@lukeed/uuid");
25
25
  let _mastra_core_error = require("@mastra/core/error");
26
- let _mastra_schema_compat_schema = require("@mastra/schema-compat/schema");
27
26
  let _mastra_core_request_context = require("@mastra/core/request-context");
27
+ let _mastra_schema_compat_schema = require("@mastra/schema-compat/schema");
28
28
  let _mastra_schema_compat_zod_to_json = require("@mastra/schema-compat/zod-to-json");
29
29
  let _mastra_core_a2a_client = require("@mastra/core/a2a/client");
30
30
  let _mastra_core_a2a_v1 = require("@mastra/core/a2a/v1");
@@ -2644,6 +2644,7 @@ var Agent = class extends BaseResource {
2644
2644
  try {
2645
2645
  let messages = [];
2646
2646
  let streamRunId = processedParams.runId;
2647
+ let finishedHistory;
2647
2648
  const [streamForController, streamForProcessing] = response.body.tee();
2648
2649
  const decoder = new TextDecoder();
2649
2650
  let pendingText = "";
@@ -2651,6 +2652,13 @@ var Agent = class extends BaseResource {
2651
2652
  pendingText += text;
2652
2653
  const lines = pendingText.split("\n\n");
2653
2654
  pendingText = isFinal ? "" : lines.pop() ?? "";
2655
+ for (const line of lines) {
2656
+ if (!line.startsWith("data: ") || !line.includes("\"type\":\"finish\"")) continue;
2657
+ try {
2658
+ const all = JSON.parse(line.slice(6))?.payload?.messages?.all;
2659
+ if (Array.isArray(all)) finishedHistory = all;
2660
+ } catch {}
2661
+ }
2654
2662
  const readableLines = lines.filter((line) => line.trim() !== "[DONE]" && line.trim() !== "data: [DONE]").join("\n\n");
2655
2663
  if (readableLines) safeEnqueue(new TextEncoder().encode(`${readableLines}\n\n`));
2656
2664
  };
@@ -2804,12 +2812,12 @@ var Agent = class extends BaseResource {
2804
2812
  role: "tool",
2805
2813
  content: toolResultContents
2806
2814
  };
2807
- const updatedMessages = threadId ? [toolResultMessage] : [
2815
+ const updatedMessages = threadId ? [toolResultMessage] : (route === "resume-stream" || route === "approve-tool-call" || route === "decline-tool-call") && finishedHistory ? [...finishedHistory, toolResultMessage] : [
2808
2816
  ...Array.isArray(processedParams.messages) ? processedParams.messages : [],
2809
2817
  ...messages,
2810
2818
  toolResultMessage
2811
2819
  ];
2812
- const recursionRoute = route === "resume-stream" ? "stream" : route === "resume-stream-until-idle" ? "stream-until-idle" : route;
2820
+ const recursionRoute = route === "resume-stream" || route === "approve-tool-call" || route === "decline-tool-call" ? "stream" : route === "resume-stream-until-idle" ? "stream-until-idle" : route;
2813
2821
  if (isAborted()) return;
2814
2822
  try {
2815
2823
  await this.processStreamResponse({
@@ -3010,7 +3018,8 @@ var Agent = class extends BaseResource {
3010
3018
  const { requestContext, ...rest } = params;
3011
3019
  const processedParams = {
3012
3020
  ...rest,
3013
- requestContext: parseClientRequestContext(requestContext)
3021
+ requestContext: parseClientRequestContext(requestContext),
3022
+ clientTools: processClientTools(params.clientToolsResolver?.() ?? params.clientTools)
3014
3023
  };
3015
3024
  const { readable, controller: readableController, signal } = this.createCancellableStream(params.abortSignal);
3016
3025
  const response = await this.processStreamResponse(processedParams, readableController, "approve-tool-call", signal);
@@ -3041,7 +3050,8 @@ var Agent = class extends BaseResource {
3041
3050
  const { requestContext, ...rest } = params;
3042
3051
  const processedParams = {
3043
3052
  ...rest,
3044
- requestContext: parseClientRequestContext(requestContext)
3053
+ requestContext: parseClientRequestContext(requestContext),
3054
+ clientTools: processClientTools(params.clientToolsResolver?.() ?? params.clientTools)
3045
3055
  };
3046
3056
  const { readable, controller: readableController, signal } = this.createCancellableStream(params.abortSignal);
3047
3057
  const response = await this.processStreamResponse(processedParams, readableController, "decline-tool-call", signal);
@@ -3198,28 +3208,70 @@ var Agent = class extends BaseResource {
3198
3208
  * Used when `requireToolApproval` is enabled with generate() to allow the agent to proceed.
3199
3209
  */
3200
3210
  async approveToolCallGenerate(params) {
3201
- const { requestContext, ...rest } = params;
3202
- return this.request(`/agents/${this.agentId}/approve-tool-call-generate`, {
3211
+ const { requestContext, clientTools, clientToolsResolver, memory, ...rest } = params;
3212
+ const response = await this.request(`/agents/${this.agentId}/approve-tool-call-generate`, {
3203
3213
  method: "POST",
3204
3214
  body: {
3205
3215
  ...rest,
3206
- requestContext: parseClientRequestContext(requestContext)
3216
+ requestContext: parseClientRequestContext(requestContext),
3217
+ clientTools: processClientTools(clientToolsResolver?.() ?? clientTools)
3207
3218
  }
3208
3219
  });
3220
+ return this.continueResumedGenerate(response, {
3221
+ requestContext,
3222
+ clientTools,
3223
+ clientToolsResolver,
3224
+ memory
3225
+ });
3209
3226
  }
3210
3227
  /**
3211
3228
  * Declines a pending tool call and returns the complete response (non-streaming).
3212
3229
  * Used when `requireToolApproval` is enabled with generate() to prevent tool execution.
3213
3230
  */
3214
3231
  async declineToolCallGenerate(params) {
3215
- const { requestContext, ...rest } = params;
3216
- return this.request(`/agents/${this.agentId}/decline-tool-call-generate`, {
3232
+ const { requestContext, clientTools, clientToolsResolver, memory, ...rest } = params;
3233
+ const response = await this.request(`/agents/${this.agentId}/decline-tool-call-generate`, {
3217
3234
  method: "POST",
3218
3235
  body: {
3219
3236
  ...rest,
3220
- requestContext: parseClientRequestContext(requestContext)
3237
+ requestContext: parseClientRequestContext(requestContext),
3238
+ clientTools: processClientTools(clientToolsResolver?.() ?? clientTools)
3221
3239
  }
3222
3240
  });
3241
+ return this.continueResumedGenerate(response, {
3242
+ requestContext,
3243
+ clientTools,
3244
+ clientToolsResolver,
3245
+ memory
3246
+ });
3247
+ }
3248
+ /**
3249
+ * Runs executable client tools the model called after an approval decision and continues on
3250
+ * /generate, like generate() does. With memory the continuation sends only the new messages to the
3251
+ * thread. Without memory it re-sends the conversation up to the last user turn, because the resume
3252
+ * routes keep the earlier history server-side and drop it once the run finishes.
3253
+ */
3254
+ async continueResumedGenerate(response, { requestContext, clientTools, clientToolsResolver, memory }) {
3255
+ if (response.finishReason !== "tool-calls") return response;
3256
+ const context = requestContext === void 0 || requestContext instanceof _mastra_core_request_context.RequestContext ? requestContext : new _mastra_core_request_context.RequestContext(Object.entries(requestContext));
3257
+ const threadId = typeof memory?.thread === "string" ? memory.thread : memory?.thread?.id;
3258
+ const history = response.messages ?? [];
3259
+ const priorTurns = history.slice(0, history.findLastIndex((message) => message.role === "user") + 1);
3260
+ return executeToolCallAndRespond({
3261
+ response,
3262
+ params: {
3263
+ messages: threadId ? [] : priorTurns,
3264
+ clientTools: clientToolsResolver?.() ?? clientTools,
3265
+ clientToolsResolver,
3266
+ requestContext: context,
3267
+ memory
3268
+ },
3269
+ agentId: this.agentId,
3270
+ resourceId: memory?.resource,
3271
+ threadId,
3272
+ requestContext: context,
3273
+ respondFn: (messages, options) => this.generate(messages, options)
3274
+ });
3223
3275
  }
3224
3276
  /**
3225
3277
  * Processes the stream response and handles tool calls