@mastra/client-js 1.21.0-alpha.6 → 1.21.0-alpha.7

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/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # @mastra/client-js
2
2
 
3
+ ## 1.21.0-alpha.7
4
+
5
+ ### Patch Changes
6
+
7
+ - Fixed client-side tools getting stuck in `input-available` state in React's `useChat` messages. After a client tool finished executing, the React UI never observed a terminal `tool-result` (or `tool-error`) chunk for it, so the matching `dynamic-tool` part stayed at `state: 'input-available'` indefinitely. The client now emits a synthetic Mastra-shaped terminal chunk into the streamed response right after the client tool resolves or rejects, so the React reducer correctly flips the part to `output-available` (or `output-error`) and renders the tool result. ([#16916](https://github.com/mastra-ai/mastra/pull/16916))
8
+
9
+ Also fixed the client stream parser so final `tool-call` chunks are not treated as partial streaming tool calls while preparing client-tool continuation messages.
10
+
11
+ - Updated dependencies [[`168fa09`](https://github.com/mastra-ai/mastra/commit/168fa09d6b39114cb8c13bd06f1dccb9bc81c6cd)]:
12
+ - @mastra/core@1.37.0-alpha.7
13
+
3
14
  ## 1.21.0-alpha.6
4
15
 
5
16
  ### Patch Changes
@@ -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.21.0-alpha.6"
6
+ version: "1.21.0-alpha.7"
7
7
  ---
8
8
 
9
9
  ## When to use
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.21.0-alpha.6",
2
+ "version": "1.21.0-alpha.7",
3
3
  "package": "@mastra/client-js",
4
4
  "exports": {
5
5
  "RequestContext": {
package/dist/index.cjs CHANGED
@@ -1294,6 +1294,7 @@ var Agent = class extends BaseResource {
1294
1294
  update,
1295
1295
  onToolCall,
1296
1296
  onFinish,
1297
+ onStreamChunk,
1297
1298
  getCurrentDate = () => /* @__PURE__ */ new Date(),
1298
1299
  lastMessage
1299
1300
  }) {
@@ -1361,6 +1362,7 @@ var Agent = class extends BaseResource {
1361
1362
  // TODO: casting as any here because the stream types were all typed as any before in core.
1362
1363
  // but this is completely wrong and this fn is probably broken. Remove ":any" and you'll see a bunch of type errors
1363
1364
  onChunk: async (chunk) => {
1365
+ onStreamChunk?.(chunk);
1364
1366
  switch (chunk.type) {
1365
1367
  case "tripwire": {
1366
1368
  message.parts.push({
@@ -1462,6 +1464,7 @@ var Agent = class extends BaseResource {
1462
1464
  execUpdate();
1463
1465
  }
1464
1466
  }
1467
+ break;
1465
1468
  }
1466
1469
  case "tool-call-input-streaming-start": {
1467
1470
  if (message.toolInvocations == null) {
@@ -1578,6 +1581,7 @@ var Agent = class extends BaseResource {
1578
1581
  try {
1579
1582
  let toolCalls = [];
1580
1583
  let messages = [];
1584
+ let streamRunId = processedParams.runId;
1581
1585
  const [streamForController, streamForProcessing] = response.body.tee();
1582
1586
  const pipePromise = streamForController.pipeTo(
1583
1587
  new WritableStream({
@@ -1631,25 +1635,76 @@ var Agent = class extends BaseResource {
1631
1635
  const clientTool = processedParams.clientTools?.[toolCall2.toolName];
1632
1636
  if (clientTool && clientTool.execute) {
1633
1637
  shouldExecuteClientTool = true;
1634
- const { result, observability } = await executeClientToolWithObservability({
1635
- clientTool,
1636
- args: toolCall2?.args,
1637
- toolName: toolCall2.toolName,
1638
- parentContext: getClientToolObservabilityContext(toolCall2),
1639
- executeContext: {
1640
- requestContext: processedParams.requestContext,
1641
- tracingContext: { currentSpan: void 0 },
1642
- agent: {
1643
- agentId: this.agentId,
1644
- messages: response.messages,
1645
- toolCallId: toolCall2?.toolCallId,
1646
- suspend: async () => {
1647
- },
1648
- threadId,
1649
- resourceId
1638
+ const runId = streamRunId ?? toolCall2.toolCallId;
1639
+ let result;
1640
+ let observability;
1641
+ let synthetic;
1642
+ try {
1643
+ ({ result, observability } = await executeClientToolWithObservability({
1644
+ clientTool,
1645
+ args: toolCall2?.args,
1646
+ toolName: toolCall2.toolName,
1647
+ parentContext: getClientToolObservabilityContext(toolCall2),
1648
+ executeContext: {
1649
+ requestContext: processedParams.requestContext,
1650
+ tracingContext: { currentSpan: void 0 },
1651
+ agent: {
1652
+ agentId: this.agentId,
1653
+ messages: response.messages,
1654
+ toolCallId: toolCall2?.toolCallId,
1655
+ suspend: async () => {
1656
+ },
1657
+ threadId,
1658
+ resourceId
1659
+ }
1650
1660
  }
1651
- }
1652
- });
1661
+ }));
1662
+ synthetic = {
1663
+ type: "tool-result",
1664
+ runId,
1665
+ from: "AGENT",
1666
+ payload: {
1667
+ toolCallId: toolCall2.toolCallId,
1668
+ toolName: toolCall2.toolName,
1669
+ result,
1670
+ isError: false,
1671
+ providerExecuted: false
1672
+ }
1673
+ };
1674
+ } catch (error) {
1675
+ synthetic = {
1676
+ type: "tool-error",
1677
+ runId,
1678
+ from: "AGENT",
1679
+ payload: {
1680
+ toolCallId: toolCall2.toolCallId,
1681
+ toolName: toolCall2.toolName,
1682
+ error,
1683
+ args: toolCall2?.args,
1684
+ providerExecuted: false
1685
+ }
1686
+ };
1687
+ result = { error: error instanceof Error ? error.message : String(error) };
1688
+ }
1689
+ try {
1690
+ await pipePromise;
1691
+ } catch {
1692
+ }
1693
+ try {
1694
+ const errorForSerialization = synthetic.type === "tool-error" ? synthetic.payload.error : void 0;
1695
+ const serializedError = errorForSerialization instanceof Error ? {
1696
+ name: errorForSerialization.name,
1697
+ message: errorForSerialization.message,
1698
+ stack: errorForSerialization.stack
1699
+ } : errorForSerialization;
1700
+ const payloadForWire = synthetic.type === "tool-error" ? { ...synthetic, payload: { ...synthetic.payload, error: serializedError } } : synthetic;
1701
+ const sseLine = `data: ${JSON.stringify(payloadForWire)}
1702
+
1703
+ `;
1704
+ controller.enqueue(new TextEncoder().encode(sseLine));
1705
+ } catch (enqueueErr) {
1706
+ console.error("Failed to enqueue synthetic tool-result chunk:", enqueueErr);
1707
+ }
1653
1708
  const lastMessageRaw = messages[messages.length - 1];
1654
1709
  const lastMessage = lastMessageRaw != null ? JSON.parse(JSON.stringify(lastMessageRaw)) : void 0;
1655
1710
  const toolInvocationPart = lastMessage?.parts?.find(
@@ -1699,6 +1754,11 @@ var Agent = class extends BaseResource {
1699
1754
  controller.close();
1700
1755
  }
1701
1756
  },
1757
+ onStreamChunk: (chunk) => {
1758
+ if (!streamRunId && typeof chunk.runId === "string") {
1759
+ streamRunId = chunk.runId;
1760
+ }
1761
+ },
1702
1762
  lastMessage: void 0
1703
1763
  }).catch(async (error) => {
1704
1764
  console.error("Error processing stream response:", error);