@mastra/react 0.0.0-refactor-agent-information-for-recomposable-ui-20251112151814 → 0.0.0-safe-stringify-telemetry-20251205024938

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/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { jsx, jsxs } from 'react/jsx-runtime';
2
2
  import { createContext, useContext, useRef, useState, Fragment, useLayoutEffect, useEffect } from 'react';
3
3
  import { MastraClient } from '@mastra/client-js';
4
+ import { v4 } from '@lukeed/uuid';
4
5
  import { ChevronDownIcon, CheckIcon, CopyIcon } from 'lucide-react';
5
6
  import { twMerge } from 'tailwind-merge';
6
7
  import { toJsxRuntime } from 'hast-util-to-jsx-runtime';
@@ -255,17 +256,19 @@ const toUIMessage = ({ chunk, conversation, metadata }) => {
255
256
  if (!lastMessage || lastMessage.role !== "assistant") return result;
256
257
  const parts = [...lastMessage.parts];
257
258
  const toolPartIndex = parts.findIndex(
258
- (part) => part.type === "dynamic-tool" && "toolCallId" in part && part.toolCallId === chunk.payload.toolCallId
259
+ (part) => (part.type === "dynamic-tool" || typeof part.type === "string" && part.type.startsWith("tool-")) && "toolCallId" in part && part.toolCallId === chunk.payload.toolCallId
259
260
  );
260
261
  if (toolPartIndex !== -1) {
261
262
  const toolPart = parts[toolPartIndex];
262
- if (toolPart.type === "dynamic-tool") {
263
+ if (toolPart.type === "dynamic-tool" || typeof toolPart.type === "string" && toolPart.type.startsWith("tool-")) {
264
+ const toolName = "toolName" in toolPart && typeof toolPart.toolName === "string" ? toolPart.toolName : toolPart.type.startsWith("tool-") ? toolPart.type.substring(5) : "";
265
+ const toolCallId = toolPart.toolCallId;
263
266
  if (chunk.type === "tool-result" && chunk.payload.isError || chunk.type === "tool-error") {
264
267
  const error = chunk.type === "tool-error" ? chunk.payload.error : chunk.payload.result;
265
268
  parts[toolPartIndex] = {
266
269
  type: "dynamic-tool",
267
- toolName: toolPart.toolName,
268
- toolCallId: toolPart.toolCallId,
270
+ toolName,
271
+ toolCallId,
269
272
  state: "output-error",
270
273
  input: toolPart.input,
271
274
  errorText: String(error),
@@ -284,8 +287,8 @@ const toUIMessage = ({ chunk, conversation, metadata }) => {
284
287
  }
285
288
  parts[toolPartIndex] = {
286
289
  type: "dynamic-tool",
287
- toolName: toolPart.toolName,
288
- toolCallId: toolPart.toolCallId,
290
+ toolName,
291
+ toolCallId,
289
292
  state: "output-available",
290
293
  input: toolPart.input,
291
294
  output,
@@ -307,11 +310,14 @@ const toUIMessage = ({ chunk, conversation, metadata }) => {
307
310
  if (!lastMessage || lastMessage.role !== "assistant") return result;
308
311
  const parts = [...lastMessage.parts];
309
312
  const toolPartIndex = parts.findIndex(
310
- (part) => part.type === "dynamic-tool" && "toolCallId" in part && part.toolCallId === chunk.payload.toolCallId
313
+ (part) => (part.type === "dynamic-tool" || typeof part.type === "string" && part.type.startsWith("tool-")) && "toolCallId" in part && part.toolCallId === chunk.payload.toolCallId
311
314
  );
312
315
  if (toolPartIndex !== -1) {
313
316
  const toolPart = parts[toolPartIndex];
314
- if (toolPart.type === "dynamic-tool") {
317
+ if (toolPart.type === "dynamic-tool" || typeof toolPart.type === "string" && toolPart.type.startsWith("tool-")) {
318
+ const toolName = "toolName" in toolPart && typeof toolPart.toolName === "string" ? toolPart.toolName : typeof toolPart.type === "string" && toolPart.type.startsWith("tool-") ? toolPart.type.substring(5) : "";
319
+ const toolCallId = toolPart.toolCallId;
320
+ const input = toolPart.input;
315
321
  if (chunk.payload.output?.type?.startsWith("workflow-")) {
316
322
  const existingWorkflowState = toolPart.output || {};
317
323
  const updatedWorkflowState = mapWorkflowStreamChunkToWatchResult(
@@ -319,7 +325,11 @@ const toUIMessage = ({ chunk, conversation, metadata }) => {
319
325
  chunk.payload.output
320
326
  );
321
327
  parts[toolPartIndex] = {
322
- ...toolPart,
328
+ type: "dynamic-tool",
329
+ toolName,
330
+ toolCallId,
331
+ state: "input-streaming",
332
+ input,
323
333
  output: updatedWorkflowState
324
334
  };
325
335
  } else if (chunk.payload.output?.from === "AGENT" || chunk.payload.output?.from === "USER" && chunk.payload.output?.payload?.output?.type?.startsWith("workflow-")) {
@@ -328,7 +338,11 @@ const toUIMessage = ({ chunk, conversation, metadata }) => {
328
338
  const currentOutput = toolPart.output || [];
329
339
  const existingOutput = Array.isArray(currentOutput) ? currentOutput : [];
330
340
  parts[toolPartIndex] = {
331
- ...toolPart,
341
+ type: "dynamic-tool",
342
+ toolName,
343
+ toolCallId,
344
+ state: "input-streaming",
345
+ input,
332
346
  output: [...existingOutput, chunk.payload.output]
333
347
  };
334
348
  }
@@ -424,10 +438,11 @@ const toUIMessage = ({ chunk, conversation, metadata }) => {
424
438
  const lastMessage = result[result.length - 1];
425
439
  if (!lastMessage || lastMessage.role !== "assistant") return result;
426
440
  const parts = lastMessage.parts.map((part) => {
427
- if (typeof part === "object" && part !== null && "type" in part && "state" in part && part.state === "streaming") {
428
- if (part.type === "text" || part.type === "reasoning") {
429
- return { ...part, state: "done" };
430
- }
441
+ if (part.type === "text" && part.state === "streaming") {
442
+ return { ...part, state: "done" };
443
+ }
444
+ if (part.type === "reasoning" && part.state === "streaming") {
445
+ return { ...part, state: "done" };
431
446
  }
432
447
  return part;
433
448
  });
@@ -599,23 +614,13 @@ const toAssistantUIMessage = (message) => {
599
614
  };
600
615
  }
601
616
  if (part.type === "file") {
602
- const type = part.mediaType.includes("image/") ? "image" : "file";
603
- if (type === "file") {
604
- return {
605
- type,
606
- mimeType: part.mediaType,
607
- data: part.url,
608
- // Use URL as data source
609
- metadata: message.metadata
610
- };
611
- }
612
- if (type === "image") {
613
- return {
614
- type,
615
- image: part.url,
616
- metadata: message.metadata
617
- };
618
- }
617
+ return {
618
+ type: "file",
619
+ mimeType: part.mediaType,
620
+ data: part.url,
621
+ // Use URL as data source
622
+ metadata: message.metadata
623
+ };
619
624
  }
620
625
  if (part.type === "dynamic-tool") {
621
626
  const baseToolCall = {
@@ -651,6 +656,20 @@ const toAssistantUIMessage = (message) => {
651
656
  }
652
657
  return baseToolCall;
653
658
  }
659
+ const requireApprovalMetadata = extendedMessage.metadata?.requireApprovalMetadata;
660
+ const partToolCallId = "toolCallId" in part && typeof part.toolCallId === "string" ? part.toolCallId : void 0;
661
+ const suspensionData = partToolCallId ? requireApprovalMetadata?.[partToolCallId] : void 0;
662
+ if (suspensionData) {
663
+ const toolName = "toolName" in part && typeof part.toolName === "string" ? part.toolName : part.type.startsWith("tool-") ? part.type.substring(5) : "";
664
+ return {
665
+ type: "tool-call",
666
+ toolCallId: partToolCallId,
667
+ toolName,
668
+ argsText: "input" in part ? JSON.stringify(part.input) : "{}",
669
+ args: "input" in part ? part.input : {},
670
+ metadata: extendedMessage.metadata
671
+ };
672
+ }
654
673
  return {
655
674
  type: "text",
656
675
  text: "",
@@ -692,9 +711,7 @@ const toAssistantUIMessage = (message) => {
692
711
 
693
712
  const resolveInitialMessages = (messages) => {
694
713
  return messages.map((message) => {
695
- const networkPart = message.parts.find(
696
- (part) => typeof part === "object" && part !== null && "type" in part && part.type === "text" && "text" in part && typeof part.text === "string" && part.text.includes('"isNetwork":true')
697
- );
714
+ const networkPart = message.parts.find((part) => part.type === "text" && part.text.includes('"isNetwork":true'));
698
715
  if (networkPart && networkPart.type === "text") {
699
716
  try {
700
717
  const json = JSON.parse(networkPart.text);
@@ -737,7 +754,6 @@ const resolveInitialMessages = (messages) => {
737
754
  childMessages,
738
755
  result: finalResult?.text || ""
739
756
  };
740
- console.log("json", json);
741
757
  const nextMessage = {
742
758
  role: "assistant",
743
759
  parts: [
@@ -765,6 +781,18 @@ const resolveInitialMessages = (messages) => {
765
781
  return message;
766
782
  }
767
783
  }
784
+ const extendedMessage = message;
785
+ const pendingToolApprovals = extendedMessage.metadata?.pendingToolApprovals;
786
+ if (pendingToolApprovals && typeof pendingToolApprovals === "object") {
787
+ return {
788
+ ...message,
789
+ metadata: {
790
+ ...message.metadata,
791
+ mode: "stream",
792
+ requireApprovalMetadata: pendingToolApprovals
793
+ }
794
+ };
795
+ }
768
796
  return message;
769
797
  });
770
798
  };
@@ -1168,63 +1196,30 @@ class AISdkNetworkTransformer {
1168
1196
  };
1169
1197
  }
1170
1198
 
1171
- const fromCoreUserMessageToUIMessage = (coreUserMessage) => {
1172
- const id = `user-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
1173
- const parts = typeof coreUserMessage.content === "string" ? [
1174
- {
1175
- type: "text",
1176
- text: coreUserMessage.content
1177
- }
1178
- ] : coreUserMessage.content.map((part) => {
1179
- switch (part.type) {
1180
- case "text": {
1181
- return {
1182
- type: "text",
1183
- text: part.text
1184
- };
1185
- }
1186
- case "image": {
1187
- const url = typeof part.image === "string" ? part.image : part.image instanceof URL ? part.image.toString() : "";
1188
- return {
1189
- type: "file",
1190
- mediaType: part.mimeType ?? "image/*",
1191
- url
1192
- };
1193
- }
1194
- case "file": {
1195
- const url = typeof part.data === "string" ? part.data : part.data instanceof URL ? part.data.toString() : "";
1196
- return {
1197
- type: "file",
1198
- mediaType: part.mimeType,
1199
- url,
1200
- ...part.filename !== void 0 ? { filename: part.filename } : {}
1201
- };
1202
- }
1203
- default: {
1204
- const exhaustiveCheck = part;
1205
- throw new Error(`Unhandled content part type: ${exhaustiveCheck.type}`);
1199
+ const useChat = ({ agentId, resourceId, initializeMessages }) => {
1200
+ const extractRunIdFromMessages = (messages2) => {
1201
+ for (const message of messages2) {
1202
+ const pendingToolApprovals = message.metadata?.pendingToolApprovals;
1203
+ if (pendingToolApprovals && typeof pendingToolApprovals === "object") {
1204
+ const suspensionData = Object.values(pendingToolApprovals)[0];
1205
+ if (suspensionData?.runId) {
1206
+ return suspensionData.runId;
1207
+ }
1206
1208
  }
1207
1209
  }
1208
- });
1209
- return {
1210
- id,
1211
- role: "user",
1212
- parts
1210
+ return void 0;
1213
1211
  };
1214
- };
1215
-
1216
- const useChat = ({ agentId, initializeMessages }) => {
1217
- const _currentRunId = useRef(void 0);
1212
+ const initialMessages = initializeMessages?.() || [];
1213
+ const initialRunId = extractRunIdFromMessages(initialMessages);
1214
+ const _currentRunId = useRef(initialRunId);
1218
1215
  const _onChunk = useRef(void 0);
1219
- const [messages, setMessages] = useState(
1220
- () => resolveInitialMessages(initializeMessages?.() || [])
1221
- );
1216
+ const [messages, setMessages] = useState(() => resolveInitialMessages(initialMessages));
1222
1217
  const [toolCallApprovals, setToolCallApprovals] = useState({});
1223
1218
  const baseClient = useMastraClient();
1224
1219
  const [isRunning, setIsRunning] = useState(false);
1225
1220
  const generate = async ({
1226
1221
  coreUserMessages,
1227
- requestContext,
1222
+ runtimeContext,
1228
1223
  threadId,
1229
1224
  modelSettings,
1230
1225
  signal,
@@ -1250,7 +1245,7 @@ const useChat = ({ agentId, initializeMessages }) => {
1250
1245
  const agent = clientWithAbort.getAgent(agentId);
1251
1246
  const response = await agent.generate({
1252
1247
  messages: coreUserMessages,
1253
- runId: agentId,
1248
+ runId: v4(),
1254
1249
  maxSteps,
1255
1250
  modelSettings: {
1256
1251
  frequencyPenalty,
@@ -1262,8 +1257,8 @@ const useChat = ({ agentId, initializeMessages }) => {
1262
1257
  topP
1263
1258
  },
1264
1259
  instructions,
1265
- requestContext,
1266
- ...threadId ? { threadId, resourceId: agentId } : {},
1260
+ runtimeContext,
1261
+ ...threadId ? { threadId, resourceId: resourceId || agentId } : {},
1267
1262
  providerOptions
1268
1263
  });
1269
1264
  setIsRunning(false);
@@ -1278,7 +1273,7 @@ const useChat = ({ agentId, initializeMessages }) => {
1278
1273
  setMessages((prev) => [...prev, ...mastraUIMessages]);
1279
1274
  }
1280
1275
  };
1281
- const stream = async ({ coreUserMessages, requestContext, threadId, onChunk, modelSettings, signal }) => {
1276
+ const stream = async ({ coreUserMessages, runtimeContext, threadId, onChunk, modelSettings, signal }) => {
1282
1277
  const {
1283
1278
  frequencyPenalty,
1284
1279
  presencePenalty,
@@ -1298,7 +1293,7 @@ const useChat = ({ agentId, initializeMessages }) => {
1298
1293
  abortSignal: signal
1299
1294
  });
1300
1295
  const agent = clientWithAbort.getAgent(agentId);
1301
- const runId = agentId;
1296
+ const runId = v4();
1302
1297
  const response = await agent.stream({
1303
1298
  messages: coreUserMessages,
1304
1299
  runId,
@@ -1313,8 +1308,8 @@ const useChat = ({ agentId, initializeMessages }) => {
1313
1308
  topP
1314
1309
  },
1315
1310
  instructions,
1316
- requestContext,
1317
- ...threadId ? { threadId, resourceId: agentId } : {},
1311
+ runtimeContext,
1312
+ ...threadId ? { threadId, resourceId: resourceId || agentId } : {},
1318
1313
  providerOptions,
1319
1314
  requireToolApproval
1320
1315
  });
@@ -1330,7 +1325,7 @@ const useChat = ({ agentId, initializeMessages }) => {
1330
1325
  };
1331
1326
  const network = async ({
1332
1327
  coreUserMessages,
1333
- requestContext,
1328
+ runtimeContext,
1334
1329
  threadId,
1335
1330
  onNetworkChunk,
1336
1331
  modelSettings,
@@ -1343,6 +1338,7 @@ const useChat = ({ agentId, initializeMessages }) => {
1343
1338
  abortSignal: signal
1344
1339
  });
1345
1340
  const agent = clientWithAbort.getAgent(agentId);
1341
+ const runId = v4();
1346
1342
  const response = await agent.network({
1347
1343
  messages: coreUserMessages,
1348
1344
  maxSteps,
@@ -1355,9 +1351,9 @@ const useChat = ({ agentId, initializeMessages }) => {
1355
1351
  topK,
1356
1352
  topP
1357
1353
  },
1358
- runId: agentId,
1359
- requestContext,
1360
- ...threadId ? { thread: threadId, resourceId: agentId } : {}
1354
+ runId,
1355
+ runtimeContext,
1356
+ ...threadId ? { thread: threadId, resourceId: resourceId || agentId } : {}
1361
1357
  });
1362
1358
  const transformer = new AISdkNetworkTransformer();
1363
1359
  await response.processDataStream({
@@ -1409,18 +1405,14 @@ const useChat = ({ agentId, initializeMessages }) => {
1409
1405
  };
1410
1406
  const sendMessage = async ({ mode = "stream", ...args }) => {
1411
1407
  const nextMessage = { role: "user", content: [{ type: "text", text: args.message }] };
1412
- const coreUserMessages = [nextMessage];
1413
- if (args.coreUserMessages) {
1414
- coreUserMessages.push(...args.coreUserMessages);
1415
- }
1416
- const uiMessages = coreUserMessages.map(fromCoreUserMessageToUIMessage);
1417
- setMessages((s) => [...s, ...uiMessages]);
1408
+ const messages2 = args.coreUserMessages ? [nextMessage, ...args.coreUserMessages] : [nextMessage];
1409
+ setMessages((s) => [...s, { role: "user", parts: [{ type: "text", text: args.message }] }]);
1418
1410
  if (mode === "generate") {
1419
- await generate({ ...args, coreUserMessages });
1411
+ await generate({ ...args, coreUserMessages: messages2 });
1420
1412
  } else if (mode === "stream") {
1421
- await stream({ ...args, coreUserMessages });
1413
+ await stream({ ...args, coreUserMessages: messages2 });
1422
1414
  } else if (mode === "network") {
1423
- await network({ ...args, coreUserMessages });
1415
+ await network({ ...args, coreUserMessages: messages2 });
1424
1416
  }
1425
1417
  };
1426
1418
  return {