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

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,132 @@
1
1
  # @mastra/react-hooks
2
2
 
3
+ ## 0.1.0-beta.11
4
+
5
+ ### Patch Changes
6
+
7
+ - Support new Workflow tripwire run status. Tripwires that are thrown from within a workflow will now bubble up and return a graceful state with information about tripwires. ([#10947](https://github.com/mastra-ai/mastra/pull/10947))
8
+
9
+ When a workflow contains an agent step that triggers a tripwire, the workflow returns with `status: 'tripwire'` and includes tripwire details:
10
+
11
+ ```typescript showLineNumbers copy
12
+ const run = await workflow.createRun();
13
+ const result = await run.start({ inputData: { message: 'Hello' } });
14
+
15
+ if (result.status === 'tripwire') {
16
+ console.log('Workflow terminated by tripwire:', result.tripwire?.reason);
17
+ console.log('Processor ID:', result.tripwire?.processorId);
18
+ console.log('Retry requested:', result.tripwire?.retry);
19
+ }
20
+ ```
21
+
22
+ Adds new UI state for tripwire in agent chat and workflow UI.
23
+
24
+ This is distinct from `status: 'failed'` which indicates an unexpected error. A tripwire status means a processor intentionally stopped execution (e.g., for content moderation).
25
+
26
+ - Updated dependencies [[`3bf6c5f`](https://github.com/mastra-ai/mastra/commit/3bf6c5f104c25226cd84e0c77f9dec15f2cac2db)]:
27
+ - @mastra/client-js@1.0.0-beta.11
28
+
29
+ ## 0.1.0-beta.10
30
+
31
+ ### Minor Changes
32
+
33
+ - 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))
34
+
35
+ ### Patch Changes
36
+
37
+ - fix: persist data-\* chunks from writer.custom() to memory storage ([#10884](https://github.com/mastra-ai/mastra/pull/10884))
38
+ - Add persistence for custom data chunks (`data-*` parts) emitted via `writer.custom()` in tools
39
+ - Data chunks are now saved to message storage so they survive page refreshes
40
+ - Update `@assistant-ui/react` to v0.11.47 with native `DataMessagePart` support
41
+ - Convert `data-*` parts to `DataMessagePart` format (`{ type: 'data', name: string, data: T }`)
42
+ - Update related `@assistant-ui/*` packages for compatibility
43
+
44
+ - Updated dependencies [[`261473a`](https://github.com/mastra-ai/mastra/commit/261473ac637e633064a22076671e2e02b002214d)]:
45
+ - @mastra/client-js@1.0.0-beta.10
46
+
47
+ ## 0.1.0-beta.9
48
+
49
+ ### Patch Changes
50
+
51
+ - Updated dependencies [[`5a1ede1`](https://github.com/mastra-ai/mastra/commit/5a1ede1f7ab527b9ead11f7eee2f73e67aeca9e4)]:
52
+ - @mastra/client-js@1.0.0-beta.9
53
+
54
+ ## 0.1.0-beta.8
55
+
56
+ ### Patch Changes
57
+
58
+ - Updated dependencies:
59
+ - @mastra/client-js@1.0.0-beta.8
60
+
61
+ ## 0.1.0-beta.7
62
+
63
+ ### Patch Changes
64
+
65
+ - Updated dependencies [[`5fe71bc`](https://github.com/mastra-ai/mastra/commit/5fe71bc925dfce597df69c89241f33b378028c63), [`21735a7`](https://github.com/mastra-ai/mastra/commit/21735a7ef306963554a69a89b44f06c3bcd85141)]:
66
+ - @mastra/client-js@1.0.0-beta.7
67
+
68
+ ## 0.1.0-beta.6
69
+
70
+ ### Patch Changes
71
+
72
+ - Adjust the types to accept tracingOptions ([#10742](https://github.com/mastra-ai/mastra/pull/10742))
73
+
74
+ - 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)]:
75
+ - @mastra/client-js@1.0.0-beta.6
76
+
77
+ ## 0.1.0-beta.5
78
+
79
+ ### Patch Changes
80
+
81
+ - Configurable resourceId in react useChat ([#10461](https://github.com/mastra-ai/mastra/pull/10461))
82
+
83
+ - fix(agent): persist messages before tool suspension ([#10369](https://github.com/mastra-ai/mastra/pull/10369))
84
+
85
+ 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.
86
+
87
+ **Backend changes (@mastra/core):**
88
+ - Add assistant messages to messageList immediately after LLM execution
89
+ - Flush messages synchronously before suspension to persist state
90
+ - Create thread if it doesn't exist before flushing
91
+ - Add metadata helpers to persist and remove tool approval state
92
+ - Pass saveQueueManager and memory context through workflow for immediate persistence
93
+
94
+ **Frontend changes (@mastra/react):**
95
+ - Extract runId from pending approvals to enable resumption after refresh
96
+ - Convert `pendingToolApprovals` (DB format) to `requireApprovalMetadata` (runtime format)
97
+ - Handle both `dynamic-tool` and `tool-{NAME}` part types for approval state
98
+ - Change runId from hardcoded `agentId` to unique `uuid()`
99
+
100
+ **UI changes (@mastra/playground-ui):**
101
+ - Handle tool calls awaiting approval in message initialization
102
+ - Convert approval metadata format when loading initial messages
103
+
104
+ Fixes #9745, #9906
105
+
106
+ - Updated dependencies [[`898a972`](https://github.com/mastra-ai/mastra/commit/898a9727d286c2510d6b702dfd367e6aaf5c6b0f)]:
107
+ - @mastra/client-js@1.0.0-beta.5
108
+
109
+ ## 0.1.0-beta.4
110
+
111
+ ### Patch Changes
112
+
113
+ - Updated dependencies [[`6a86fe5`](https://github.com/mastra-ai/mastra/commit/6a86fe56b8ff53ca2eb3ed87ffc0748749ebadce), [`595a3b8`](https://github.com/mastra-ai/mastra/commit/595a3b8727c901f44e333909c09843c711224440)]:
114
+ - @mastra/client-js@1.0.0-beta.4
115
+
116
+ ## 0.1.0-beta.3
117
+
118
+ ### Patch Changes
119
+
120
+ - Updated dependencies [[`e1bb9c9`](https://github.com/mastra-ai/mastra/commit/e1bb9c94b4eb68b019ae275981be3feb769b5365)]:
121
+ - @mastra/client-js@1.0.0-beta.3
122
+
123
+ ## 0.1.0-beta.2
124
+
125
+ ### Patch Changes
126
+
127
+ - Updated dependencies []:
128
+ - @mastra/client-js@1.0.0-beta.2
129
+
3
130
  ## 0.1.0-beta.1
4
131
 
5
132
  ### 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');
@@ -50,7 +51,7 @@ const mapWorkflowStreamChunkToWatchResult = (prev, chunk) => {
50
51
  return {
51
52
  ...prev,
52
53
  status: chunk.payload.workflowStatus,
53
- ...finalStatus === "success" && lastStep?.status === "success" ? { result: lastStep?.output } : finalStatus === "failed" && lastStep?.status === "failed" ? { error: lastStep?.error } : {}
54
+ ...finalStatus === "success" && lastStep?.status === "success" ? { result: lastStep?.output } : finalStatus === "failed" && lastStep?.status === "failed" ? { error: lastStep?.error } : finalStatus === "tripwire" && chunk.payload.tripwire ? { tripwire: chunk.payload.tripwire } : {}
54
55
  };
55
56
  }
56
57
  const { stepCallId, stepName, ...newPayload } = chunk.payload ?? {};
@@ -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 = {
@@ -110,12 +139,17 @@ const toUIMessage = ({ chunk, conversation, metadata }) => {
110
139
  parts: [
111
140
  {
112
141
  type: "text",
113
- text: chunk.payload.tripwireReason
142
+ text: chunk.payload.reason
114
143
  }
115
144
  ],
116
145
  metadata: {
117
146
  ...metadata,
118
- status: "warning"
147
+ status: "tripwire",
148
+ tripwire: {
149
+ retry: chunk.payload.retry,
150
+ tripwirePayload: chunk.payload.metadata,
151
+ processorId: chunk.payload.processorId
152
+ }
119
153
  }
120
154
  };
121
155
  return [...result, newMessage];
@@ -259,17 +293,19 @@ const toUIMessage = ({ chunk, conversation, metadata }) => {
259
293
  if (!lastMessage || lastMessage.role !== "assistant") return result;
260
294
  const parts = [...lastMessage.parts];
261
295
  const toolPartIndex = parts.findIndex(
262
- (part) => part.type === "dynamic-tool" && "toolCallId" in part && part.toolCallId === chunk.payload.toolCallId
296
+ (part) => (part.type === "dynamic-tool" || typeof part.type === "string" && part.type.startsWith("tool-")) && "toolCallId" in part && part.toolCallId === chunk.payload.toolCallId
263
297
  );
264
298
  if (toolPartIndex !== -1) {
265
299
  const toolPart = parts[toolPartIndex];
266
- if (toolPart.type === "dynamic-tool") {
300
+ if (toolPart.type === "dynamic-tool" || typeof toolPart.type === "string" && toolPart.type.startsWith("tool-")) {
301
+ const toolName = "toolName" in toolPart && typeof toolPart.toolName === "string" ? toolPart.toolName : toolPart.type.startsWith("tool-") ? toolPart.type.substring(5) : "";
302
+ const toolCallId = toolPart.toolCallId;
267
303
  if (chunk.type === "tool-result" && chunk.payload.isError || chunk.type === "tool-error") {
268
304
  const error = chunk.type === "tool-error" ? chunk.payload.error : chunk.payload.result;
269
305
  parts[toolPartIndex] = {
270
306
  type: "dynamic-tool",
271
- toolName: toolPart.toolName,
272
- toolCallId: toolPart.toolCallId,
307
+ toolName,
308
+ toolCallId,
273
309
  state: "output-error",
274
310
  input: toolPart.input,
275
311
  errorText: String(error),
@@ -288,8 +324,8 @@ const toUIMessage = ({ chunk, conversation, metadata }) => {
288
324
  }
289
325
  parts[toolPartIndex] = {
290
326
  type: "dynamic-tool",
291
- toolName: toolPart.toolName,
292
- toolCallId: toolPart.toolCallId,
327
+ toolName,
328
+ toolCallId,
293
329
  state: "output-available",
294
330
  input: toolPart.input,
295
331
  output,
@@ -311,11 +347,14 @@ const toUIMessage = ({ chunk, conversation, metadata }) => {
311
347
  if (!lastMessage || lastMessage.role !== "assistant") return result;
312
348
  const parts = [...lastMessage.parts];
313
349
  const toolPartIndex = parts.findIndex(
314
- (part) => part.type === "dynamic-tool" && "toolCallId" in part && part.toolCallId === chunk.payload.toolCallId
350
+ (part) => (part.type === "dynamic-tool" || typeof part.type === "string" && part.type.startsWith("tool-")) && "toolCallId" in part && part.toolCallId === chunk.payload.toolCallId
315
351
  );
316
352
  if (toolPartIndex !== -1) {
317
353
  const toolPart = parts[toolPartIndex];
318
- if (toolPart.type === "dynamic-tool") {
354
+ if (toolPart.type === "dynamic-tool" || typeof toolPart.type === "string" && toolPart.type.startsWith("tool-")) {
355
+ const toolName = "toolName" in toolPart && typeof toolPart.toolName === "string" ? toolPart.toolName : typeof toolPart.type === "string" && toolPart.type.startsWith("tool-") ? toolPart.type.substring(5) : "";
356
+ const toolCallId = toolPart.toolCallId;
357
+ const input = toolPart.input;
319
358
  if (chunk.payload.output?.type?.startsWith("workflow-")) {
320
359
  const existingWorkflowState = toolPart.output || {};
321
360
  const updatedWorkflowState = mapWorkflowStreamChunkToWatchResult(
@@ -323,7 +362,11 @@ const toUIMessage = ({ chunk, conversation, metadata }) => {
323
362
  chunk.payload.output
324
363
  );
325
364
  parts[toolPartIndex] = {
326
- ...toolPart,
365
+ type: "dynamic-tool",
366
+ toolName,
367
+ toolCallId,
368
+ state: "input-streaming",
369
+ input,
327
370
  output: updatedWorkflowState
328
371
  };
329
372
  } else if (chunk.payload.output?.from === "AGENT" || chunk.payload.output?.from === "USER" && chunk.payload.output?.payload?.output?.type?.startsWith("workflow-")) {
@@ -332,7 +375,11 @@ const toUIMessage = ({ chunk, conversation, metadata }) => {
332
375
  const currentOutput = toolPart.output || [];
333
376
  const existingOutput = Array.isArray(currentOutput) ? currentOutput : [];
334
377
  parts[toolPartIndex] = {
335
- ...toolPart,
378
+ type: "dynamic-tool",
379
+ toolName,
380
+ toolCallId,
381
+ state: "input-streaming",
382
+ input,
336
383
  output: [...existingOutput, chunk.payload.output]
337
384
  };
338
385
  }
@@ -655,6 +702,29 @@ const toAssistantUIMessage = (message) => {
655
702
  }
656
703
  return baseToolCall;
657
704
  }
705
+ const requireApprovalMetadata = extendedMessage.metadata?.requireApprovalMetadata;
706
+ const partToolCallId = "toolCallId" in part && typeof part.toolCallId === "string" ? part.toolCallId : void 0;
707
+ const suspensionData = partToolCallId ? requireApprovalMetadata?.[partToolCallId] : void 0;
708
+ if (suspensionData) {
709
+ const toolName = "toolName" in part && typeof part.toolName === "string" ? part.toolName : part.type.startsWith("tool-") ? part.type.substring(5) : "";
710
+ return {
711
+ type: "tool-call",
712
+ toolCallId: partToolCallId,
713
+ toolName,
714
+ argsText: "input" in part ? JSON.stringify(part.input) : "{}",
715
+ args: "input" in part ? part.input : {},
716
+ metadata: extendedMessage.metadata
717
+ };
718
+ }
719
+ if (part.type.startsWith("data-")) {
720
+ return {
721
+ type: "data",
722
+ name: part.type.substring(5),
723
+ // Extract name from 'data-{name}'
724
+ data: part.data,
725
+ metadata: message.metadata
726
+ };
727
+ }
658
728
  return {
659
729
  type: "text",
660
730
  text: "",
@@ -741,7 +811,6 @@ const resolveInitialMessages = (messages) => {
741
811
  childMessages,
742
812
  result: finalResult?.text || ""
743
813
  };
744
- console.log("json", json);
745
814
  const nextMessage = {
746
815
  role: "assistant",
747
816
  parts: [
@@ -769,6 +838,18 @@ const resolveInitialMessages = (messages) => {
769
838
  return message;
770
839
  }
771
840
  }
841
+ const extendedMessage = message;
842
+ const pendingToolApprovals = extendedMessage.metadata?.pendingToolApprovals;
843
+ if (pendingToolApprovals && typeof pendingToolApprovals === "object") {
844
+ return {
845
+ ...message,
846
+ metadata: {
847
+ ...message.metadata,
848
+ mode: "stream",
849
+ requireApprovalMetadata: pendingToolApprovals
850
+ }
851
+ };
852
+ }
772
853
  return message;
773
854
  });
774
855
  };
@@ -1217,12 +1298,24 @@ const fromCoreUserMessageToUIMessage = (coreUserMessage) => {
1217
1298
  };
1218
1299
  };
1219
1300
 
1220
- const useChat = ({ agentId, initializeMessages }) => {
1221
- const _currentRunId = react.useRef(void 0);
1301
+ const useChat = ({ agentId, resourceId, initializeMessages }) => {
1302
+ const extractRunIdFromMessages = (messages2) => {
1303
+ for (const message of messages2) {
1304
+ const pendingToolApprovals = message.metadata?.pendingToolApprovals;
1305
+ if (pendingToolApprovals && typeof pendingToolApprovals === "object") {
1306
+ const suspensionData = Object.values(pendingToolApprovals)[0];
1307
+ if (suspensionData?.runId) {
1308
+ return suspensionData.runId;
1309
+ }
1310
+ }
1311
+ }
1312
+ return void 0;
1313
+ };
1314
+ const initialMessages = initializeMessages?.() || [];
1315
+ const initialRunId = extractRunIdFromMessages(initialMessages);
1316
+ const _currentRunId = react.useRef(initialRunId);
1222
1317
  const _onChunk = react.useRef(void 0);
1223
- const [messages, setMessages] = react.useState(
1224
- () => resolveInitialMessages(initializeMessages?.() || [])
1225
- );
1318
+ const [messages, setMessages] = react.useState(() => resolveInitialMessages(initialMessages));
1226
1319
  const [toolCallApprovals, setToolCallApprovals] = react.useState({});
1227
1320
  const baseClient = useMastraClient();
1228
1321
  const [isRunning, setIsRunning] = react.useState(false);
@@ -1232,7 +1325,8 @@ const useChat = ({ agentId, initializeMessages }) => {
1232
1325
  threadId,
1233
1326
  modelSettings,
1234
1327
  signal,
1235
- onFinish
1328
+ onFinish,
1329
+ tracingOptions
1236
1330
  }) => {
1237
1331
  const {
1238
1332
  frequencyPenalty,
@@ -1254,7 +1348,7 @@ const useChat = ({ agentId, initializeMessages }) => {
1254
1348
  const agent = clientWithAbort.getAgent(agentId);
1255
1349
  const response = await agent.generate({
1256
1350
  messages: coreUserMessages,
1257
- runId: agentId,
1351
+ runId: uuid.v4(),
1258
1352
  maxSteps,
1259
1353
  modelSettings: {
1260
1354
  frequencyPenalty,
@@ -1267,8 +1361,9 @@ const useChat = ({ agentId, initializeMessages }) => {
1267
1361
  },
1268
1362
  instructions,
1269
1363
  requestContext,
1270
- ...threadId ? { threadId, resourceId: agentId } : {},
1271
- providerOptions
1364
+ ...threadId ? { threadId, resourceId: resourceId || agentId } : {},
1365
+ providerOptions,
1366
+ tracingOptions
1272
1367
  });
1273
1368
  setIsRunning(false);
1274
1369
  if (response && "uiMessages" in response.response && response.response.uiMessages) {
@@ -1282,7 +1377,15 @@ const useChat = ({ agentId, initializeMessages }) => {
1282
1377
  setMessages((prev) => [...prev, ...mastraUIMessages]);
1283
1378
  }
1284
1379
  };
1285
- const stream = async ({ coreUserMessages, requestContext, threadId, onChunk, modelSettings, signal }) => {
1380
+ const stream = async ({
1381
+ coreUserMessages,
1382
+ requestContext,
1383
+ threadId,
1384
+ onChunk,
1385
+ modelSettings,
1386
+ signal,
1387
+ tracingOptions
1388
+ }) => {
1286
1389
  const {
1287
1390
  frequencyPenalty,
1288
1391
  presencePenalty,
@@ -1302,7 +1405,7 @@ const useChat = ({ agentId, initializeMessages }) => {
1302
1405
  abortSignal: signal
1303
1406
  });
1304
1407
  const agent = clientWithAbort.getAgent(agentId);
1305
- const runId = agentId;
1408
+ const runId = uuid.v4();
1306
1409
  const response = await agent.stream({
1307
1410
  messages: coreUserMessages,
1308
1411
  runId,
@@ -1318,9 +1421,10 @@ const useChat = ({ agentId, initializeMessages }) => {
1318
1421
  },
1319
1422
  instructions,
1320
1423
  requestContext,
1321
- ...threadId ? { threadId, resourceId: agentId } : {},
1424
+ ...threadId ? { threadId, resourceId: resourceId || agentId } : {},
1322
1425
  providerOptions,
1323
- requireToolApproval
1426
+ requireToolApproval,
1427
+ tracingOptions
1324
1428
  });
1325
1429
  _onChunk.current = onChunk;
1326
1430
  _currentRunId.current = runId;
@@ -1338,7 +1442,8 @@ const useChat = ({ agentId, initializeMessages }) => {
1338
1442
  threadId,
1339
1443
  onNetworkChunk,
1340
1444
  modelSettings,
1341
- signal
1445
+ signal,
1446
+ tracingOptions
1342
1447
  }) => {
1343
1448
  const { frequencyPenalty, presencePenalty, maxRetries, maxTokens, temperature, topK, topP, maxSteps } = modelSettings || {};
1344
1449
  setIsRunning(true);
@@ -1347,6 +1452,7 @@ const useChat = ({ agentId, initializeMessages }) => {
1347
1452
  abortSignal: signal
1348
1453
  });
1349
1454
  const agent = clientWithAbort.getAgent(agentId);
1455
+ const runId = uuid.v4();
1350
1456
  const response = await agent.network({
1351
1457
  messages: coreUserMessages,
1352
1458
  maxSteps,
@@ -1359,9 +1465,10 @@ const useChat = ({ agentId, initializeMessages }) => {
1359
1465
  topK,
1360
1466
  topP
1361
1467
  },
1362
- runId: agentId,
1468
+ runId,
1363
1469
  requestContext,
1364
- ...threadId ? { thread: threadId, resourceId: agentId } : {}
1470
+ ...threadId ? { thread: threadId, resourceId: resourceId || agentId } : {},
1471
+ tracingOptions
1365
1472
  });
1366
1473
  const transformer = new AISdkNetworkTransformer();
1367
1474
  await response.processDataStream({