@mastra/react 0.0.0-monorepo-binary-20251013210052 → 0.0.0-netlify-no-bundle-20251127120354

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,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 reactDom = require('react-dom');
8
+ const uuid = require('@lukeed/uuid');
9
9
  const lucideReact = require('lucide-react');
10
10
  const tailwindMerge = require('tailwind-merge');
11
11
  const hastUtilToJsxRuntime = require('hast-util-to-jsx-runtime');
@@ -254,35 +254,48 @@ const toUIMessage = ({ chunk, conversation, metadata }) => {
254
254
  }
255
255
  ];
256
256
  }
257
+ case "tool-error":
257
258
  case "tool-result": {
258
259
  const lastMessage = result[result.length - 1];
259
260
  if (!lastMessage || lastMessage.role !== "assistant") return result;
260
261
  const parts = [...lastMessage.parts];
261
262
  const toolPartIndex = parts.findIndex(
262
- (part) => part.type === "dynamic-tool" && "toolCallId" in part && part.toolCallId === chunk.payload.toolCallId
263
+ (part) => (part.type === "dynamic-tool" || typeof part.type === "string" && part.type.startsWith("tool-")) && "toolCallId" in part && part.toolCallId === chunk.payload.toolCallId
263
264
  );
264
265
  if (toolPartIndex !== -1) {
265
266
  const toolPart = parts[toolPartIndex];
266
- if (toolPart.type === "dynamic-tool") {
267
- if (chunk.payload.isError) {
267
+ if (toolPart.type === "dynamic-tool" || typeof toolPart.type === "string" && toolPart.type.startsWith("tool-")) {
268
+ const toolName = "toolName" in toolPart && typeof toolPart.toolName === "string" ? toolPart.toolName : toolPart.type.startsWith("tool-") ? toolPart.type.substring(5) : "";
269
+ const toolCallId = toolPart.toolCallId;
270
+ if (chunk.type === "tool-result" && chunk.payload.isError || chunk.type === "tool-error") {
271
+ const error = chunk.type === "tool-error" ? chunk.payload.error : chunk.payload.result;
268
272
  parts[toolPartIndex] = {
269
273
  type: "dynamic-tool",
270
- toolName: toolPart.toolName,
271
- toolCallId: toolPart.toolCallId,
274
+ toolName,
275
+ toolCallId,
272
276
  state: "output-error",
273
277
  input: toolPart.input,
274
- errorText: String(chunk.payload.result),
278
+ errorText: String(error),
275
279
  callProviderMetadata: chunk.payload.providerMetadata
276
280
  };
277
281
  } else {
278
282
  const isWorkflow = Boolean(chunk.payload.result?.result?.steps);
283
+ const isAgent = chunk?.from === "AGENT";
284
+ let output;
285
+ if (isWorkflow) {
286
+ output = chunk.payload.result?.result;
287
+ } else if (isAgent) {
288
+ output = parts[toolPartIndex].output ?? chunk.payload.result;
289
+ } else {
290
+ output = chunk.payload.result;
291
+ }
279
292
  parts[toolPartIndex] = {
280
293
  type: "dynamic-tool",
281
- toolName: toolPart.toolName,
282
- toolCallId: toolPart.toolCallId,
294
+ toolName,
295
+ toolCallId,
283
296
  state: "output-available",
284
297
  input: toolPart.input,
285
- output: isWorkflow ? chunk.payload.result?.result : chunk.payload.result,
298
+ output,
286
299
  callProviderMetadata: chunk.payload.providerMetadata
287
300
  };
288
301
  }
@@ -301,11 +314,14 @@ const toUIMessage = ({ chunk, conversation, metadata }) => {
301
314
  if (!lastMessage || lastMessage.role !== "assistant") return result;
302
315
  const parts = [...lastMessage.parts];
303
316
  const toolPartIndex = parts.findIndex(
304
- (part) => part.type === "dynamic-tool" && "toolCallId" in part && part.toolCallId === chunk.payload.toolCallId
317
+ (part) => (part.type === "dynamic-tool" || typeof part.type === "string" && part.type.startsWith("tool-")) && "toolCallId" in part && part.toolCallId === chunk.payload.toolCallId
305
318
  );
306
319
  if (toolPartIndex !== -1) {
307
320
  const toolPart = parts[toolPartIndex];
308
- if (toolPart.type === "dynamic-tool") {
321
+ if (toolPart.type === "dynamic-tool" || typeof toolPart.type === "string" && toolPart.type.startsWith("tool-")) {
322
+ const toolName = "toolName" in toolPart && typeof toolPart.toolName === "string" ? toolPart.toolName : typeof toolPart.type === "string" && toolPart.type.startsWith("tool-") ? toolPart.type.substring(5) : "";
323
+ const toolCallId = toolPart.toolCallId;
324
+ const input = toolPart.input;
309
325
  if (chunk.payload.output?.type?.startsWith("workflow-")) {
310
326
  const existingWorkflowState = toolPart.output || {};
311
327
  const updatedWorkflowState = mapWorkflowStreamChunkToWatchResult(
@@ -313,14 +329,24 @@ const toUIMessage = ({ chunk, conversation, metadata }) => {
313
329
  chunk.payload.output
314
330
  );
315
331
  parts[toolPartIndex] = {
316
- ...toolPart,
332
+ type: "dynamic-tool",
333
+ toolName,
334
+ toolCallId,
335
+ state: "input-streaming",
336
+ input,
317
337
  output: updatedWorkflowState
318
338
  };
339
+ } else if (chunk.payload.output?.from === "AGENT" || chunk.payload.output?.from === "USER" && chunk.payload.output?.payload?.output?.type?.startsWith("workflow-")) {
340
+ return toUIMessageFromAgent(chunk.payload.output, conversation);
319
341
  } else {
320
342
  const currentOutput = toolPart.output || [];
321
343
  const existingOutput = Array.isArray(currentOutput) ? currentOutput : [];
322
344
  parts[toolPartIndex] = {
323
- ...toolPart,
345
+ type: "dynamic-tool",
346
+ toolName,
347
+ toolCallId,
348
+ state: "input-streaming",
349
+ input,
324
350
  output: [...existingOutput, chunk.payload.output]
325
351
  };
326
352
  }
@@ -389,15 +415,37 @@ const toUIMessage = ({ chunk, conversation, metadata }) => {
389
415
  }
390
416
  ];
391
417
  }
418
+ case "tool-call-approval": {
419
+ const lastMessage = result[result.length - 1];
420
+ if (!lastMessage || lastMessage.role !== "assistant") return result;
421
+ const lastRequireApprovalMetadata = lastMessage.metadata?.mode === "stream" ? lastMessage.metadata?.requireApprovalMetadata : {};
422
+ return [
423
+ ...result.slice(0, -1),
424
+ {
425
+ ...lastMessage,
426
+ metadata: {
427
+ ...lastMessage.metadata,
428
+ mode: "stream",
429
+ requireApprovalMetadata: {
430
+ ...lastRequireApprovalMetadata,
431
+ [chunk.payload.toolCallId]: {
432
+ toolCallId: chunk.payload.toolCallId,
433
+ toolName: chunk.payload.toolName,
434
+ args: chunk.payload.args
435
+ }
436
+ }
437
+ }
438
+ }
439
+ ];
440
+ }
392
441
  case "finish": {
393
442
  const lastMessage = result[result.length - 1];
394
443
  if (!lastMessage || lastMessage.role !== "assistant") return result;
395
444
  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" };
445
+ if (typeof part === "object" && part !== null && "type" in part && "state" in part && part.state === "streaming") {
446
+ if (part.type === "text" || part.type === "reasoning") {
447
+ return { ...part, state: "done" };
448
+ }
401
449
  }
402
450
  return part;
403
451
  });
@@ -431,6 +479,105 @@ const toUIMessage = ({ chunk, conversation, metadata }) => {
431
479
  return result;
432
480
  }
433
481
  };
482
+ const toUIMessageFromAgent = (chunk, conversation, metadata) => {
483
+ const lastMessage = conversation[conversation.length - 1];
484
+ if (!lastMessage || lastMessage.role !== "assistant") return conversation;
485
+ const parts = [...lastMessage.parts];
486
+ if (chunk.type === "text-delta") {
487
+ const agentChunk = chunk.payload;
488
+ const toolPartIndex = parts.findIndex((part) => part.type === "dynamic-tool");
489
+ if (toolPartIndex === -1) return conversation;
490
+ const toolPart = parts[toolPartIndex];
491
+ const childMessages = toolPart?.output?.childMessages || [];
492
+ const lastChildMessage = childMessages[childMessages.length - 1];
493
+ const textMessage = { type: "text", content: (lastChildMessage?.content || "") + agentChunk.text };
494
+ const nextMessages = lastChildMessage?.type === "text" ? [...childMessages.slice(0, -1), textMessage] : [...childMessages, textMessage];
495
+ parts[toolPartIndex] = {
496
+ ...toolPart,
497
+ output: {
498
+ childMessages: nextMessages
499
+ }
500
+ };
501
+ } else if (chunk.type === "tool-call") {
502
+ const agentChunk = chunk.payload;
503
+ const toolPartIndex = parts.findIndex((part) => part.type === "dynamic-tool");
504
+ if (toolPartIndex === -1) return conversation;
505
+ const toolPart = parts[toolPartIndex];
506
+ const childMessages = toolPart?.output?.childMessages || [];
507
+ parts[toolPartIndex] = {
508
+ ...toolPart,
509
+ output: {
510
+ ...toolPart?.output,
511
+ childMessages: [
512
+ ...childMessages,
513
+ {
514
+ type: "tool",
515
+ toolCallId: agentChunk.toolCallId,
516
+ toolName: agentChunk.toolName,
517
+ args: agentChunk.args
518
+ }
519
+ ]
520
+ }
521
+ };
522
+ } else if (chunk.type === "tool-output") {
523
+ const agentChunk = chunk.payload;
524
+ const toolPartIndex = parts.findIndex((part) => part.type === "dynamic-tool");
525
+ if (toolPartIndex === -1) return conversation;
526
+ const toolPart = parts[toolPartIndex];
527
+ if (agentChunk?.output?.type?.startsWith("workflow-")) {
528
+ const childMessages = toolPart?.output?.childMessages || [];
529
+ const lastToolIndex = childMessages.length - 1;
530
+ const currentMessage = childMessages[lastToolIndex];
531
+ const actualExistingWorkflowState = currentMessage?.toolOutput || {};
532
+ const updatedWorkflowState = mapWorkflowStreamChunkToWatchResult(actualExistingWorkflowState, agentChunk.output);
533
+ if (lastToolIndex >= 0 && childMessages[lastToolIndex]?.type === "tool") {
534
+ parts[toolPartIndex] = {
535
+ ...toolPart,
536
+ output: {
537
+ ...toolPart?.output,
538
+ childMessages: [
539
+ ...childMessages.slice(0, -1),
540
+ {
541
+ ...currentMessage,
542
+ toolOutput: { ...updatedWorkflowState, runId: agentChunk.output.runId }
543
+ }
544
+ ]
545
+ }
546
+ };
547
+ }
548
+ }
549
+ } else if (chunk.type === "tool-result") {
550
+ const agentChunk = chunk.payload;
551
+ const toolPartIndex = parts.findIndex((part) => part.type === "dynamic-tool");
552
+ if (toolPartIndex === -1) return conversation;
553
+ const toolPart = parts[toolPartIndex];
554
+ const childMessages = toolPart?.output?.childMessages || [];
555
+ const lastToolIndex = childMessages.length - 1;
556
+ const isWorkflow = agentChunk?.toolName?.startsWith("workflow-");
557
+ if (lastToolIndex >= 0 && childMessages[lastToolIndex]?.type === "tool") {
558
+ parts[toolPartIndex] = {
559
+ ...toolPart,
560
+ output: {
561
+ ...toolPart?.output,
562
+ childMessages: [
563
+ ...childMessages.slice(0, -1),
564
+ {
565
+ ...childMessages[lastToolIndex],
566
+ toolOutput: isWorkflow ? { ...agentChunk.result?.result, runId: agentChunk.result?.runId } : agentChunk.result
567
+ }
568
+ ]
569
+ }
570
+ };
571
+ }
572
+ }
573
+ return [
574
+ ...conversation.slice(0, -1),
575
+ {
576
+ ...lastMessage,
577
+ parts
578
+ }
579
+ ];
580
+ };
434
581
 
435
582
  const toAssistantUIMessage = (message) => {
436
583
  const extendedMessage = message;
@@ -470,13 +617,23 @@ const toAssistantUIMessage = (message) => {
470
617
  };
471
618
  }
472
619
  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
- };
620
+ const type = part.mediaType.includes("image/") ? "image" : "file";
621
+ if (type === "file") {
622
+ return {
623
+ type,
624
+ mimeType: part.mediaType,
625
+ data: part.url,
626
+ // Use URL as data source
627
+ metadata: message.metadata
628
+ };
629
+ }
630
+ if (type === "image") {
631
+ return {
632
+ type,
633
+ image: part.url,
634
+ metadata: message.metadata
635
+ };
636
+ }
480
637
  }
481
638
  if (part.type === "dynamic-tool") {
482
639
  const baseToolCall = {
@@ -512,6 +669,20 @@ const toAssistantUIMessage = (message) => {
512
669
  }
513
670
  return baseToolCall;
514
671
  }
672
+ const requireApprovalMetadata = extendedMessage.metadata?.requireApprovalMetadata;
673
+ const partToolCallId = "toolCallId" in part && typeof part.toolCallId === "string" ? part.toolCallId : void 0;
674
+ const suspensionData = partToolCallId ? requireApprovalMetadata?.[partToolCallId] : void 0;
675
+ if (suspensionData) {
676
+ const toolName = "toolName" in part && typeof part.toolName === "string" ? part.toolName : part.type.startsWith("tool-") ? part.type.substring(5) : "";
677
+ return {
678
+ type: "tool-call",
679
+ toolCallId: partToolCallId,
680
+ toolName,
681
+ argsText: "input" in part ? JSON.stringify(part.input) : "{}",
682
+ args: "input" in part ? part.input : {},
683
+ metadata: extendedMessage.metadata
684
+ };
685
+ }
515
686
  return {
516
687
  type: "text",
517
688
  text: "",
@@ -551,9 +722,129 @@ const toAssistantUIMessage = (message) => {
551
722
  return threadMessage;
552
723
  };
553
724
 
725
+ const resolveInitialMessages = (messages) => {
726
+ return messages.map((message) => {
727
+ const networkPart = message.parts.find(
728
+ (part) => typeof part === "object" && part !== null && "type" in part && part.type === "text" && "text" in part && typeof part.text === "string" && part.text.includes('"isNetwork":true')
729
+ );
730
+ if (networkPart && networkPart.type === "text") {
731
+ try {
732
+ const json = JSON.parse(networkPart.text);
733
+ if (json.isNetwork === true) {
734
+ const selectionReason = json.selectionReason || "";
735
+ const primitiveType = json.primitiveType || "";
736
+ const primitiveId = json.primitiveId || "";
737
+ const finalResult = json.finalResult;
738
+ const toolCalls = finalResult?.toolCalls || [];
739
+ const childMessages = [];
740
+ for (const toolCall of toolCalls) {
741
+ if (toolCall.type === "tool-call" && toolCall.payload) {
742
+ const toolCallId = toolCall.payload.toolCallId;
743
+ let toolResult;
744
+ for (const message2 of finalResult?.messages || []) {
745
+ for (const part of message2.content || []) {
746
+ if (typeof part === "object" && part.type === "tool-result" && part.toolCallId === toolCallId) {
747
+ toolResult = part;
748
+ break;
749
+ }
750
+ }
751
+ }
752
+ const isWorkflow = Boolean(toolResult?.result?.result?.steps);
753
+ childMessages.push({
754
+ type: "tool",
755
+ toolCallId: toolCall.payload.toolCallId,
756
+ toolName: toolCall.payload.toolName,
757
+ args: toolCall.payload.args,
758
+ toolOutput: isWorkflow ? toolResult?.result?.result : toolResult?.result
759
+ });
760
+ }
761
+ }
762
+ if (finalResult && finalResult.text) {
763
+ childMessages.push({
764
+ type: "text",
765
+ content: finalResult.text
766
+ });
767
+ }
768
+ const result = {
769
+ childMessages,
770
+ result: finalResult?.text || ""
771
+ };
772
+ const nextMessage = {
773
+ role: "assistant",
774
+ parts: [
775
+ {
776
+ type: "dynamic-tool",
777
+ toolCallId: primitiveId,
778
+ toolName: primitiveId,
779
+ state: "output-available",
780
+ input: json.input,
781
+ output: result
782
+ }
783
+ ],
784
+ id: message.id,
785
+ metadata: {
786
+ ...message.metadata,
787
+ mode: "network",
788
+ selectionReason,
789
+ agentInput: json.input,
790
+ from: primitiveType === "agent" ? "AGENT" : "WORKFLOW"
791
+ }
792
+ };
793
+ return nextMessage;
794
+ }
795
+ } catch (error) {
796
+ return message;
797
+ }
798
+ }
799
+ const extendedMessage = message;
800
+ const pendingToolApprovals = extendedMessage.metadata?.pendingToolApprovals;
801
+ if (pendingToolApprovals && typeof pendingToolApprovals === "object") {
802
+ return {
803
+ ...message,
804
+ metadata: {
805
+ ...message.metadata,
806
+ mode: "stream",
807
+ requireApprovalMetadata: pendingToolApprovals
808
+ }
809
+ };
810
+ }
811
+ return message;
812
+ });
813
+ };
814
+ const resolveToChildMessages = (messages) => {
815
+ const assistantMessage = messages.find((message) => message.role === "assistant");
816
+ if (!assistantMessage) return [];
817
+ const parts = assistantMessage.parts;
818
+ let childMessages = [];
819
+ for (const part of parts) {
820
+ const toolPart = part;
821
+ if (part.type.startsWith("tool-")) {
822
+ const toolName = part.type.substring("tool-".length);
823
+ const isWorkflow = toolName.startsWith("workflow-");
824
+ childMessages.push({
825
+ type: "tool",
826
+ toolCallId: toolPart.toolCallId,
827
+ toolName,
828
+ args: toolPart.input,
829
+ toolOutput: isWorkflow ? { ...toolPart.output?.result, runId: toolPart.output?.runId } : toolPart.output
830
+ });
831
+ }
832
+ if (part.type === "text") {
833
+ childMessages.push({
834
+ type: "text",
835
+ content: toolPart.text
836
+ });
837
+ }
838
+ }
839
+ return childMessages;
840
+ };
841
+
554
842
  class AISdkNetworkTransformer {
555
843
  transform({ chunk, conversation, metadata }) {
556
844
  const newConversation = [...conversation];
845
+ if (chunk.type === "routing-agent-text-delta") {
846
+ return this.handleRoutingAgentConversation(chunk, newConversation);
847
+ }
557
848
  if (chunk.type.startsWith("agent-execution-")) {
558
849
  return this.handleAgentConversation(chunk, newConversation, metadata);
559
850
  }
@@ -564,22 +855,80 @@ class AISdkNetworkTransformer {
564
855
  return this.handleToolConversation(chunk, newConversation, metadata);
565
856
  }
566
857
  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: [
858
+ const lastMessage = newConversation[newConversation.length - 1];
859
+ if (!lastMessage || lastMessage.role !== "assistant") return newConversation;
860
+ const agentChunk = chunk.payload;
861
+ const parts = [...lastMessage.parts];
862
+ const textPartIndex = parts.findIndex((part) => part.type === "text");
863
+ if (textPartIndex === -1) {
864
+ parts.push({
865
+ type: "text",
866
+ text: agentChunk.result,
867
+ state: "done"
868
+ });
869
+ return [
870
+ ...newConversation.slice(0, -1),
571
871
  {
572
- type: "text",
573
- text: chunk.payload?.result || "",
574
- state: "done"
872
+ ...lastMessage,
873
+ parts
575
874
  }
576
- ],
577
- metadata
578
- };
579
- return [...newConversation, newMessage];
875
+ ];
876
+ }
877
+ const textPart = parts[textPartIndex];
878
+ if (textPart.type === "text") {
879
+ parts[textPartIndex] = {
880
+ ...textPart,
881
+ state: "done"
882
+ };
883
+ return [
884
+ ...newConversation.slice(0, -1),
885
+ {
886
+ ...lastMessage,
887
+ parts
888
+ }
889
+ ];
890
+ }
891
+ return newConversation;
580
892
  }
581
893
  return newConversation;
582
894
  }
895
+ handleRoutingAgentConversation = (chunk, newConversation) => {
896
+ const lastMessage = newConversation[newConversation.length - 1];
897
+ if (!lastMessage || lastMessage.role !== "assistant") return newConversation;
898
+ const agentChunk = chunk.payload;
899
+ const parts = [...lastMessage.parts];
900
+ const textPartIndex = parts.findIndex((part) => part.type === "text");
901
+ if (textPartIndex === -1) {
902
+ parts.push({
903
+ type: "text",
904
+ text: agentChunk.text,
905
+ state: "streaming"
906
+ });
907
+ return [
908
+ ...newConversation.slice(0, -1),
909
+ {
910
+ ...lastMessage,
911
+ parts
912
+ }
913
+ ];
914
+ }
915
+ const textPart = parts[textPartIndex];
916
+ if (textPart.type === "text") {
917
+ parts[textPartIndex] = {
918
+ ...textPart,
919
+ text: textPart.text + agentChunk.text,
920
+ state: "streaming"
921
+ };
922
+ return [
923
+ ...newConversation.slice(0, -1),
924
+ {
925
+ ...lastMessage,
926
+ parts
927
+ }
928
+ ];
929
+ }
930
+ return newConversation;
931
+ };
583
932
  handleAgentConversation = (chunk, newConversation, metadata) => {
584
933
  if (chunk.type === "agent-execution-start") {
585
934
  const primitiveId = chunk.payload?.args?.primitiveId;
@@ -862,92 +1211,75 @@ class AISdkNetworkTransformer {
862
1211
  };
863
1212
  }
864
1213
 
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;
1214
+ const fromCoreUserMessageToUIMessage = (coreUserMessage) => {
1215
+ const id = `user-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
1216
+ const parts = typeof coreUserMessage.content === "string" ? [
1217
+ {
1218
+ type: "text",
1219
+ text: coreUserMessage.content
1220
+ }
1221
+ ] : coreUserMessage.content.map((part) => {
1222
+ switch (part.type) {
1223
+ case "text": {
1224
+ return {
1225
+ type: "text",
1226
+ text: part.text
1227
+ };
1228
+ }
1229
+ case "image": {
1230
+ const url = typeof part.image === "string" ? part.image : part.image instanceof URL ? part.image.toString() : "";
1231
+ return {
1232
+ type: "file",
1233
+ mediaType: part.mimeType ?? "image/*",
1234
+ url
1235
+ };
1236
+ }
1237
+ case "file": {
1238
+ const url = typeof part.data === "string" ? part.data : part.data instanceof URL ? part.data.toString() : "";
1239
+ return {
1240
+ type: "file",
1241
+ mediaType: part.mimeType,
1242
+ url,
1243
+ ...part.filename !== void 0 ? { filename: part.filename } : {}
1244
+ };
1245
+ }
1246
+ default: {
1247
+ const exhaustiveCheck = part;
1248
+ throw new Error(`Unhandled content part type: ${exhaustiveCheck.type}`);
936
1249
  }
937
1250
  }
938
- return message;
939
1251
  });
1252
+ return {
1253
+ id,
1254
+ role: "user",
1255
+ parts
1256
+ };
940
1257
  };
941
1258
 
942
- const useChat = ({ agentId, initializeMessages }) => {
943
- const [messages, setMessages] = react.useState(
944
- () => resolveInitialMessages(initializeMessages?.() || [])
945
- );
1259
+ const useChat = ({ agentId, resourceId, initializeMessages }) => {
1260
+ const extractRunIdFromMessages = (messages2) => {
1261
+ for (const message of messages2) {
1262
+ const pendingToolApprovals = message.metadata?.pendingToolApprovals;
1263
+ if (pendingToolApprovals && typeof pendingToolApprovals === "object") {
1264
+ const suspensionData = Object.values(pendingToolApprovals)[0];
1265
+ if (suspensionData?.runId) {
1266
+ return suspensionData.runId;
1267
+ }
1268
+ }
1269
+ }
1270
+ return void 0;
1271
+ };
1272
+ const initialMessages = initializeMessages?.() || [];
1273
+ const initialRunId = extractRunIdFromMessages(initialMessages);
1274
+ const _currentRunId = react.useRef(initialRunId);
1275
+ const _onChunk = react.useRef(void 0);
1276
+ const [messages, setMessages] = react.useState(() => resolveInitialMessages(initialMessages));
1277
+ const [toolCallApprovals, setToolCallApprovals] = react.useState({});
946
1278
  const baseClient = useMastraClient();
947
1279
  const [isRunning, setIsRunning] = react.useState(false);
948
1280
  const generate = async ({
949
1281
  coreUserMessages,
950
- runtimeContext,
1282
+ requestContext,
951
1283
  threadId,
952
1284
  modelSettings,
953
1285
  signal,
@@ -973,7 +1305,7 @@ const useChat = ({ agentId, initializeMessages }) => {
973
1305
  const agent = clientWithAbort.getAgent(agentId);
974
1306
  const response = await agent.generate({
975
1307
  messages: coreUserMessages,
976
- runId: agentId,
1308
+ runId: uuid.v4(),
977
1309
  maxSteps,
978
1310
  modelSettings: {
979
1311
  frequencyPenalty,
@@ -985,8 +1317,8 @@ const useChat = ({ agentId, initializeMessages }) => {
985
1317
  topP
986
1318
  },
987
1319
  instructions,
988
- runtimeContext,
989
- ...threadId ? { threadId, resourceId: agentId } : {},
1320
+ requestContext,
1321
+ ...threadId ? { threadId, resourceId: resourceId || agentId } : {},
990
1322
  providerOptions
991
1323
  });
992
1324
  setIsRunning(false);
@@ -1001,7 +1333,7 @@ const useChat = ({ agentId, initializeMessages }) => {
1001
1333
  setMessages((prev) => [...prev, ...mastraUIMessages]);
1002
1334
  }
1003
1335
  };
1004
- const stream = async ({ coreUserMessages, runtimeContext, threadId, onChunk, modelSettings, signal }) => {
1336
+ const stream = async ({ coreUserMessages, requestContext, threadId, onChunk, modelSettings, signal }) => {
1005
1337
  const {
1006
1338
  frequencyPenalty,
1007
1339
  presencePenalty,
@@ -1012,7 +1344,8 @@ const useChat = ({ agentId, initializeMessages }) => {
1012
1344
  topP,
1013
1345
  instructions,
1014
1346
  providerOptions,
1015
- maxSteps
1347
+ maxSteps,
1348
+ requireToolApproval
1016
1349
  } = modelSettings || {};
1017
1350
  setIsRunning(true);
1018
1351
  const clientWithAbort = new clientJs.MastraClient({
@@ -1020,9 +1353,10 @@ const useChat = ({ agentId, initializeMessages }) => {
1020
1353
  abortSignal: signal
1021
1354
  });
1022
1355
  const agent = clientWithAbort.getAgent(agentId);
1356
+ const runId = uuid.v4();
1023
1357
  const response = await agent.stream({
1024
1358
  messages: coreUserMessages,
1025
- runId: agentId,
1359
+ runId,
1026
1360
  maxSteps,
1027
1361
  modelSettings: {
1028
1362
  frequencyPenalty,
@@ -1034,19 +1368,16 @@ const useChat = ({ agentId, initializeMessages }) => {
1034
1368
  topP
1035
1369
  },
1036
1370
  instructions,
1037
- runtimeContext,
1038
- ...threadId ? { threadId, resourceId: agentId } : {},
1039
- providerOptions
1371
+ requestContext,
1372
+ ...threadId ? { threadId, resourceId: resourceId || agentId } : {},
1373
+ providerOptions,
1374
+ requireToolApproval
1040
1375
  });
1041
- if (!response.body) {
1042
- setIsRunning(false);
1043
- throw new Error("[Stream] No response body");
1044
- }
1376
+ _onChunk.current = onChunk;
1377
+ _currentRunId.current = runId;
1045
1378
  await response.processDataStream({
1046
1379
  onChunk: async (chunk) => {
1047
- reactDom.flushSync(() => {
1048
- setMessages((prev) => toUIMessage({ chunk, conversation: prev, metadata: { mode: "stream" } }));
1049
- });
1380
+ setMessages((prev) => toUIMessage({ chunk, conversation: prev, metadata: { mode: "stream" } }));
1050
1381
  onChunk?.(chunk);
1051
1382
  }
1052
1383
  });
@@ -1054,7 +1385,7 @@ const useChat = ({ agentId, initializeMessages }) => {
1054
1385
  };
1055
1386
  const network = async ({
1056
1387
  coreUserMessages,
1057
- runtimeContext,
1388
+ requestContext,
1058
1389
  threadId,
1059
1390
  onNetworkChunk,
1060
1391
  modelSettings,
@@ -1067,6 +1398,7 @@ const useChat = ({ agentId, initializeMessages }) => {
1067
1398
  abortSignal: signal
1068
1399
  });
1069
1400
  const agent = clientWithAbort.getAgent(agentId);
1401
+ const runId = uuid.v4();
1070
1402
  const response = await agent.network({
1071
1403
  messages: coreUserMessages,
1072
1404
  maxSteps,
@@ -1079,31 +1411,72 @@ const useChat = ({ agentId, initializeMessages }) => {
1079
1411
  topK,
1080
1412
  topP
1081
1413
  },
1082
- runId: agentId,
1083
- runtimeContext,
1084
- ...threadId ? { thread: threadId, resourceId: agentId } : {}
1414
+ runId,
1415
+ requestContext,
1416
+ ...threadId ? { thread: threadId, resourceId: resourceId || agentId } : {}
1085
1417
  });
1086
1418
  const transformer = new AISdkNetworkTransformer();
1087
1419
  await response.processDataStream({
1088
1420
  onChunk: async (chunk) => {
1089
- reactDom.flushSync(() => {
1090
- setMessages((prev) => transformer.transform({ chunk, conversation: prev, metadata: { mode: "network" } }));
1091
- });
1421
+ setMessages((prev) => transformer.transform({ chunk, conversation: prev, metadata: { mode: "network" } }));
1092
1422
  onNetworkChunk?.(chunk);
1093
1423
  }
1094
1424
  });
1095
1425
  setIsRunning(false);
1096
1426
  };
1427
+ const handleCancelRun = () => {
1428
+ setIsRunning(false);
1429
+ _currentRunId.current = void 0;
1430
+ _onChunk.current = void 0;
1431
+ };
1432
+ const approveToolCall = async (toolCallId) => {
1433
+ const onChunk = _onChunk.current;
1434
+ const currentRunId = _currentRunId.current;
1435
+ if (!currentRunId)
1436
+ return console.info("[approveToolCall] approveToolCall can only be called after a stream has started");
1437
+ setIsRunning(true);
1438
+ setToolCallApprovals((prev) => ({ ...prev, [toolCallId]: { status: "approved" } }));
1439
+ const agent = baseClient.getAgent(agentId);
1440
+ const response = await agent.approveToolCall({ runId: currentRunId, toolCallId });
1441
+ await response.processDataStream({
1442
+ onChunk: async (chunk) => {
1443
+ setMessages((prev) => toUIMessage({ chunk, conversation: prev, metadata: { mode: "stream" } }));
1444
+ onChunk?.(chunk);
1445
+ }
1446
+ });
1447
+ setIsRunning(false);
1448
+ };
1449
+ const declineToolCall = async (toolCallId) => {
1450
+ const onChunk = _onChunk.current;
1451
+ const currentRunId = _currentRunId.current;
1452
+ if (!currentRunId)
1453
+ return console.info("[declineToolCall] declineToolCall can only be called after a stream has started");
1454
+ setIsRunning(true);
1455
+ setToolCallApprovals((prev) => ({ ...prev, [toolCallId]: { status: "declined" } }));
1456
+ const agent = baseClient.getAgent(agentId);
1457
+ const response = await agent.declineToolCall({ runId: currentRunId, toolCallId });
1458
+ await response.processDataStream({
1459
+ onChunk: async (chunk) => {
1460
+ setMessages((prev) => toUIMessage({ chunk, conversation: prev, metadata: { mode: "stream" } }));
1461
+ onChunk?.(chunk);
1462
+ }
1463
+ });
1464
+ setIsRunning(false);
1465
+ };
1097
1466
  const sendMessage = async ({ mode = "stream", ...args }) => {
1098
1467
  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 }] }]);
1468
+ const coreUserMessages = [nextMessage];
1469
+ if (args.coreUserMessages) {
1470
+ coreUserMessages.push(...args.coreUserMessages);
1471
+ }
1472
+ const uiMessages = coreUserMessages.map(fromCoreUserMessageToUIMessage);
1473
+ setMessages((s) => [...s, ...uiMessages]);
1101
1474
  if (mode === "generate") {
1102
- await generate({ ...args, coreUserMessages: messages2 });
1475
+ await generate({ ...args, coreUserMessages });
1103
1476
  } else if (mode === "stream") {
1104
- await stream({ ...args, coreUserMessages: messages2 });
1477
+ await stream({ ...args, coreUserMessages });
1105
1478
  } else if (mode === "network") {
1106
- await network({ ...args, coreUserMessages: messages2 });
1479
+ await network({ ...args, coreUserMessages });
1107
1480
  }
1108
1481
  };
1109
1482
  return {
@@ -1111,7 +1484,10 @@ const useChat = ({ agentId, initializeMessages }) => {
1111
1484
  sendMessage,
1112
1485
  isRunning,
1113
1486
  messages,
1114
- cancelRun: () => setIsRunning(false)
1487
+ approveToolCall,
1488
+ declineToolCall,
1489
+ cancelRun: handleCancelRun,
1490
+ toolCallApprovals
1115
1491
  };
1116
1492
  };
1117
1493
 
@@ -1485,6 +1861,7 @@ exports.TooltipContentClass = TooltipContentClass;
1485
1861
  exports.TooltipTrigger = TooltipTrigger;
1486
1862
  exports.WorkflowIcon = WorkflowIcon;
1487
1863
  exports.mapWorkflowStreamChunkToWatchResult = mapWorkflowStreamChunkToWatchResult;
1864
+ exports.resolveToChildMessages = resolveToChildMessages;
1488
1865
  exports.toAssistantUIMessage = toAssistantUIMessage;
1489
1866
  exports.toUIMessage = toUIMessage;
1490
1867
  exports.useChat = useChat;