@mastra/react 0.0.0-monorepo-binary-20251013210052 → 0.0.0-refactor-agent-information-for-recomposable-ui-20251112151814

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.cjs CHANGED
@@ -5,7 +5,6 @@ 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 reactDom = require('react-dom');
9
8
  const lucideReact = require('lucide-react');
10
9
  const tailwindMerge = require('tailwind-merge');
11
10
  const hastUtilToJsxRuntime = require('hast-util-to-jsx-runtime');
@@ -254,6 +253,7 @@ const toUIMessage = ({ chunk, conversation, metadata }) => {
254
253
  }
255
254
  ];
256
255
  }
256
+ case "tool-error":
257
257
  case "tool-result": {
258
258
  const lastMessage = result[result.length - 1];
259
259
  if (!lastMessage || lastMessage.role !== "assistant") return result;
@@ -264,25 +264,35 @@ const toUIMessage = ({ chunk, conversation, metadata }) => {
264
264
  if (toolPartIndex !== -1) {
265
265
  const toolPart = parts[toolPartIndex];
266
266
  if (toolPart.type === "dynamic-tool") {
267
- if (chunk.payload.isError) {
267
+ if (chunk.type === "tool-result" && chunk.payload.isError || chunk.type === "tool-error") {
268
+ const error = chunk.type === "tool-error" ? chunk.payload.error : chunk.payload.result;
268
269
  parts[toolPartIndex] = {
269
270
  type: "dynamic-tool",
270
271
  toolName: toolPart.toolName,
271
272
  toolCallId: toolPart.toolCallId,
272
273
  state: "output-error",
273
274
  input: toolPart.input,
274
- errorText: String(chunk.payload.result),
275
+ errorText: String(error),
275
276
  callProviderMetadata: chunk.payload.providerMetadata
276
277
  };
277
278
  } else {
278
279
  const isWorkflow = Boolean(chunk.payload.result?.result?.steps);
280
+ const isAgent = chunk?.from === "AGENT";
281
+ let output;
282
+ if (isWorkflow) {
283
+ output = chunk.payload.result?.result;
284
+ } else if (isAgent) {
285
+ output = parts[toolPartIndex].output ?? chunk.payload.result;
286
+ } else {
287
+ output = chunk.payload.result;
288
+ }
279
289
  parts[toolPartIndex] = {
280
290
  type: "dynamic-tool",
281
291
  toolName: toolPart.toolName,
282
292
  toolCallId: toolPart.toolCallId,
283
293
  state: "output-available",
284
294
  input: toolPart.input,
285
- output: isWorkflow ? chunk.payload.result?.result : chunk.payload.result,
295
+ output,
286
296
  callProviderMetadata: chunk.payload.providerMetadata
287
297
  };
288
298
  }
@@ -316,6 +326,8 @@ const toUIMessage = ({ chunk, conversation, metadata }) => {
316
326
  ...toolPart,
317
327
  output: updatedWorkflowState
318
328
  };
329
+ } else if (chunk.payload.output?.from === "AGENT" || chunk.payload.output?.from === "USER" && chunk.payload.output?.payload?.output?.type?.startsWith("workflow-")) {
330
+ return toUIMessageFromAgent(chunk.payload.output, conversation);
319
331
  } else {
320
332
  const currentOutput = toolPart.output || [];
321
333
  const existingOutput = Array.isArray(currentOutput) ? currentOutput : [];
@@ -389,15 +401,37 @@ const toUIMessage = ({ chunk, conversation, metadata }) => {
389
401
  }
390
402
  ];
391
403
  }
404
+ case "tool-call-approval": {
405
+ const lastMessage = result[result.length - 1];
406
+ if (!lastMessage || lastMessage.role !== "assistant") return result;
407
+ const lastRequireApprovalMetadata = lastMessage.metadata?.mode === "stream" ? lastMessage.metadata?.requireApprovalMetadata : {};
408
+ return [
409
+ ...result.slice(0, -1),
410
+ {
411
+ ...lastMessage,
412
+ metadata: {
413
+ ...lastMessage.metadata,
414
+ mode: "stream",
415
+ requireApprovalMetadata: {
416
+ ...lastRequireApprovalMetadata,
417
+ [chunk.payload.toolCallId]: {
418
+ toolCallId: chunk.payload.toolCallId,
419
+ toolName: chunk.payload.toolName,
420
+ args: chunk.payload.args
421
+ }
422
+ }
423
+ }
424
+ }
425
+ ];
426
+ }
392
427
  case "finish": {
393
428
  const lastMessage = result[result.length - 1];
394
429
  if (!lastMessage || lastMessage.role !== "assistant") return result;
395
430
  const parts = lastMessage.parts.map((part) => {
396
- if (part.type === "text" && part.state === "streaming") {
397
- return { ...part, state: "done" };
398
- }
399
- if (part.type === "reasoning" && part.state === "streaming") {
400
- return { ...part, state: "done" };
431
+ if (typeof part === "object" && part !== null && "type" in part && "state" in part && part.state === "streaming") {
432
+ if (part.type === "text" || part.type === "reasoning") {
433
+ return { ...part, state: "done" };
434
+ }
401
435
  }
402
436
  return part;
403
437
  });
@@ -431,6 +465,105 @@ const toUIMessage = ({ chunk, conversation, metadata }) => {
431
465
  return result;
432
466
  }
433
467
  };
468
+ const toUIMessageFromAgent = (chunk, conversation, metadata) => {
469
+ const lastMessage = conversation[conversation.length - 1];
470
+ if (!lastMessage || lastMessage.role !== "assistant") return conversation;
471
+ const parts = [...lastMessage.parts];
472
+ if (chunk.type === "text-delta") {
473
+ const agentChunk = chunk.payload;
474
+ const toolPartIndex = parts.findIndex((part) => part.type === "dynamic-tool");
475
+ if (toolPartIndex === -1) return conversation;
476
+ const toolPart = parts[toolPartIndex];
477
+ const childMessages = toolPart?.output?.childMessages || [];
478
+ const lastChildMessage = childMessages[childMessages.length - 1];
479
+ const textMessage = { type: "text", content: (lastChildMessage?.content || "") + agentChunk.text };
480
+ const nextMessages = lastChildMessage?.type === "text" ? [...childMessages.slice(0, -1), textMessage] : [...childMessages, textMessage];
481
+ parts[toolPartIndex] = {
482
+ ...toolPart,
483
+ output: {
484
+ childMessages: nextMessages
485
+ }
486
+ };
487
+ } else if (chunk.type === "tool-call") {
488
+ const agentChunk = chunk.payload;
489
+ const toolPartIndex = parts.findIndex((part) => part.type === "dynamic-tool");
490
+ if (toolPartIndex === -1) return conversation;
491
+ const toolPart = parts[toolPartIndex];
492
+ const childMessages = toolPart?.output?.childMessages || [];
493
+ parts[toolPartIndex] = {
494
+ ...toolPart,
495
+ output: {
496
+ ...toolPart?.output,
497
+ childMessages: [
498
+ ...childMessages,
499
+ {
500
+ type: "tool",
501
+ toolCallId: agentChunk.toolCallId,
502
+ toolName: agentChunk.toolName,
503
+ args: agentChunk.args
504
+ }
505
+ ]
506
+ }
507
+ };
508
+ } else if (chunk.type === "tool-output") {
509
+ const agentChunk = chunk.payload;
510
+ const toolPartIndex = parts.findIndex((part) => part.type === "dynamic-tool");
511
+ if (toolPartIndex === -1) return conversation;
512
+ const toolPart = parts[toolPartIndex];
513
+ if (agentChunk?.output?.type?.startsWith("workflow-")) {
514
+ const childMessages = toolPart?.output?.childMessages || [];
515
+ const lastToolIndex = childMessages.length - 1;
516
+ const currentMessage = childMessages[lastToolIndex];
517
+ const actualExistingWorkflowState = currentMessage?.toolOutput || {};
518
+ const updatedWorkflowState = mapWorkflowStreamChunkToWatchResult(actualExistingWorkflowState, agentChunk.output);
519
+ if (lastToolIndex >= 0 && childMessages[lastToolIndex]?.type === "tool") {
520
+ parts[toolPartIndex] = {
521
+ ...toolPart,
522
+ output: {
523
+ ...toolPart?.output,
524
+ childMessages: [
525
+ ...childMessages.slice(0, -1),
526
+ {
527
+ ...currentMessage,
528
+ toolOutput: { ...updatedWorkflowState, runId: agentChunk.output.runId }
529
+ }
530
+ ]
531
+ }
532
+ };
533
+ }
534
+ }
535
+ } else if (chunk.type === "tool-result") {
536
+ const agentChunk = chunk.payload;
537
+ const toolPartIndex = parts.findIndex((part) => part.type === "dynamic-tool");
538
+ if (toolPartIndex === -1) return conversation;
539
+ const toolPart = parts[toolPartIndex];
540
+ const childMessages = toolPart?.output?.childMessages || [];
541
+ const lastToolIndex = childMessages.length - 1;
542
+ const isWorkflow = agentChunk?.toolName?.startsWith("workflow-");
543
+ if (lastToolIndex >= 0 && childMessages[lastToolIndex]?.type === "tool") {
544
+ parts[toolPartIndex] = {
545
+ ...toolPart,
546
+ output: {
547
+ ...toolPart?.output,
548
+ childMessages: [
549
+ ...childMessages.slice(0, -1),
550
+ {
551
+ ...childMessages[lastToolIndex],
552
+ toolOutput: isWorkflow ? { ...agentChunk.result?.result, runId: agentChunk.result?.runId } : agentChunk.result
553
+ }
554
+ ]
555
+ }
556
+ };
557
+ }
558
+ }
559
+ return [
560
+ ...conversation.slice(0, -1),
561
+ {
562
+ ...lastMessage,
563
+ parts
564
+ }
565
+ ];
566
+ };
434
567
 
435
568
  const toAssistantUIMessage = (message) => {
436
569
  const extendedMessage = message;
@@ -470,13 +603,23 @@ const toAssistantUIMessage = (message) => {
470
603
  };
471
604
  }
472
605
  if (part.type === "file") {
473
- return {
474
- type: "file",
475
- mimeType: part.mediaType,
476
- data: part.url,
477
- // Use URL as data source
478
- metadata: message.metadata
479
- };
606
+ const type = part.mediaType.includes("image/") ? "image" : "file";
607
+ if (type === "file") {
608
+ return {
609
+ type,
610
+ mimeType: part.mediaType,
611
+ data: part.url,
612
+ // Use URL as data source
613
+ metadata: message.metadata
614
+ };
615
+ }
616
+ if (type === "image") {
617
+ return {
618
+ type,
619
+ image: part.url,
620
+ metadata: message.metadata
621
+ };
622
+ }
480
623
  }
481
624
  if (part.type === "dynamic-tool") {
482
625
  const baseToolCall = {
@@ -551,9 +694,118 @@ const toAssistantUIMessage = (message) => {
551
694
  return threadMessage;
552
695
  };
553
696
 
697
+ const resolveInitialMessages = (messages) => {
698
+ return messages.map((message) => {
699
+ const networkPart = message.parts.find(
700
+ (part) => typeof part === "object" && part !== null && "type" in part && part.type === "text" && "text" in part && typeof part.text === "string" && part.text.includes('"isNetwork":true')
701
+ );
702
+ if (networkPart && networkPart.type === "text") {
703
+ try {
704
+ const json = JSON.parse(networkPart.text);
705
+ if (json.isNetwork === true) {
706
+ const selectionReason = json.selectionReason || "";
707
+ const primitiveType = json.primitiveType || "";
708
+ const primitiveId = json.primitiveId || "";
709
+ const finalResult = json.finalResult;
710
+ const toolCalls = finalResult?.toolCalls || [];
711
+ const childMessages = [];
712
+ for (const toolCall of toolCalls) {
713
+ if (toolCall.type === "tool-call" && toolCall.payload) {
714
+ const toolCallId = toolCall.payload.toolCallId;
715
+ let toolResult;
716
+ for (const message2 of finalResult?.messages || []) {
717
+ for (const part of message2.content || []) {
718
+ if (typeof part === "object" && part.type === "tool-result" && part.toolCallId === toolCallId) {
719
+ toolResult = part;
720
+ break;
721
+ }
722
+ }
723
+ }
724
+ const isWorkflow = Boolean(toolResult?.result?.result?.steps);
725
+ childMessages.push({
726
+ type: "tool",
727
+ toolCallId: toolCall.payload.toolCallId,
728
+ toolName: toolCall.payload.toolName,
729
+ args: toolCall.payload.args,
730
+ toolOutput: isWorkflow ? toolResult?.result?.result : toolResult?.result
731
+ });
732
+ }
733
+ }
734
+ if (finalResult && finalResult.text) {
735
+ childMessages.push({
736
+ type: "text",
737
+ content: finalResult.text
738
+ });
739
+ }
740
+ const result = {
741
+ childMessages,
742
+ result: finalResult?.text || ""
743
+ };
744
+ console.log("json", json);
745
+ const nextMessage = {
746
+ role: "assistant",
747
+ parts: [
748
+ {
749
+ type: "dynamic-tool",
750
+ toolCallId: primitiveId,
751
+ toolName: primitiveId,
752
+ state: "output-available",
753
+ input: json.input,
754
+ output: result
755
+ }
756
+ ],
757
+ id: message.id,
758
+ metadata: {
759
+ ...message.metadata,
760
+ mode: "network",
761
+ selectionReason,
762
+ agentInput: json.input,
763
+ from: primitiveType === "agent" ? "AGENT" : "WORKFLOW"
764
+ }
765
+ };
766
+ return nextMessage;
767
+ }
768
+ } catch (error) {
769
+ return message;
770
+ }
771
+ }
772
+ return message;
773
+ });
774
+ };
775
+ const resolveToChildMessages = (messages) => {
776
+ const assistantMessage = messages.find((message) => message.role === "assistant");
777
+ if (!assistantMessage) return [];
778
+ const parts = assistantMessage.parts;
779
+ let childMessages = [];
780
+ for (const part of parts) {
781
+ const toolPart = part;
782
+ if (part.type.startsWith("tool-")) {
783
+ const toolName = part.type.substring("tool-".length);
784
+ const isWorkflow = toolName.startsWith("workflow-");
785
+ childMessages.push({
786
+ type: "tool",
787
+ toolCallId: toolPart.toolCallId,
788
+ toolName,
789
+ args: toolPart.input,
790
+ toolOutput: isWorkflow ? { ...toolPart.output?.result, runId: toolPart.output?.runId } : toolPart.output
791
+ });
792
+ }
793
+ if (part.type === "text") {
794
+ childMessages.push({
795
+ type: "text",
796
+ content: toolPart.text
797
+ });
798
+ }
799
+ }
800
+ return childMessages;
801
+ };
802
+
554
803
  class AISdkNetworkTransformer {
555
804
  transform({ chunk, conversation, metadata }) {
556
805
  const newConversation = [...conversation];
806
+ if (chunk.type === "routing-agent-text-delta") {
807
+ return this.handleRoutingAgentConversation(chunk, newConversation);
808
+ }
557
809
  if (chunk.type.startsWith("agent-execution-")) {
558
810
  return this.handleAgentConversation(chunk, newConversation, metadata);
559
811
  }
@@ -564,22 +816,80 @@ class AISdkNetworkTransformer {
564
816
  return this.handleToolConversation(chunk, newConversation, metadata);
565
817
  }
566
818
  if (chunk.type === "network-execution-event-step-finish") {
567
- const newMessage = {
568
- id: `network-execution-event-step-finish-${chunk.runId}-${Date.now()}`,
569
- role: "assistant",
570
- parts: [
819
+ const lastMessage = newConversation[newConversation.length - 1];
820
+ if (!lastMessage || lastMessage.role !== "assistant") return newConversation;
821
+ const agentChunk = chunk.payload;
822
+ const parts = [...lastMessage.parts];
823
+ const textPartIndex = parts.findIndex((part) => part.type === "text");
824
+ if (textPartIndex === -1) {
825
+ parts.push({
826
+ type: "text",
827
+ text: agentChunk.result,
828
+ state: "done"
829
+ });
830
+ return [
831
+ ...newConversation.slice(0, -1),
571
832
  {
572
- type: "text",
573
- text: chunk.payload?.result || "",
574
- state: "done"
833
+ ...lastMessage,
834
+ parts
575
835
  }
576
- ],
577
- metadata
578
- };
579
- return [...newConversation, newMessage];
836
+ ];
837
+ }
838
+ const textPart = parts[textPartIndex];
839
+ if (textPart.type === "text") {
840
+ parts[textPartIndex] = {
841
+ ...textPart,
842
+ state: "done"
843
+ };
844
+ return [
845
+ ...newConversation.slice(0, -1),
846
+ {
847
+ ...lastMessage,
848
+ parts
849
+ }
850
+ ];
851
+ }
852
+ return newConversation;
580
853
  }
581
854
  return newConversation;
582
855
  }
856
+ handleRoutingAgentConversation = (chunk, newConversation) => {
857
+ const lastMessage = newConversation[newConversation.length - 1];
858
+ if (!lastMessage || lastMessage.role !== "assistant") return newConversation;
859
+ const agentChunk = chunk.payload;
860
+ const parts = [...lastMessage.parts];
861
+ const textPartIndex = parts.findIndex((part) => part.type === "text");
862
+ if (textPartIndex === -1) {
863
+ parts.push({
864
+ type: "text",
865
+ text: agentChunk.text,
866
+ state: "streaming"
867
+ });
868
+ return [
869
+ ...newConversation.slice(0, -1),
870
+ {
871
+ ...lastMessage,
872
+ parts
873
+ }
874
+ ];
875
+ }
876
+ const textPart = parts[textPartIndex];
877
+ if (textPart.type === "text") {
878
+ parts[textPartIndex] = {
879
+ ...textPart,
880
+ text: textPart.text + agentChunk.text,
881
+ state: "streaming"
882
+ };
883
+ return [
884
+ ...newConversation.slice(0, -1),
885
+ {
886
+ ...lastMessage,
887
+ parts
888
+ }
889
+ ];
890
+ }
891
+ return newConversation;
892
+ };
583
893
  handleAgentConversation = (chunk, newConversation, metadata) => {
584
894
  if (chunk.type === "agent-execution-start") {
585
895
  const primitiveId = chunk.payload?.args?.primitiveId;
@@ -862,92 +1172,63 @@ class AISdkNetworkTransformer {
862
1172
  };
863
1173
  }
864
1174
 
865
- const resolveInitialMessages = (messages) => {
866
- return messages.map((message) => {
867
- const networkPart = message.parts.find((part) => part.type === "text" && part.text.includes('"isNetwork":true'));
868
- if (networkPart && networkPart.type === "text") {
869
- try {
870
- const json = JSON.parse(networkPart.text);
871
- if (json.isNetwork === true) {
872
- const selectionReason = json.selectionReason || "";
873
- const primitiveType = json.primitiveType || "";
874
- const primitiveId = json.primitiveId || "";
875
- const finalResult = json.finalResult;
876
- const toolCalls = finalResult?.toolCalls || [];
877
- const childMessages = [];
878
- for (const toolCall of toolCalls) {
879
- if (toolCall.type === "tool-call" && toolCall.payload) {
880
- const toolCallId = toolCall.payload.toolCallId;
881
- let toolResult;
882
- for (const message2 of finalResult?.messages || []) {
883
- for (const part of message2.content || []) {
884
- if (typeof part === "object" && part.type === "tool-result" && part.toolCallId === toolCallId) {
885
- toolResult = part;
886
- break;
887
- }
888
- }
889
- }
890
- const isWorkflow = Boolean(toolResult?.result?.result?.steps);
891
- childMessages.push({
892
- type: "tool",
893
- toolCallId: toolCall.payload.toolCallId,
894
- toolName: toolCall.payload.toolName,
895
- args: toolCall.payload.args,
896
- toolOutput: isWorkflow ? toolResult?.result?.result : toolResult?.result
897
- });
898
- }
899
- }
900
- if (finalResult && finalResult.text) {
901
- childMessages.push({
902
- type: "text",
903
- content: finalResult.text
904
- });
905
- }
906
- const result = {
907
- childMessages,
908
- result: finalResult?.text || ""
909
- };
910
- console.log("json", json);
911
- const nextMessage = {
912
- role: "assistant",
913
- parts: [
914
- {
915
- type: "dynamic-tool",
916
- toolCallId: primitiveId,
917
- toolName: primitiveId,
918
- state: "output-available",
919
- input: json.input,
920
- output: result
921
- }
922
- ],
923
- id: message.id,
924
- metadata: {
925
- ...message.metadata,
926
- mode: "network",
927
- selectionReason,
928
- agentInput: json.input,
929
- from: primitiveType === "agent" ? "AGENT" : "WORKFLOW"
930
- }
931
- };
932
- return nextMessage;
933
- }
934
- } catch (error) {
935
- return message;
1175
+ const fromCoreUserMessageToUIMessage = (coreUserMessage) => {
1176
+ const id = `user-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
1177
+ const parts = typeof coreUserMessage.content === "string" ? [
1178
+ {
1179
+ type: "text",
1180
+ text: coreUserMessage.content
1181
+ }
1182
+ ] : coreUserMessage.content.map((part) => {
1183
+ switch (part.type) {
1184
+ case "text": {
1185
+ return {
1186
+ type: "text",
1187
+ text: part.text
1188
+ };
1189
+ }
1190
+ case "image": {
1191
+ const url = typeof part.image === "string" ? part.image : part.image instanceof URL ? part.image.toString() : "";
1192
+ return {
1193
+ type: "file",
1194
+ mediaType: part.mimeType ?? "image/*",
1195
+ url
1196
+ };
1197
+ }
1198
+ case "file": {
1199
+ const url = typeof part.data === "string" ? part.data : part.data instanceof URL ? part.data.toString() : "";
1200
+ return {
1201
+ type: "file",
1202
+ mediaType: part.mimeType,
1203
+ url,
1204
+ ...part.filename !== void 0 ? { filename: part.filename } : {}
1205
+ };
1206
+ }
1207
+ default: {
1208
+ const exhaustiveCheck = part;
1209
+ throw new Error(`Unhandled content part type: ${exhaustiveCheck.type}`);
936
1210
  }
937
1211
  }
938
- return message;
939
1212
  });
1213
+ return {
1214
+ id,
1215
+ role: "user",
1216
+ parts
1217
+ };
940
1218
  };
941
1219
 
942
1220
  const useChat = ({ agentId, initializeMessages }) => {
1221
+ const _currentRunId = react.useRef(void 0);
1222
+ const _onChunk = react.useRef(void 0);
943
1223
  const [messages, setMessages] = react.useState(
944
1224
  () => resolveInitialMessages(initializeMessages?.() || [])
945
1225
  );
1226
+ const [toolCallApprovals, setToolCallApprovals] = react.useState({});
946
1227
  const baseClient = useMastraClient();
947
1228
  const [isRunning, setIsRunning] = react.useState(false);
948
1229
  const generate = async ({
949
1230
  coreUserMessages,
950
- runtimeContext,
1231
+ requestContext,
951
1232
  threadId,
952
1233
  modelSettings,
953
1234
  signal,
@@ -985,7 +1266,7 @@ const useChat = ({ agentId, initializeMessages }) => {
985
1266
  topP
986
1267
  },
987
1268
  instructions,
988
- runtimeContext,
1269
+ requestContext,
989
1270
  ...threadId ? { threadId, resourceId: agentId } : {},
990
1271
  providerOptions
991
1272
  });
@@ -1001,7 +1282,7 @@ const useChat = ({ agentId, initializeMessages }) => {
1001
1282
  setMessages((prev) => [...prev, ...mastraUIMessages]);
1002
1283
  }
1003
1284
  };
1004
- const stream = async ({ coreUserMessages, runtimeContext, threadId, onChunk, modelSettings, signal }) => {
1285
+ const stream = async ({ coreUserMessages, requestContext, threadId, onChunk, modelSettings, signal }) => {
1005
1286
  const {
1006
1287
  frequencyPenalty,
1007
1288
  presencePenalty,
@@ -1012,7 +1293,8 @@ const useChat = ({ agentId, initializeMessages }) => {
1012
1293
  topP,
1013
1294
  instructions,
1014
1295
  providerOptions,
1015
- maxSteps
1296
+ maxSteps,
1297
+ requireToolApproval
1016
1298
  } = modelSettings || {};
1017
1299
  setIsRunning(true);
1018
1300
  const clientWithAbort = new clientJs.MastraClient({
@@ -1020,9 +1302,10 @@ const useChat = ({ agentId, initializeMessages }) => {
1020
1302
  abortSignal: signal
1021
1303
  });
1022
1304
  const agent = clientWithAbort.getAgent(agentId);
1305
+ const runId = agentId;
1023
1306
  const response = await agent.stream({
1024
1307
  messages: coreUserMessages,
1025
- runId: agentId,
1308
+ runId,
1026
1309
  maxSteps,
1027
1310
  modelSettings: {
1028
1311
  frequencyPenalty,
@@ -1034,19 +1317,16 @@ const useChat = ({ agentId, initializeMessages }) => {
1034
1317
  topP
1035
1318
  },
1036
1319
  instructions,
1037
- runtimeContext,
1320
+ requestContext,
1038
1321
  ...threadId ? { threadId, resourceId: agentId } : {},
1039
- providerOptions
1322
+ providerOptions,
1323
+ requireToolApproval
1040
1324
  });
1041
- if (!response.body) {
1042
- setIsRunning(false);
1043
- throw new Error("[Stream] No response body");
1044
- }
1325
+ _onChunk.current = onChunk;
1326
+ _currentRunId.current = runId;
1045
1327
  await response.processDataStream({
1046
1328
  onChunk: async (chunk) => {
1047
- reactDom.flushSync(() => {
1048
- setMessages((prev) => toUIMessage({ chunk, conversation: prev, metadata: { mode: "stream" } }));
1049
- });
1329
+ setMessages((prev) => toUIMessage({ chunk, conversation: prev, metadata: { mode: "stream" } }));
1050
1330
  onChunk?.(chunk);
1051
1331
  }
1052
1332
  });
@@ -1054,7 +1334,7 @@ const useChat = ({ agentId, initializeMessages }) => {
1054
1334
  };
1055
1335
  const network = async ({
1056
1336
  coreUserMessages,
1057
- runtimeContext,
1337
+ requestContext,
1058
1338
  threadId,
1059
1339
  onNetworkChunk,
1060
1340
  modelSettings,
@@ -1080,30 +1360,71 @@ const useChat = ({ agentId, initializeMessages }) => {
1080
1360
  topP
1081
1361
  },
1082
1362
  runId: agentId,
1083
- runtimeContext,
1363
+ requestContext,
1084
1364
  ...threadId ? { thread: threadId, resourceId: agentId } : {}
1085
1365
  });
1086
1366
  const transformer = new AISdkNetworkTransformer();
1087
1367
  await response.processDataStream({
1088
1368
  onChunk: async (chunk) => {
1089
- reactDom.flushSync(() => {
1090
- setMessages((prev) => transformer.transform({ chunk, conversation: prev, metadata: { mode: "network" } }));
1091
- });
1369
+ setMessages((prev) => transformer.transform({ chunk, conversation: prev, metadata: { mode: "network" } }));
1092
1370
  onNetworkChunk?.(chunk);
1093
1371
  }
1094
1372
  });
1095
1373
  setIsRunning(false);
1096
1374
  };
1375
+ const handleCancelRun = () => {
1376
+ setIsRunning(false);
1377
+ _currentRunId.current = void 0;
1378
+ _onChunk.current = void 0;
1379
+ };
1380
+ const approveToolCall = async (toolCallId) => {
1381
+ const onChunk = _onChunk.current;
1382
+ const currentRunId = _currentRunId.current;
1383
+ if (!currentRunId)
1384
+ return console.info("[approveToolCall] approveToolCall can only be called after a stream has started");
1385
+ setIsRunning(true);
1386
+ setToolCallApprovals((prev) => ({ ...prev, [toolCallId]: { status: "approved" } }));
1387
+ const agent = baseClient.getAgent(agentId);
1388
+ const response = await agent.approveToolCall({ runId: currentRunId, toolCallId });
1389
+ await response.processDataStream({
1390
+ onChunk: async (chunk) => {
1391
+ setMessages((prev) => toUIMessage({ chunk, conversation: prev, metadata: { mode: "stream" } }));
1392
+ onChunk?.(chunk);
1393
+ }
1394
+ });
1395
+ setIsRunning(false);
1396
+ };
1397
+ const declineToolCall = async (toolCallId) => {
1398
+ const onChunk = _onChunk.current;
1399
+ const currentRunId = _currentRunId.current;
1400
+ if (!currentRunId)
1401
+ return console.info("[declineToolCall] declineToolCall can only be called after a stream has started");
1402
+ setIsRunning(true);
1403
+ setToolCallApprovals((prev) => ({ ...prev, [toolCallId]: { status: "declined" } }));
1404
+ const agent = baseClient.getAgent(agentId);
1405
+ const response = await agent.declineToolCall({ runId: currentRunId, toolCallId });
1406
+ await response.processDataStream({
1407
+ onChunk: async (chunk) => {
1408
+ setMessages((prev) => toUIMessage({ chunk, conversation: prev, metadata: { mode: "stream" } }));
1409
+ onChunk?.(chunk);
1410
+ }
1411
+ });
1412
+ setIsRunning(false);
1413
+ };
1097
1414
  const sendMessage = async ({ mode = "stream", ...args }) => {
1098
1415
  const nextMessage = { role: "user", content: [{ type: "text", text: args.message }] };
1099
- const messages2 = args.coreUserMessages ? [nextMessage, ...args.coreUserMessages] : [nextMessage];
1100
- setMessages((s) => [...s, { role: "user", parts: [{ type: "text", text: args.message }] }]);
1416
+ const coreUserMessages = [nextMessage];
1417
+ if (args.coreUserMessages) {
1418
+ coreUserMessages.push(...args.coreUserMessages);
1419
+ }
1420
+ const uiMessages = coreUserMessages.map(fromCoreUserMessageToUIMessage);
1421
+ setMessages((s) => [...s, ...uiMessages]);
1101
1422
  if (mode === "generate") {
1102
- await generate({ ...args, coreUserMessages: messages2 });
1423
+ await generate({ ...args, coreUserMessages });
1103
1424
  } else if (mode === "stream") {
1104
- await stream({ ...args, coreUserMessages: messages2 });
1425
+ await stream({ ...args, coreUserMessages });
1105
1426
  } else if (mode === "network") {
1106
- await network({ ...args, coreUserMessages: messages2 });
1427
+ await network({ ...args, coreUserMessages });
1107
1428
  }
1108
1429
  };
1109
1430
  return {
@@ -1111,7 +1432,10 @@ const useChat = ({ agentId, initializeMessages }) => {
1111
1432
  sendMessage,
1112
1433
  isRunning,
1113
1434
  messages,
1114
- cancelRun: () => setIsRunning(false)
1435
+ approveToolCall,
1436
+ declineToolCall,
1437
+ cancelRun: handleCancelRun,
1438
+ toolCallApprovals
1115
1439
  };
1116
1440
  };
1117
1441
 
@@ -1485,6 +1809,7 @@ exports.TooltipContentClass = TooltipContentClass;
1485
1809
  exports.TooltipTrigger = TooltipTrigger;
1486
1810
  exports.WorkflowIcon = WorkflowIcon;
1487
1811
  exports.mapWorkflowStreamChunkToWatchResult = mapWorkflowStreamChunkToWatchResult;
1812
+ exports.resolveToChildMessages = resolveToChildMessages;
1488
1813
  exports.toAssistantUIMessage = toAssistantUIMessage;
1489
1814
  exports.toUIMessage = toUIMessage;
1490
1815
  exports.useChat = useChat;