@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.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { jsx, jsxs } from 'react/jsx-runtime';
2
- import { createContext, useContext, useState, Fragment, useLayoutEffect, useRef, useEffect } from 'react';
2
+ import { createContext, useContext, useRef, useState, Fragment, useLayoutEffect, useEffect } from 'react';
3
3
  import { MastraClient } from '@mastra/client-js';
4
- import { flushSync } from 'react-dom';
4
+ import { v4 } from '@lukeed/uuid';
5
5
  import { ChevronDownIcon, CheckIcon, CopyIcon } from 'lucide-react';
6
6
  import { twMerge } from 'tailwind-merge';
7
7
  import { toJsxRuntime } from 'hast-util-to-jsx-runtime';
@@ -250,35 +250,48 @@ const toUIMessage = ({ chunk, conversation, metadata }) => {
250
250
  }
251
251
  ];
252
252
  }
253
+ case "tool-error":
253
254
  case "tool-result": {
254
255
  const lastMessage = result[result.length - 1];
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 (chunk.payload.isError) {
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;
266
+ if (chunk.type === "tool-result" && chunk.payload.isError || chunk.type === "tool-error") {
267
+ const error = chunk.type === "tool-error" ? chunk.payload.error : chunk.payload.result;
264
268
  parts[toolPartIndex] = {
265
269
  type: "dynamic-tool",
266
- toolName: toolPart.toolName,
267
- toolCallId: toolPart.toolCallId,
270
+ toolName,
271
+ toolCallId,
268
272
  state: "output-error",
269
273
  input: toolPart.input,
270
- errorText: String(chunk.payload.result),
274
+ errorText: String(error),
271
275
  callProviderMetadata: chunk.payload.providerMetadata
272
276
  };
273
277
  } else {
274
278
  const isWorkflow = Boolean(chunk.payload.result?.result?.steps);
279
+ const isAgent = chunk?.from === "AGENT";
280
+ let output;
281
+ if (isWorkflow) {
282
+ output = chunk.payload.result?.result;
283
+ } else if (isAgent) {
284
+ output = parts[toolPartIndex].output ?? chunk.payload.result;
285
+ } else {
286
+ output = chunk.payload.result;
287
+ }
275
288
  parts[toolPartIndex] = {
276
289
  type: "dynamic-tool",
277
- toolName: toolPart.toolName,
278
- toolCallId: toolPart.toolCallId,
290
+ toolName,
291
+ toolCallId,
279
292
  state: "output-available",
280
293
  input: toolPart.input,
281
- output: isWorkflow ? chunk.payload.result?.result : chunk.payload.result,
294
+ output,
282
295
  callProviderMetadata: chunk.payload.providerMetadata
283
296
  };
284
297
  }
@@ -297,11 +310,14 @@ const toUIMessage = ({ chunk, conversation, metadata }) => {
297
310
  if (!lastMessage || lastMessage.role !== "assistant") return result;
298
311
  const parts = [...lastMessage.parts];
299
312
  const toolPartIndex = parts.findIndex(
300
- (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
301
314
  );
302
315
  if (toolPartIndex !== -1) {
303
316
  const toolPart = parts[toolPartIndex];
304
- 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;
305
321
  if (chunk.payload.output?.type?.startsWith("workflow-")) {
306
322
  const existingWorkflowState = toolPart.output || {};
307
323
  const updatedWorkflowState = mapWorkflowStreamChunkToWatchResult(
@@ -309,14 +325,24 @@ const toUIMessage = ({ chunk, conversation, metadata }) => {
309
325
  chunk.payload.output
310
326
  );
311
327
  parts[toolPartIndex] = {
312
- ...toolPart,
328
+ type: "dynamic-tool",
329
+ toolName,
330
+ toolCallId,
331
+ state: "input-streaming",
332
+ input,
313
333
  output: updatedWorkflowState
314
334
  };
335
+ } else if (chunk.payload.output?.from === "AGENT" || chunk.payload.output?.from === "USER" && chunk.payload.output?.payload?.output?.type?.startsWith("workflow-")) {
336
+ return toUIMessageFromAgent(chunk.payload.output, conversation);
315
337
  } else {
316
338
  const currentOutput = toolPart.output || [];
317
339
  const existingOutput = Array.isArray(currentOutput) ? currentOutput : [];
318
340
  parts[toolPartIndex] = {
319
- ...toolPart,
341
+ type: "dynamic-tool",
342
+ toolName,
343
+ toolCallId,
344
+ state: "input-streaming",
345
+ input,
320
346
  output: [...existingOutput, chunk.payload.output]
321
347
  };
322
348
  }
@@ -385,15 +411,37 @@ const toUIMessage = ({ chunk, conversation, metadata }) => {
385
411
  }
386
412
  ];
387
413
  }
414
+ case "tool-call-approval": {
415
+ const lastMessage = result[result.length - 1];
416
+ if (!lastMessage || lastMessage.role !== "assistant") return result;
417
+ const lastRequireApprovalMetadata = lastMessage.metadata?.mode === "stream" ? lastMessage.metadata?.requireApprovalMetadata : {};
418
+ return [
419
+ ...result.slice(0, -1),
420
+ {
421
+ ...lastMessage,
422
+ metadata: {
423
+ ...lastMessage.metadata,
424
+ mode: "stream",
425
+ requireApprovalMetadata: {
426
+ ...lastRequireApprovalMetadata,
427
+ [chunk.payload.toolCallId]: {
428
+ toolCallId: chunk.payload.toolCallId,
429
+ toolName: chunk.payload.toolName,
430
+ args: chunk.payload.args
431
+ }
432
+ }
433
+ }
434
+ }
435
+ ];
436
+ }
388
437
  case "finish": {
389
438
  const lastMessage = result[result.length - 1];
390
439
  if (!lastMessage || lastMessage.role !== "assistant") return result;
391
440
  const parts = lastMessage.parts.map((part) => {
392
- if (part.type === "text" && part.state === "streaming") {
393
- return { ...part, state: "done" };
394
- }
395
- if (part.type === "reasoning" && part.state === "streaming") {
396
- return { ...part, state: "done" };
441
+ if (typeof part === "object" && part !== null && "type" in part && "state" in part && part.state === "streaming") {
442
+ if (part.type === "text" || part.type === "reasoning") {
443
+ return { ...part, state: "done" };
444
+ }
397
445
  }
398
446
  return part;
399
447
  });
@@ -427,6 +475,105 @@ const toUIMessage = ({ chunk, conversation, metadata }) => {
427
475
  return result;
428
476
  }
429
477
  };
478
+ const toUIMessageFromAgent = (chunk, conversation, metadata) => {
479
+ const lastMessage = conversation[conversation.length - 1];
480
+ if (!lastMessage || lastMessage.role !== "assistant") return conversation;
481
+ const parts = [...lastMessage.parts];
482
+ if (chunk.type === "text-delta") {
483
+ const agentChunk = chunk.payload;
484
+ const toolPartIndex = parts.findIndex((part) => part.type === "dynamic-tool");
485
+ if (toolPartIndex === -1) return conversation;
486
+ const toolPart = parts[toolPartIndex];
487
+ const childMessages = toolPart?.output?.childMessages || [];
488
+ const lastChildMessage = childMessages[childMessages.length - 1];
489
+ const textMessage = { type: "text", content: (lastChildMessage?.content || "") + agentChunk.text };
490
+ const nextMessages = lastChildMessage?.type === "text" ? [...childMessages.slice(0, -1), textMessage] : [...childMessages, textMessage];
491
+ parts[toolPartIndex] = {
492
+ ...toolPart,
493
+ output: {
494
+ childMessages: nextMessages
495
+ }
496
+ };
497
+ } else if (chunk.type === "tool-call") {
498
+ const agentChunk = chunk.payload;
499
+ const toolPartIndex = parts.findIndex((part) => part.type === "dynamic-tool");
500
+ if (toolPartIndex === -1) return conversation;
501
+ const toolPart = parts[toolPartIndex];
502
+ const childMessages = toolPart?.output?.childMessages || [];
503
+ parts[toolPartIndex] = {
504
+ ...toolPart,
505
+ output: {
506
+ ...toolPart?.output,
507
+ childMessages: [
508
+ ...childMessages,
509
+ {
510
+ type: "tool",
511
+ toolCallId: agentChunk.toolCallId,
512
+ toolName: agentChunk.toolName,
513
+ args: agentChunk.args
514
+ }
515
+ ]
516
+ }
517
+ };
518
+ } else if (chunk.type === "tool-output") {
519
+ const agentChunk = chunk.payload;
520
+ const toolPartIndex = parts.findIndex((part) => part.type === "dynamic-tool");
521
+ if (toolPartIndex === -1) return conversation;
522
+ const toolPart = parts[toolPartIndex];
523
+ if (agentChunk?.output?.type?.startsWith("workflow-")) {
524
+ const childMessages = toolPart?.output?.childMessages || [];
525
+ const lastToolIndex = childMessages.length - 1;
526
+ const currentMessage = childMessages[lastToolIndex];
527
+ const actualExistingWorkflowState = currentMessage?.toolOutput || {};
528
+ const updatedWorkflowState = mapWorkflowStreamChunkToWatchResult(actualExistingWorkflowState, agentChunk.output);
529
+ if (lastToolIndex >= 0 && childMessages[lastToolIndex]?.type === "tool") {
530
+ parts[toolPartIndex] = {
531
+ ...toolPart,
532
+ output: {
533
+ ...toolPart?.output,
534
+ childMessages: [
535
+ ...childMessages.slice(0, -1),
536
+ {
537
+ ...currentMessage,
538
+ toolOutput: { ...updatedWorkflowState, runId: agentChunk.output.runId }
539
+ }
540
+ ]
541
+ }
542
+ };
543
+ }
544
+ }
545
+ } else if (chunk.type === "tool-result") {
546
+ const agentChunk = chunk.payload;
547
+ const toolPartIndex = parts.findIndex((part) => part.type === "dynamic-tool");
548
+ if (toolPartIndex === -1) return conversation;
549
+ const toolPart = parts[toolPartIndex];
550
+ const childMessages = toolPart?.output?.childMessages || [];
551
+ const lastToolIndex = childMessages.length - 1;
552
+ const isWorkflow = agentChunk?.toolName?.startsWith("workflow-");
553
+ if (lastToolIndex >= 0 && childMessages[lastToolIndex]?.type === "tool") {
554
+ parts[toolPartIndex] = {
555
+ ...toolPart,
556
+ output: {
557
+ ...toolPart?.output,
558
+ childMessages: [
559
+ ...childMessages.slice(0, -1),
560
+ {
561
+ ...childMessages[lastToolIndex],
562
+ toolOutput: isWorkflow ? { ...agentChunk.result?.result, runId: agentChunk.result?.runId } : agentChunk.result
563
+ }
564
+ ]
565
+ }
566
+ };
567
+ }
568
+ }
569
+ return [
570
+ ...conversation.slice(0, -1),
571
+ {
572
+ ...lastMessage,
573
+ parts
574
+ }
575
+ ];
576
+ };
430
577
 
431
578
  const toAssistantUIMessage = (message) => {
432
579
  const extendedMessage = message;
@@ -466,13 +613,23 @@ const toAssistantUIMessage = (message) => {
466
613
  };
467
614
  }
468
615
  if (part.type === "file") {
469
- return {
470
- type: "file",
471
- mimeType: part.mediaType,
472
- data: part.url,
473
- // Use URL as data source
474
- metadata: message.metadata
475
- };
616
+ const type = part.mediaType.includes("image/") ? "image" : "file";
617
+ if (type === "file") {
618
+ return {
619
+ type,
620
+ mimeType: part.mediaType,
621
+ data: part.url,
622
+ // Use URL as data source
623
+ metadata: message.metadata
624
+ };
625
+ }
626
+ if (type === "image") {
627
+ return {
628
+ type,
629
+ image: part.url,
630
+ metadata: message.metadata
631
+ };
632
+ }
476
633
  }
477
634
  if (part.type === "dynamic-tool") {
478
635
  const baseToolCall = {
@@ -508,6 +665,20 @@ const toAssistantUIMessage = (message) => {
508
665
  }
509
666
  return baseToolCall;
510
667
  }
668
+ const requireApprovalMetadata = extendedMessage.metadata?.requireApprovalMetadata;
669
+ const partToolCallId = "toolCallId" in part && typeof part.toolCallId === "string" ? part.toolCallId : void 0;
670
+ const suspensionData = partToolCallId ? requireApprovalMetadata?.[partToolCallId] : void 0;
671
+ if (suspensionData) {
672
+ const toolName = "toolName" in part && typeof part.toolName === "string" ? part.toolName : part.type.startsWith("tool-") ? part.type.substring(5) : "";
673
+ return {
674
+ type: "tool-call",
675
+ toolCallId: partToolCallId,
676
+ toolName,
677
+ argsText: "input" in part ? JSON.stringify(part.input) : "{}",
678
+ args: "input" in part ? part.input : {},
679
+ metadata: extendedMessage.metadata
680
+ };
681
+ }
511
682
  return {
512
683
  type: "text",
513
684
  text: "",
@@ -547,9 +718,129 @@ const toAssistantUIMessage = (message) => {
547
718
  return threadMessage;
548
719
  };
549
720
 
721
+ const resolveInitialMessages = (messages) => {
722
+ return messages.map((message) => {
723
+ const networkPart = message.parts.find(
724
+ (part) => typeof part === "object" && part !== null && "type" in part && part.type === "text" && "text" in part && typeof part.text === "string" && part.text.includes('"isNetwork":true')
725
+ );
726
+ if (networkPart && networkPart.type === "text") {
727
+ try {
728
+ const json = JSON.parse(networkPart.text);
729
+ if (json.isNetwork === true) {
730
+ const selectionReason = json.selectionReason || "";
731
+ const primitiveType = json.primitiveType || "";
732
+ const primitiveId = json.primitiveId || "";
733
+ const finalResult = json.finalResult;
734
+ const toolCalls = finalResult?.toolCalls || [];
735
+ const childMessages = [];
736
+ for (const toolCall of toolCalls) {
737
+ if (toolCall.type === "tool-call" && toolCall.payload) {
738
+ const toolCallId = toolCall.payload.toolCallId;
739
+ let toolResult;
740
+ for (const message2 of finalResult?.messages || []) {
741
+ for (const part of message2.content || []) {
742
+ if (typeof part === "object" && part.type === "tool-result" && part.toolCallId === toolCallId) {
743
+ toolResult = part;
744
+ break;
745
+ }
746
+ }
747
+ }
748
+ const isWorkflow = Boolean(toolResult?.result?.result?.steps);
749
+ childMessages.push({
750
+ type: "tool",
751
+ toolCallId: toolCall.payload.toolCallId,
752
+ toolName: toolCall.payload.toolName,
753
+ args: toolCall.payload.args,
754
+ toolOutput: isWorkflow ? toolResult?.result?.result : toolResult?.result
755
+ });
756
+ }
757
+ }
758
+ if (finalResult && finalResult.text) {
759
+ childMessages.push({
760
+ type: "text",
761
+ content: finalResult.text
762
+ });
763
+ }
764
+ const result = {
765
+ childMessages,
766
+ result: finalResult?.text || ""
767
+ };
768
+ const nextMessage = {
769
+ role: "assistant",
770
+ parts: [
771
+ {
772
+ type: "dynamic-tool",
773
+ toolCallId: primitiveId,
774
+ toolName: primitiveId,
775
+ state: "output-available",
776
+ input: json.input,
777
+ output: result
778
+ }
779
+ ],
780
+ id: message.id,
781
+ metadata: {
782
+ ...message.metadata,
783
+ mode: "network",
784
+ selectionReason,
785
+ agentInput: json.input,
786
+ from: primitiveType === "agent" ? "AGENT" : "WORKFLOW"
787
+ }
788
+ };
789
+ return nextMessage;
790
+ }
791
+ } catch (error) {
792
+ return message;
793
+ }
794
+ }
795
+ const extendedMessage = message;
796
+ const pendingToolApprovals = extendedMessage.metadata?.pendingToolApprovals;
797
+ if (pendingToolApprovals && typeof pendingToolApprovals === "object") {
798
+ return {
799
+ ...message,
800
+ metadata: {
801
+ ...message.metadata,
802
+ mode: "stream",
803
+ requireApprovalMetadata: pendingToolApprovals
804
+ }
805
+ };
806
+ }
807
+ return message;
808
+ });
809
+ };
810
+ const resolveToChildMessages = (messages) => {
811
+ const assistantMessage = messages.find((message) => message.role === "assistant");
812
+ if (!assistantMessage) return [];
813
+ const parts = assistantMessage.parts;
814
+ let childMessages = [];
815
+ for (const part of parts) {
816
+ const toolPart = part;
817
+ if (part.type.startsWith("tool-")) {
818
+ const toolName = part.type.substring("tool-".length);
819
+ const isWorkflow = toolName.startsWith("workflow-");
820
+ childMessages.push({
821
+ type: "tool",
822
+ toolCallId: toolPart.toolCallId,
823
+ toolName,
824
+ args: toolPart.input,
825
+ toolOutput: isWorkflow ? { ...toolPart.output?.result, runId: toolPart.output?.runId } : toolPart.output
826
+ });
827
+ }
828
+ if (part.type === "text") {
829
+ childMessages.push({
830
+ type: "text",
831
+ content: toolPart.text
832
+ });
833
+ }
834
+ }
835
+ return childMessages;
836
+ };
837
+
550
838
  class AISdkNetworkTransformer {
551
839
  transform({ chunk, conversation, metadata }) {
552
840
  const newConversation = [...conversation];
841
+ if (chunk.type === "routing-agent-text-delta") {
842
+ return this.handleRoutingAgentConversation(chunk, newConversation);
843
+ }
553
844
  if (chunk.type.startsWith("agent-execution-")) {
554
845
  return this.handleAgentConversation(chunk, newConversation, metadata);
555
846
  }
@@ -560,22 +851,80 @@ class AISdkNetworkTransformer {
560
851
  return this.handleToolConversation(chunk, newConversation, metadata);
561
852
  }
562
853
  if (chunk.type === "network-execution-event-step-finish") {
563
- const newMessage = {
564
- id: `network-execution-event-step-finish-${chunk.runId}-${Date.now()}`,
565
- role: "assistant",
566
- parts: [
854
+ const lastMessage = newConversation[newConversation.length - 1];
855
+ if (!lastMessage || lastMessage.role !== "assistant") return newConversation;
856
+ const agentChunk = chunk.payload;
857
+ const parts = [...lastMessage.parts];
858
+ const textPartIndex = parts.findIndex((part) => part.type === "text");
859
+ if (textPartIndex === -1) {
860
+ parts.push({
861
+ type: "text",
862
+ text: agentChunk.result,
863
+ state: "done"
864
+ });
865
+ return [
866
+ ...newConversation.slice(0, -1),
567
867
  {
568
- type: "text",
569
- text: chunk.payload?.result || "",
570
- state: "done"
868
+ ...lastMessage,
869
+ parts
571
870
  }
572
- ],
573
- metadata
574
- };
575
- return [...newConversation, newMessage];
871
+ ];
872
+ }
873
+ const textPart = parts[textPartIndex];
874
+ if (textPart.type === "text") {
875
+ parts[textPartIndex] = {
876
+ ...textPart,
877
+ state: "done"
878
+ };
879
+ return [
880
+ ...newConversation.slice(0, -1),
881
+ {
882
+ ...lastMessage,
883
+ parts
884
+ }
885
+ ];
886
+ }
887
+ return newConversation;
576
888
  }
577
889
  return newConversation;
578
890
  }
891
+ handleRoutingAgentConversation = (chunk, newConversation) => {
892
+ const lastMessage = newConversation[newConversation.length - 1];
893
+ if (!lastMessage || lastMessage.role !== "assistant") return newConversation;
894
+ const agentChunk = chunk.payload;
895
+ const parts = [...lastMessage.parts];
896
+ const textPartIndex = parts.findIndex((part) => part.type === "text");
897
+ if (textPartIndex === -1) {
898
+ parts.push({
899
+ type: "text",
900
+ text: agentChunk.text,
901
+ state: "streaming"
902
+ });
903
+ return [
904
+ ...newConversation.slice(0, -1),
905
+ {
906
+ ...lastMessage,
907
+ parts
908
+ }
909
+ ];
910
+ }
911
+ const textPart = parts[textPartIndex];
912
+ if (textPart.type === "text") {
913
+ parts[textPartIndex] = {
914
+ ...textPart,
915
+ text: textPart.text + agentChunk.text,
916
+ state: "streaming"
917
+ };
918
+ return [
919
+ ...newConversation.slice(0, -1),
920
+ {
921
+ ...lastMessage,
922
+ parts
923
+ }
924
+ ];
925
+ }
926
+ return newConversation;
927
+ };
579
928
  handleAgentConversation = (chunk, newConversation, metadata) => {
580
929
  if (chunk.type === "agent-execution-start") {
581
930
  const primitiveId = chunk.payload?.args?.primitiveId;
@@ -858,92 +1207,75 @@ class AISdkNetworkTransformer {
858
1207
  };
859
1208
  }
860
1209
 
861
- const resolveInitialMessages = (messages) => {
862
- return messages.map((message) => {
863
- const networkPart = message.parts.find((part) => part.type === "text" && part.text.includes('"isNetwork":true'));
864
- if (networkPart && networkPart.type === "text") {
865
- try {
866
- const json = JSON.parse(networkPart.text);
867
- if (json.isNetwork === true) {
868
- const selectionReason = json.selectionReason || "";
869
- const primitiveType = json.primitiveType || "";
870
- const primitiveId = json.primitiveId || "";
871
- const finalResult = json.finalResult;
872
- const toolCalls = finalResult?.toolCalls || [];
873
- const childMessages = [];
874
- for (const toolCall of toolCalls) {
875
- if (toolCall.type === "tool-call" && toolCall.payload) {
876
- const toolCallId = toolCall.payload.toolCallId;
877
- let toolResult;
878
- for (const message2 of finalResult?.messages || []) {
879
- for (const part of message2.content || []) {
880
- if (typeof part === "object" && part.type === "tool-result" && part.toolCallId === toolCallId) {
881
- toolResult = part;
882
- break;
883
- }
884
- }
885
- }
886
- const isWorkflow = Boolean(toolResult?.result?.result?.steps);
887
- childMessages.push({
888
- type: "tool",
889
- toolCallId: toolCall.payload.toolCallId,
890
- toolName: toolCall.payload.toolName,
891
- args: toolCall.payload.args,
892
- toolOutput: isWorkflow ? toolResult?.result?.result : toolResult?.result
893
- });
894
- }
895
- }
896
- if (finalResult && finalResult.text) {
897
- childMessages.push({
898
- type: "text",
899
- content: finalResult.text
900
- });
901
- }
902
- const result = {
903
- childMessages,
904
- result: finalResult?.text || ""
905
- };
906
- console.log("json", json);
907
- const nextMessage = {
908
- role: "assistant",
909
- parts: [
910
- {
911
- type: "dynamic-tool",
912
- toolCallId: primitiveId,
913
- toolName: primitiveId,
914
- state: "output-available",
915
- input: json.input,
916
- output: result
917
- }
918
- ],
919
- id: message.id,
920
- metadata: {
921
- ...message.metadata,
922
- mode: "network",
923
- selectionReason,
924
- agentInput: json.input,
925
- from: primitiveType === "agent" ? "AGENT" : "WORKFLOW"
926
- }
927
- };
928
- return nextMessage;
929
- }
930
- } catch (error) {
931
- return message;
1210
+ const fromCoreUserMessageToUIMessage = (coreUserMessage) => {
1211
+ const id = `user-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
1212
+ const parts = typeof coreUserMessage.content === "string" ? [
1213
+ {
1214
+ type: "text",
1215
+ text: coreUserMessage.content
1216
+ }
1217
+ ] : coreUserMessage.content.map((part) => {
1218
+ switch (part.type) {
1219
+ case "text": {
1220
+ return {
1221
+ type: "text",
1222
+ text: part.text
1223
+ };
1224
+ }
1225
+ case "image": {
1226
+ const url = typeof part.image === "string" ? part.image : part.image instanceof URL ? part.image.toString() : "";
1227
+ return {
1228
+ type: "file",
1229
+ mediaType: part.mimeType ?? "image/*",
1230
+ url
1231
+ };
1232
+ }
1233
+ case "file": {
1234
+ const url = typeof part.data === "string" ? part.data : part.data instanceof URL ? part.data.toString() : "";
1235
+ return {
1236
+ type: "file",
1237
+ mediaType: part.mimeType,
1238
+ url,
1239
+ ...part.filename !== void 0 ? { filename: part.filename } : {}
1240
+ };
1241
+ }
1242
+ default: {
1243
+ const exhaustiveCheck = part;
1244
+ throw new Error(`Unhandled content part type: ${exhaustiveCheck.type}`);
932
1245
  }
933
1246
  }
934
- return message;
935
1247
  });
1248
+ return {
1249
+ id,
1250
+ role: "user",
1251
+ parts
1252
+ };
936
1253
  };
937
1254
 
938
- const useChat = ({ agentId, initializeMessages }) => {
939
- const [messages, setMessages] = useState(
940
- () => resolveInitialMessages(initializeMessages?.() || [])
941
- );
1255
+ const useChat = ({ agentId, resourceId, initializeMessages }) => {
1256
+ const extractRunIdFromMessages = (messages2) => {
1257
+ for (const message of messages2) {
1258
+ const pendingToolApprovals = message.metadata?.pendingToolApprovals;
1259
+ if (pendingToolApprovals && typeof pendingToolApprovals === "object") {
1260
+ const suspensionData = Object.values(pendingToolApprovals)[0];
1261
+ if (suspensionData?.runId) {
1262
+ return suspensionData.runId;
1263
+ }
1264
+ }
1265
+ }
1266
+ return void 0;
1267
+ };
1268
+ const initialMessages = initializeMessages?.() || [];
1269
+ const initialRunId = extractRunIdFromMessages(initialMessages);
1270
+ const _currentRunId = useRef(initialRunId);
1271
+ const _onChunk = useRef(void 0);
1272
+ const [messages, setMessages] = useState(() => resolveInitialMessages(initialMessages));
1273
+ const [toolCallApprovals, setToolCallApprovals] = useState({});
942
1274
  const baseClient = useMastraClient();
943
1275
  const [isRunning, setIsRunning] = useState(false);
944
1276
  const generate = async ({
945
1277
  coreUserMessages,
946
- runtimeContext,
1278
+ requestContext,
947
1279
  threadId,
948
1280
  modelSettings,
949
1281
  signal,
@@ -969,7 +1301,7 @@ const useChat = ({ agentId, initializeMessages }) => {
969
1301
  const agent = clientWithAbort.getAgent(agentId);
970
1302
  const response = await agent.generate({
971
1303
  messages: coreUserMessages,
972
- runId: agentId,
1304
+ runId: v4(),
973
1305
  maxSteps,
974
1306
  modelSettings: {
975
1307
  frequencyPenalty,
@@ -981,8 +1313,8 @@ const useChat = ({ agentId, initializeMessages }) => {
981
1313
  topP
982
1314
  },
983
1315
  instructions,
984
- runtimeContext,
985
- ...threadId ? { threadId, resourceId: agentId } : {},
1316
+ requestContext,
1317
+ ...threadId ? { threadId, resourceId: resourceId || agentId } : {},
986
1318
  providerOptions
987
1319
  });
988
1320
  setIsRunning(false);
@@ -997,7 +1329,7 @@ const useChat = ({ agentId, initializeMessages }) => {
997
1329
  setMessages((prev) => [...prev, ...mastraUIMessages]);
998
1330
  }
999
1331
  };
1000
- const stream = async ({ coreUserMessages, runtimeContext, threadId, onChunk, modelSettings, signal }) => {
1332
+ const stream = async ({ coreUserMessages, requestContext, threadId, onChunk, modelSettings, signal }) => {
1001
1333
  const {
1002
1334
  frequencyPenalty,
1003
1335
  presencePenalty,
@@ -1008,7 +1340,8 @@ const useChat = ({ agentId, initializeMessages }) => {
1008
1340
  topP,
1009
1341
  instructions,
1010
1342
  providerOptions,
1011
- maxSteps
1343
+ maxSteps,
1344
+ requireToolApproval
1012
1345
  } = modelSettings || {};
1013
1346
  setIsRunning(true);
1014
1347
  const clientWithAbort = new MastraClient({
@@ -1016,9 +1349,10 @@ const useChat = ({ agentId, initializeMessages }) => {
1016
1349
  abortSignal: signal
1017
1350
  });
1018
1351
  const agent = clientWithAbort.getAgent(agentId);
1352
+ const runId = v4();
1019
1353
  const response = await agent.stream({
1020
1354
  messages: coreUserMessages,
1021
- runId: agentId,
1355
+ runId,
1022
1356
  maxSteps,
1023
1357
  modelSettings: {
1024
1358
  frequencyPenalty,
@@ -1030,19 +1364,16 @@ const useChat = ({ agentId, initializeMessages }) => {
1030
1364
  topP
1031
1365
  },
1032
1366
  instructions,
1033
- runtimeContext,
1034
- ...threadId ? { threadId, resourceId: agentId } : {},
1035
- providerOptions
1367
+ requestContext,
1368
+ ...threadId ? { threadId, resourceId: resourceId || agentId } : {},
1369
+ providerOptions,
1370
+ requireToolApproval
1036
1371
  });
1037
- if (!response.body) {
1038
- setIsRunning(false);
1039
- throw new Error("[Stream] No response body");
1040
- }
1372
+ _onChunk.current = onChunk;
1373
+ _currentRunId.current = runId;
1041
1374
  await response.processDataStream({
1042
1375
  onChunk: async (chunk) => {
1043
- flushSync(() => {
1044
- setMessages((prev) => toUIMessage({ chunk, conversation: prev, metadata: { mode: "stream" } }));
1045
- });
1376
+ setMessages((prev) => toUIMessage({ chunk, conversation: prev, metadata: { mode: "stream" } }));
1046
1377
  onChunk?.(chunk);
1047
1378
  }
1048
1379
  });
@@ -1050,7 +1381,7 @@ const useChat = ({ agentId, initializeMessages }) => {
1050
1381
  };
1051
1382
  const network = async ({
1052
1383
  coreUserMessages,
1053
- runtimeContext,
1384
+ requestContext,
1054
1385
  threadId,
1055
1386
  onNetworkChunk,
1056
1387
  modelSettings,
@@ -1063,6 +1394,7 @@ const useChat = ({ agentId, initializeMessages }) => {
1063
1394
  abortSignal: signal
1064
1395
  });
1065
1396
  const agent = clientWithAbort.getAgent(agentId);
1397
+ const runId = v4();
1066
1398
  const response = await agent.network({
1067
1399
  messages: coreUserMessages,
1068
1400
  maxSteps,
@@ -1075,31 +1407,72 @@ const useChat = ({ agentId, initializeMessages }) => {
1075
1407
  topK,
1076
1408
  topP
1077
1409
  },
1078
- runId: agentId,
1079
- runtimeContext,
1080
- ...threadId ? { thread: threadId, resourceId: agentId } : {}
1410
+ runId,
1411
+ requestContext,
1412
+ ...threadId ? { thread: threadId, resourceId: resourceId || agentId } : {}
1081
1413
  });
1082
1414
  const transformer = new AISdkNetworkTransformer();
1083
1415
  await response.processDataStream({
1084
1416
  onChunk: async (chunk) => {
1085
- flushSync(() => {
1086
- setMessages((prev) => transformer.transform({ chunk, conversation: prev, metadata: { mode: "network" } }));
1087
- });
1417
+ setMessages((prev) => transformer.transform({ chunk, conversation: prev, metadata: { mode: "network" } }));
1088
1418
  onNetworkChunk?.(chunk);
1089
1419
  }
1090
1420
  });
1091
1421
  setIsRunning(false);
1092
1422
  };
1423
+ const handleCancelRun = () => {
1424
+ setIsRunning(false);
1425
+ _currentRunId.current = void 0;
1426
+ _onChunk.current = void 0;
1427
+ };
1428
+ const approveToolCall = async (toolCallId) => {
1429
+ const onChunk = _onChunk.current;
1430
+ const currentRunId = _currentRunId.current;
1431
+ if (!currentRunId)
1432
+ return console.info("[approveToolCall] approveToolCall can only be called after a stream has started");
1433
+ setIsRunning(true);
1434
+ setToolCallApprovals((prev) => ({ ...prev, [toolCallId]: { status: "approved" } }));
1435
+ const agent = baseClient.getAgent(agentId);
1436
+ const response = await agent.approveToolCall({ runId: currentRunId, toolCallId });
1437
+ await response.processDataStream({
1438
+ onChunk: async (chunk) => {
1439
+ setMessages((prev) => toUIMessage({ chunk, conversation: prev, metadata: { mode: "stream" } }));
1440
+ onChunk?.(chunk);
1441
+ }
1442
+ });
1443
+ setIsRunning(false);
1444
+ };
1445
+ const declineToolCall = async (toolCallId) => {
1446
+ const onChunk = _onChunk.current;
1447
+ const currentRunId = _currentRunId.current;
1448
+ if (!currentRunId)
1449
+ return console.info("[declineToolCall] declineToolCall can only be called after a stream has started");
1450
+ setIsRunning(true);
1451
+ setToolCallApprovals((prev) => ({ ...prev, [toolCallId]: { status: "declined" } }));
1452
+ const agent = baseClient.getAgent(agentId);
1453
+ const response = await agent.declineToolCall({ runId: currentRunId, toolCallId });
1454
+ await response.processDataStream({
1455
+ onChunk: async (chunk) => {
1456
+ setMessages((prev) => toUIMessage({ chunk, conversation: prev, metadata: { mode: "stream" } }));
1457
+ onChunk?.(chunk);
1458
+ }
1459
+ });
1460
+ setIsRunning(false);
1461
+ };
1093
1462
  const sendMessage = async ({ mode = "stream", ...args }) => {
1094
1463
  const nextMessage = { role: "user", content: [{ type: "text", text: args.message }] };
1095
- const messages2 = args.coreUserMessages ? [nextMessage, ...args.coreUserMessages] : [nextMessage];
1096
- setMessages((s) => [...s, { role: "user", parts: [{ type: "text", text: args.message }] }]);
1464
+ const coreUserMessages = [nextMessage];
1465
+ if (args.coreUserMessages) {
1466
+ coreUserMessages.push(...args.coreUserMessages);
1467
+ }
1468
+ const uiMessages = coreUserMessages.map(fromCoreUserMessageToUIMessage);
1469
+ setMessages((s) => [...s, ...uiMessages]);
1097
1470
  if (mode === "generate") {
1098
- await generate({ ...args, coreUserMessages: messages2 });
1471
+ await generate({ ...args, coreUserMessages });
1099
1472
  } else if (mode === "stream") {
1100
- await stream({ ...args, coreUserMessages: messages2 });
1473
+ await stream({ ...args, coreUserMessages });
1101
1474
  } else if (mode === "network") {
1102
- await network({ ...args, coreUserMessages: messages2 });
1475
+ await network({ ...args, coreUserMessages });
1103
1476
  }
1104
1477
  };
1105
1478
  return {
@@ -1107,7 +1480,10 @@ const useChat = ({ agentId, initializeMessages }) => {
1107
1480
  sendMessage,
1108
1481
  isRunning,
1109
1482
  messages,
1110
- cancelRun: () => setIsRunning(false)
1483
+ approveToolCall,
1484
+ declineToolCall,
1485
+ cancelRun: handleCancelRun,
1486
+ toolCallApprovals
1111
1487
  };
1112
1488
  };
1113
1489
 
@@ -1426,5 +1802,5 @@ const MessageStreaming = ({ className, ...props }) => {
1426
1802
  return /* @__PURE__ */ jsx("span", { className: className || MessageStreamingClass, ...props });
1427
1803
  };
1428
1804
 
1429
- export { AgentIcon, CodeBlock, CodeBlockClass, CodeCopyButton, Entity, EntityCaret, EntityContent, EntityContentClass, EntityTrigger, EntityTriggerClass, EntityTriggerVariantClasses, Entry, EntryClass, EntryTitle, EntryTitleClass, Icon, IconButton, IconButtonClass, IconSizes, MastraReactProvider, Message, MessageActions, MessageActionsClass, MessageClass, MessageContent, MessageContentClass, MessageList, MessageListClass, MessageStreaming, MessageStreamingClass, MessageUsage, MessageUsageClass, MessageUsageEntry, MessageUsageEntryClass, MessageUsageValue, MessageUsageValueClass, MessageUsages, MessageUsagesClass, ToolApproval, ToolApprovalActions, ToolApprovalActionsClass, ToolApprovalClass, ToolApprovalContent, ToolApprovalContentClass, ToolApprovalHeader, ToolApprovalHeaderClass, ToolApprovalTitle, ToolApprovalTitleClass, ToolsIcon, Tooltip, TooltipContent, TooltipContentClass, TooltipTrigger, WorkflowIcon, mapWorkflowStreamChunkToWatchResult, toAssistantUIMessage, toUIMessage, useChat, useEntity, useMastraClient };
1805
+ export { AgentIcon, CodeBlock, CodeBlockClass, CodeCopyButton, Entity, EntityCaret, EntityContent, EntityContentClass, EntityTrigger, EntityTriggerClass, EntityTriggerVariantClasses, Entry, EntryClass, EntryTitle, EntryTitleClass, Icon, IconButton, IconButtonClass, IconSizes, MastraReactProvider, Message, MessageActions, MessageActionsClass, MessageClass, MessageContent, MessageContentClass, MessageList, MessageListClass, MessageStreaming, MessageStreamingClass, MessageUsage, MessageUsageClass, MessageUsageEntry, MessageUsageEntryClass, MessageUsageValue, MessageUsageValueClass, MessageUsages, MessageUsagesClass, ToolApproval, ToolApprovalActions, ToolApprovalActionsClass, ToolApprovalClass, ToolApprovalContent, ToolApprovalContentClass, ToolApprovalHeader, ToolApprovalHeaderClass, ToolApprovalTitle, ToolApprovalTitleClass, ToolsIcon, Tooltip, TooltipContent, TooltipContentClass, TooltipTrigger, WorkflowIcon, mapWorkflowStreamChunkToWatchResult, resolveToChildMessages, toAssistantUIMessage, toUIMessage, useChat, useEntity, useMastraClient };
1430
1806
  //# sourceMappingURL=index.js.map