@mastra/react 0.1.0-beta.1 → 0.1.0-beta.10

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,106 @@
1
1
  # @mastra/react-hooks
2
2
 
3
+ ## 0.1.0-beta.10
4
+
5
+ ### Minor Changes
6
+
7
+ - Fix "MessagePartRuntime is not available" error when chatting with agents in Studio playground by replacing deprecated `useMessagePart` hook with `useAssistantState` ([#11039](https://github.com/mastra-ai/mastra/pull/11039))
8
+
9
+ ### Patch Changes
10
+
11
+ - fix: persist data-\* chunks from writer.custom() to memory storage ([#10884](https://github.com/mastra-ai/mastra/pull/10884))
12
+ - Add persistence for custom data chunks (`data-*` parts) emitted via `writer.custom()` in tools
13
+ - Data chunks are now saved to message storage so they survive page refreshes
14
+ - Update `@assistant-ui/react` to v0.11.47 with native `DataMessagePart` support
15
+ - Convert `data-*` parts to `DataMessagePart` format (`{ type: 'data', name: string, data: T }`)
16
+ - Update related `@assistant-ui/*` packages for compatibility
17
+
18
+ - Updated dependencies [[`261473a`](https://github.com/mastra-ai/mastra/commit/261473ac637e633064a22076671e2e02b002214d)]:
19
+ - @mastra/client-js@1.0.0-beta.10
20
+
21
+ ## 0.1.0-beta.9
22
+
23
+ ### Patch Changes
24
+
25
+ - Updated dependencies [[`5a1ede1`](https://github.com/mastra-ai/mastra/commit/5a1ede1f7ab527b9ead11f7eee2f73e67aeca9e4)]:
26
+ - @mastra/client-js@1.0.0-beta.9
27
+
28
+ ## 0.1.0-beta.8
29
+
30
+ ### Patch Changes
31
+
32
+ - Updated dependencies:
33
+ - @mastra/client-js@1.0.0-beta.8
34
+
35
+ ## 0.1.0-beta.7
36
+
37
+ ### Patch Changes
38
+
39
+ - Updated dependencies [[`5fe71bc`](https://github.com/mastra-ai/mastra/commit/5fe71bc925dfce597df69c89241f33b378028c63), [`21735a7`](https://github.com/mastra-ai/mastra/commit/21735a7ef306963554a69a89b44f06c3bcd85141)]:
40
+ - @mastra/client-js@1.0.0-beta.7
41
+
42
+ ## 0.1.0-beta.6
43
+
44
+ ### Patch Changes
45
+
46
+ - Adjust the types to accept tracingOptions ([#10742](https://github.com/mastra-ai/mastra/pull/10742))
47
+
48
+ - Updated dependencies [[`6edf340`](https://github.com/mastra-ai/mastra/commit/6edf3402f6a46ee8def2f42a2287785251fbffd6), [`ad7e8f1`](https://github.com/mastra-ai/mastra/commit/ad7e8f16ac843cbd16687ad47b66ba96bcffe111), [`e1b7118`](https://github.com/mastra-ai/mastra/commit/e1b7118f42ca0a97247afc75e57dcd5fdf987752), [`441c7b6`](https://github.com/mastra-ai/mastra/commit/441c7b6665915cfa7fd625fded8c0f518530bf10), [`e849603`](https://github.com/mastra-ai/mastra/commit/e849603a596269069f58a438b98449ea2770493d)]:
49
+ - @mastra/client-js@1.0.0-beta.6
50
+
51
+ ## 0.1.0-beta.5
52
+
53
+ ### Patch Changes
54
+
55
+ - Configurable resourceId in react useChat ([#10461](https://github.com/mastra-ai/mastra/pull/10461))
56
+
57
+ - fix(agent): persist messages before tool suspension ([#10369](https://github.com/mastra-ai/mastra/pull/10369))
58
+
59
+ Fixes issues where thread and messages were not saved before suspension when tools require approval or call suspend() during execution. This caused conversation history to be lost if users refreshed during tool approval or suspension.
60
+
61
+ **Backend changes (@mastra/core):**
62
+ - Add assistant messages to messageList immediately after LLM execution
63
+ - Flush messages synchronously before suspension to persist state
64
+ - Create thread if it doesn't exist before flushing
65
+ - Add metadata helpers to persist and remove tool approval state
66
+ - Pass saveQueueManager and memory context through workflow for immediate persistence
67
+
68
+ **Frontend changes (@mastra/react):**
69
+ - Extract runId from pending approvals to enable resumption after refresh
70
+ - Convert `pendingToolApprovals` (DB format) to `requireApprovalMetadata` (runtime format)
71
+ - Handle both `dynamic-tool` and `tool-{NAME}` part types for approval state
72
+ - Change runId from hardcoded `agentId` to unique `uuid()`
73
+
74
+ **UI changes (@mastra/playground-ui):**
75
+ - Handle tool calls awaiting approval in message initialization
76
+ - Convert approval metadata format when loading initial messages
77
+
78
+ Fixes #9745, #9906
79
+
80
+ - Updated dependencies [[`898a972`](https://github.com/mastra-ai/mastra/commit/898a9727d286c2510d6b702dfd367e6aaf5c6b0f)]:
81
+ - @mastra/client-js@1.0.0-beta.5
82
+
83
+ ## 0.1.0-beta.4
84
+
85
+ ### Patch Changes
86
+
87
+ - Updated dependencies [[`6a86fe5`](https://github.com/mastra-ai/mastra/commit/6a86fe56b8ff53ca2eb3ed87ffc0748749ebadce), [`595a3b8`](https://github.com/mastra-ai/mastra/commit/595a3b8727c901f44e333909c09843c711224440)]:
88
+ - @mastra/client-js@1.0.0-beta.4
89
+
90
+ ## 0.1.0-beta.3
91
+
92
+ ### Patch Changes
93
+
94
+ - Updated dependencies [[`e1bb9c9`](https://github.com/mastra-ai/mastra/commit/e1bb9c94b4eb68b019ae275981be3feb769b5365)]:
95
+ - @mastra/client-js@1.0.0-beta.3
96
+
97
+ ## 0.1.0-beta.2
98
+
99
+ ### Patch Changes
100
+
101
+ - Updated dependencies []:
102
+ - @mastra/client-js@1.0.0-beta.2
103
+
3
104
  ## 0.1.0-beta.1
4
105
 
5
106
  ### Patch Changes
package/dist/index.cjs CHANGED
@@ -5,6 +5,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
5
5
  const jsxRuntime = require('react/jsx-runtime');
6
6
  const react = require('react');
7
7
  const clientJs = require('@mastra/client-js');
8
+ const uuid = require('@lukeed/uuid');
8
9
  const lucideReact = require('lucide-react');
9
10
  const tailwindMerge = require('tailwind-merge');
10
11
  const hastUtilToJsxRuntime = require('hast-util-to-jsx-runtime');
@@ -102,6 +103,34 @@ const mapWorkflowStreamChunkToWatchResult = (prev, chunk) => {
102
103
  };
103
104
  const toUIMessage = ({ chunk, conversation, metadata }) => {
104
105
  const result = [...conversation];
106
+ if (chunk.type.startsWith("data-")) {
107
+ const lastMessage = result[result.length - 1];
108
+ if (!lastMessage || lastMessage.role !== "assistant") {
109
+ const newMessage = {
110
+ id: `data-${chunk.runId}-${Date.now()}`,
111
+ role: "assistant",
112
+ parts: [
113
+ {
114
+ type: chunk.type,
115
+ data: "data" in chunk ? chunk.data : void 0
116
+ }
117
+ ],
118
+ metadata
119
+ };
120
+ return [...result, newMessage];
121
+ }
122
+ const updatedMessage = {
123
+ ...lastMessage,
124
+ parts: [
125
+ ...lastMessage.parts,
126
+ {
127
+ type: chunk.type,
128
+ data: "data" in chunk ? chunk.data : void 0
129
+ }
130
+ ]
131
+ };
132
+ return [...result.slice(0, -1), updatedMessage];
133
+ }
105
134
  switch (chunk.type) {
106
135
  case "tripwire": {
107
136
  const newMessage = {
@@ -259,17 +288,19 @@ const toUIMessage = ({ chunk, conversation, metadata }) => {
259
288
  if (!lastMessage || lastMessage.role !== "assistant") return result;
260
289
  const parts = [...lastMessage.parts];
261
290
  const toolPartIndex = parts.findIndex(
262
- (part) => part.type === "dynamic-tool" && "toolCallId" in part && part.toolCallId === chunk.payload.toolCallId
291
+ (part) => (part.type === "dynamic-tool" || typeof part.type === "string" && part.type.startsWith("tool-")) && "toolCallId" in part && part.toolCallId === chunk.payload.toolCallId
263
292
  );
264
293
  if (toolPartIndex !== -1) {
265
294
  const toolPart = parts[toolPartIndex];
266
- if (toolPart.type === "dynamic-tool") {
295
+ if (toolPart.type === "dynamic-tool" || typeof toolPart.type === "string" && toolPart.type.startsWith("tool-")) {
296
+ const toolName = "toolName" in toolPart && typeof toolPart.toolName === "string" ? toolPart.toolName : toolPart.type.startsWith("tool-") ? toolPart.type.substring(5) : "";
297
+ const toolCallId = toolPart.toolCallId;
267
298
  if (chunk.type === "tool-result" && chunk.payload.isError || chunk.type === "tool-error") {
268
299
  const error = chunk.type === "tool-error" ? chunk.payload.error : chunk.payload.result;
269
300
  parts[toolPartIndex] = {
270
301
  type: "dynamic-tool",
271
- toolName: toolPart.toolName,
272
- toolCallId: toolPart.toolCallId,
302
+ toolName,
303
+ toolCallId,
273
304
  state: "output-error",
274
305
  input: toolPart.input,
275
306
  errorText: String(error),
@@ -288,8 +319,8 @@ const toUIMessage = ({ chunk, conversation, metadata }) => {
288
319
  }
289
320
  parts[toolPartIndex] = {
290
321
  type: "dynamic-tool",
291
- toolName: toolPart.toolName,
292
- toolCallId: toolPart.toolCallId,
322
+ toolName,
323
+ toolCallId,
293
324
  state: "output-available",
294
325
  input: toolPart.input,
295
326
  output,
@@ -311,11 +342,14 @@ const toUIMessage = ({ chunk, conversation, metadata }) => {
311
342
  if (!lastMessage || lastMessage.role !== "assistant") return result;
312
343
  const parts = [...lastMessage.parts];
313
344
  const toolPartIndex = parts.findIndex(
314
- (part) => part.type === "dynamic-tool" && "toolCallId" in part && part.toolCallId === chunk.payload.toolCallId
345
+ (part) => (part.type === "dynamic-tool" || typeof part.type === "string" && part.type.startsWith("tool-")) && "toolCallId" in part && part.toolCallId === chunk.payload.toolCallId
315
346
  );
316
347
  if (toolPartIndex !== -1) {
317
348
  const toolPart = parts[toolPartIndex];
318
- if (toolPart.type === "dynamic-tool") {
349
+ if (toolPart.type === "dynamic-tool" || typeof toolPart.type === "string" && toolPart.type.startsWith("tool-")) {
350
+ const toolName = "toolName" in toolPart && typeof toolPart.toolName === "string" ? toolPart.toolName : typeof toolPart.type === "string" && toolPart.type.startsWith("tool-") ? toolPart.type.substring(5) : "";
351
+ const toolCallId = toolPart.toolCallId;
352
+ const input = toolPart.input;
319
353
  if (chunk.payload.output?.type?.startsWith("workflow-")) {
320
354
  const existingWorkflowState = toolPart.output || {};
321
355
  const updatedWorkflowState = mapWorkflowStreamChunkToWatchResult(
@@ -323,7 +357,11 @@ const toUIMessage = ({ chunk, conversation, metadata }) => {
323
357
  chunk.payload.output
324
358
  );
325
359
  parts[toolPartIndex] = {
326
- ...toolPart,
360
+ type: "dynamic-tool",
361
+ toolName,
362
+ toolCallId,
363
+ state: "input-streaming",
364
+ input,
327
365
  output: updatedWorkflowState
328
366
  };
329
367
  } else if (chunk.payload.output?.from === "AGENT" || chunk.payload.output?.from === "USER" && chunk.payload.output?.payload?.output?.type?.startsWith("workflow-")) {
@@ -332,7 +370,11 @@ const toUIMessage = ({ chunk, conversation, metadata }) => {
332
370
  const currentOutput = toolPart.output || [];
333
371
  const existingOutput = Array.isArray(currentOutput) ? currentOutput : [];
334
372
  parts[toolPartIndex] = {
335
- ...toolPart,
373
+ type: "dynamic-tool",
374
+ toolName,
375
+ toolCallId,
376
+ state: "input-streaming",
377
+ input,
336
378
  output: [...existingOutput, chunk.payload.output]
337
379
  };
338
380
  }
@@ -655,6 +697,29 @@ const toAssistantUIMessage = (message) => {
655
697
  }
656
698
  return baseToolCall;
657
699
  }
700
+ const requireApprovalMetadata = extendedMessage.metadata?.requireApprovalMetadata;
701
+ const partToolCallId = "toolCallId" in part && typeof part.toolCallId === "string" ? part.toolCallId : void 0;
702
+ const suspensionData = partToolCallId ? requireApprovalMetadata?.[partToolCallId] : void 0;
703
+ if (suspensionData) {
704
+ const toolName = "toolName" in part && typeof part.toolName === "string" ? part.toolName : part.type.startsWith("tool-") ? part.type.substring(5) : "";
705
+ return {
706
+ type: "tool-call",
707
+ toolCallId: partToolCallId,
708
+ toolName,
709
+ argsText: "input" in part ? JSON.stringify(part.input) : "{}",
710
+ args: "input" in part ? part.input : {},
711
+ metadata: extendedMessage.metadata
712
+ };
713
+ }
714
+ if (part.type.startsWith("data-")) {
715
+ return {
716
+ type: "data",
717
+ name: part.type.substring(5),
718
+ // Extract name from 'data-{name}'
719
+ data: part.data,
720
+ metadata: message.metadata
721
+ };
722
+ }
658
723
  return {
659
724
  type: "text",
660
725
  text: "",
@@ -741,7 +806,6 @@ const resolveInitialMessages = (messages) => {
741
806
  childMessages,
742
807
  result: finalResult?.text || ""
743
808
  };
744
- console.log("json", json);
745
809
  const nextMessage = {
746
810
  role: "assistant",
747
811
  parts: [
@@ -769,6 +833,18 @@ const resolveInitialMessages = (messages) => {
769
833
  return message;
770
834
  }
771
835
  }
836
+ const extendedMessage = message;
837
+ const pendingToolApprovals = extendedMessage.metadata?.pendingToolApprovals;
838
+ if (pendingToolApprovals && typeof pendingToolApprovals === "object") {
839
+ return {
840
+ ...message,
841
+ metadata: {
842
+ ...message.metadata,
843
+ mode: "stream",
844
+ requireApprovalMetadata: pendingToolApprovals
845
+ }
846
+ };
847
+ }
772
848
  return message;
773
849
  });
774
850
  };
@@ -1217,12 +1293,24 @@ const fromCoreUserMessageToUIMessage = (coreUserMessage) => {
1217
1293
  };
1218
1294
  };
1219
1295
 
1220
- const useChat = ({ agentId, initializeMessages }) => {
1221
- const _currentRunId = react.useRef(void 0);
1296
+ const useChat = ({ agentId, resourceId, initializeMessages }) => {
1297
+ const extractRunIdFromMessages = (messages2) => {
1298
+ for (const message of messages2) {
1299
+ const pendingToolApprovals = message.metadata?.pendingToolApprovals;
1300
+ if (pendingToolApprovals && typeof pendingToolApprovals === "object") {
1301
+ const suspensionData = Object.values(pendingToolApprovals)[0];
1302
+ if (suspensionData?.runId) {
1303
+ return suspensionData.runId;
1304
+ }
1305
+ }
1306
+ }
1307
+ return void 0;
1308
+ };
1309
+ const initialMessages = initializeMessages?.() || [];
1310
+ const initialRunId = extractRunIdFromMessages(initialMessages);
1311
+ const _currentRunId = react.useRef(initialRunId);
1222
1312
  const _onChunk = react.useRef(void 0);
1223
- const [messages, setMessages] = react.useState(
1224
- () => resolveInitialMessages(initializeMessages?.() || [])
1225
- );
1313
+ const [messages, setMessages] = react.useState(() => resolveInitialMessages(initialMessages));
1226
1314
  const [toolCallApprovals, setToolCallApprovals] = react.useState({});
1227
1315
  const baseClient = useMastraClient();
1228
1316
  const [isRunning, setIsRunning] = react.useState(false);
@@ -1232,7 +1320,8 @@ const useChat = ({ agentId, initializeMessages }) => {
1232
1320
  threadId,
1233
1321
  modelSettings,
1234
1322
  signal,
1235
- onFinish
1323
+ onFinish,
1324
+ tracingOptions
1236
1325
  }) => {
1237
1326
  const {
1238
1327
  frequencyPenalty,
@@ -1254,7 +1343,7 @@ const useChat = ({ agentId, initializeMessages }) => {
1254
1343
  const agent = clientWithAbort.getAgent(agentId);
1255
1344
  const response = await agent.generate({
1256
1345
  messages: coreUserMessages,
1257
- runId: agentId,
1346
+ runId: uuid.v4(),
1258
1347
  maxSteps,
1259
1348
  modelSettings: {
1260
1349
  frequencyPenalty,
@@ -1267,8 +1356,9 @@ const useChat = ({ agentId, initializeMessages }) => {
1267
1356
  },
1268
1357
  instructions,
1269
1358
  requestContext,
1270
- ...threadId ? { threadId, resourceId: agentId } : {},
1271
- providerOptions
1359
+ ...threadId ? { threadId, resourceId: resourceId || agentId } : {},
1360
+ providerOptions,
1361
+ tracingOptions
1272
1362
  });
1273
1363
  setIsRunning(false);
1274
1364
  if (response && "uiMessages" in response.response && response.response.uiMessages) {
@@ -1282,7 +1372,15 @@ const useChat = ({ agentId, initializeMessages }) => {
1282
1372
  setMessages((prev) => [...prev, ...mastraUIMessages]);
1283
1373
  }
1284
1374
  };
1285
- const stream = async ({ coreUserMessages, requestContext, threadId, onChunk, modelSettings, signal }) => {
1375
+ const stream = async ({
1376
+ coreUserMessages,
1377
+ requestContext,
1378
+ threadId,
1379
+ onChunk,
1380
+ modelSettings,
1381
+ signal,
1382
+ tracingOptions
1383
+ }) => {
1286
1384
  const {
1287
1385
  frequencyPenalty,
1288
1386
  presencePenalty,
@@ -1302,7 +1400,7 @@ const useChat = ({ agentId, initializeMessages }) => {
1302
1400
  abortSignal: signal
1303
1401
  });
1304
1402
  const agent = clientWithAbort.getAgent(agentId);
1305
- const runId = agentId;
1403
+ const runId = uuid.v4();
1306
1404
  const response = await agent.stream({
1307
1405
  messages: coreUserMessages,
1308
1406
  runId,
@@ -1318,9 +1416,10 @@ const useChat = ({ agentId, initializeMessages }) => {
1318
1416
  },
1319
1417
  instructions,
1320
1418
  requestContext,
1321
- ...threadId ? { threadId, resourceId: agentId } : {},
1419
+ ...threadId ? { threadId, resourceId: resourceId || agentId } : {},
1322
1420
  providerOptions,
1323
- requireToolApproval
1421
+ requireToolApproval,
1422
+ tracingOptions
1324
1423
  });
1325
1424
  _onChunk.current = onChunk;
1326
1425
  _currentRunId.current = runId;
@@ -1338,7 +1437,8 @@ const useChat = ({ agentId, initializeMessages }) => {
1338
1437
  threadId,
1339
1438
  onNetworkChunk,
1340
1439
  modelSettings,
1341
- signal
1440
+ signal,
1441
+ tracingOptions
1342
1442
  }) => {
1343
1443
  const { frequencyPenalty, presencePenalty, maxRetries, maxTokens, temperature, topK, topP, maxSteps } = modelSettings || {};
1344
1444
  setIsRunning(true);
@@ -1347,6 +1447,7 @@ const useChat = ({ agentId, initializeMessages }) => {
1347
1447
  abortSignal: signal
1348
1448
  });
1349
1449
  const agent = clientWithAbort.getAgent(agentId);
1450
+ const runId = uuid.v4();
1350
1451
  const response = await agent.network({
1351
1452
  messages: coreUserMessages,
1352
1453
  maxSteps,
@@ -1359,9 +1460,10 @@ const useChat = ({ agentId, initializeMessages }) => {
1359
1460
  topK,
1360
1461
  topP
1361
1462
  },
1362
- runId: agentId,
1463
+ runId,
1363
1464
  requestContext,
1364
- ...threadId ? { thread: threadId, resourceId: agentId } : {}
1465
+ ...threadId ? { thread: threadId, resourceId: resourceId || agentId } : {},
1466
+ tracingOptions
1365
1467
  });
1366
1468
  const transformer = new AISdkNetworkTransformer();
1367
1469
  await response.processDataStream({