@mastra/react 0.0.0-monorepo-binary-20251013210052 → 0.0.0-new-button-export-20251219130424

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';
@@ -47,7 +47,7 @@ const mapWorkflowStreamChunkToWatchResult = (prev, chunk) => {
47
47
  return {
48
48
  ...prev,
49
49
  status: chunk.payload.workflowStatus,
50
- ...finalStatus === "success" && lastStep?.status === "success" ? { result: lastStep?.output } : finalStatus === "failed" && lastStep?.status === "failed" ? { error: lastStep?.error } : {}
50
+ ...finalStatus === "success" && lastStep?.status === "success" ? { result: lastStep?.output } : finalStatus === "failed" && lastStep?.status === "failed" ? { error: lastStep?.error } : finalStatus === "tripwire" && chunk.payload.tripwire ? { tripwire: chunk.payload.tripwire } : {}
51
51
  };
52
52
  }
53
53
  const { stepCallId, stepName, ...newPayload } = chunk.payload ?? {};
@@ -99,6 +99,34 @@ const mapWorkflowStreamChunkToWatchResult = (prev, chunk) => {
99
99
  };
100
100
  const toUIMessage = ({ chunk, conversation, metadata }) => {
101
101
  const result = [...conversation];
102
+ if (chunk.type.startsWith("data-")) {
103
+ const lastMessage = result[result.length - 1];
104
+ if (!lastMessage || lastMessage.role !== "assistant") {
105
+ const newMessage = {
106
+ id: `data-${chunk.runId}-${Date.now()}`,
107
+ role: "assistant",
108
+ parts: [
109
+ {
110
+ type: chunk.type,
111
+ data: "data" in chunk ? chunk.data : void 0
112
+ }
113
+ ],
114
+ metadata
115
+ };
116
+ return [...result, newMessage];
117
+ }
118
+ const updatedMessage = {
119
+ ...lastMessage,
120
+ parts: [
121
+ ...lastMessage.parts,
122
+ {
123
+ type: chunk.type,
124
+ data: "data" in chunk ? chunk.data : void 0
125
+ }
126
+ ]
127
+ };
128
+ return [...result.slice(0, -1), updatedMessage];
129
+ }
102
130
  switch (chunk.type) {
103
131
  case "tripwire": {
104
132
  const newMessage = {
@@ -107,19 +135,24 @@ const toUIMessage = ({ chunk, conversation, metadata }) => {
107
135
  parts: [
108
136
  {
109
137
  type: "text",
110
- text: chunk.payload.tripwireReason
138
+ text: chunk.payload.reason
111
139
  }
112
140
  ],
113
141
  metadata: {
114
142
  ...metadata,
115
- status: "warning"
143
+ status: "tripwire",
144
+ tripwire: {
145
+ retry: chunk.payload.retry,
146
+ tripwirePayload: chunk.payload.metadata,
147
+ processorId: chunk.payload.processorId
148
+ }
116
149
  }
117
150
  };
118
151
  return [...result, newMessage];
119
152
  }
120
153
  case "start": {
121
154
  const newMessage = {
122
- id: `start-${chunk.runId + Date.now()}`,
155
+ id: typeof chunk.payload.messageId === "string" ? chunk.payload.messageId : `start-${chunk.runId + Date.now()}`,
123
156
  role: "assistant",
124
157
  parts: [],
125
158
  metadata
@@ -250,35 +283,48 @@ const toUIMessage = ({ chunk, conversation, metadata }) => {
250
283
  }
251
284
  ];
252
285
  }
286
+ case "tool-error":
253
287
  case "tool-result": {
254
288
  const lastMessage = result[result.length - 1];
255
289
  if (!lastMessage || lastMessage.role !== "assistant") return result;
256
290
  const parts = [...lastMessage.parts];
257
291
  const toolPartIndex = parts.findIndex(
258
- (part) => part.type === "dynamic-tool" && "toolCallId" in part && part.toolCallId === chunk.payload.toolCallId
292
+ (part) => (part.type === "dynamic-tool" || typeof part.type === "string" && part.type.startsWith("tool-")) && "toolCallId" in part && part.toolCallId === chunk.payload.toolCallId
259
293
  );
260
294
  if (toolPartIndex !== -1) {
261
295
  const toolPart = parts[toolPartIndex];
262
- if (toolPart.type === "dynamic-tool") {
263
- if (chunk.payload.isError) {
296
+ if (toolPart.type === "dynamic-tool" || typeof toolPart.type === "string" && toolPart.type.startsWith("tool-")) {
297
+ const toolName = "toolName" in toolPart && typeof toolPart.toolName === "string" ? toolPart.toolName : toolPart.type.startsWith("tool-") ? toolPart.type.substring(5) : "";
298
+ const toolCallId = toolPart.toolCallId;
299
+ if (chunk.type === "tool-result" && chunk.payload.isError || chunk.type === "tool-error") {
300
+ const error = chunk.type === "tool-error" ? chunk.payload.error : chunk.payload.result;
264
301
  parts[toolPartIndex] = {
265
302
  type: "dynamic-tool",
266
- toolName: toolPart.toolName,
267
- toolCallId: toolPart.toolCallId,
303
+ toolName,
304
+ toolCallId,
268
305
  state: "output-error",
269
306
  input: toolPart.input,
270
- errorText: String(chunk.payload.result),
307
+ errorText: String(error),
271
308
  callProviderMetadata: chunk.payload.providerMetadata
272
309
  };
273
310
  } else {
274
311
  const isWorkflow = Boolean(chunk.payload.result?.result?.steps);
312
+ const isAgent = chunk?.from === "AGENT";
313
+ let output;
314
+ if (isWorkflow) {
315
+ output = chunk.payload.result?.result;
316
+ } else if (isAgent) {
317
+ output = parts[toolPartIndex].output ?? chunk.payload.result;
318
+ } else {
319
+ output = chunk.payload.result;
320
+ }
275
321
  parts[toolPartIndex] = {
276
322
  type: "dynamic-tool",
277
- toolName: toolPart.toolName,
278
- toolCallId: toolPart.toolCallId,
323
+ toolName,
324
+ toolCallId,
279
325
  state: "output-available",
280
326
  input: toolPart.input,
281
- output: isWorkflow ? chunk.payload.result?.result : chunk.payload.result,
327
+ output,
282
328
  callProviderMetadata: chunk.payload.providerMetadata
283
329
  };
284
330
  }
@@ -297,11 +343,14 @@ const toUIMessage = ({ chunk, conversation, metadata }) => {
297
343
  if (!lastMessage || lastMessage.role !== "assistant") return result;
298
344
  const parts = [...lastMessage.parts];
299
345
  const toolPartIndex = parts.findIndex(
300
- (part) => part.type === "dynamic-tool" && "toolCallId" in part && part.toolCallId === chunk.payload.toolCallId
346
+ (part) => (part.type === "dynamic-tool" || typeof part.type === "string" && part.type.startsWith("tool-")) && "toolCallId" in part && part.toolCallId === chunk.payload.toolCallId
301
347
  );
302
348
  if (toolPartIndex !== -1) {
303
349
  const toolPart = parts[toolPartIndex];
304
- if (toolPart.type === "dynamic-tool") {
350
+ if (toolPart.type === "dynamic-tool" || typeof toolPart.type === "string" && toolPart.type.startsWith("tool-")) {
351
+ const toolName = "toolName" in toolPart && typeof toolPart.toolName === "string" ? toolPart.toolName : typeof toolPart.type === "string" && toolPart.type.startsWith("tool-") ? toolPart.type.substring(5) : "";
352
+ const toolCallId = toolPart.toolCallId;
353
+ const input = toolPart.input;
305
354
  if (chunk.payload.output?.type?.startsWith("workflow-")) {
306
355
  const existingWorkflowState = toolPart.output || {};
307
356
  const updatedWorkflowState = mapWorkflowStreamChunkToWatchResult(
@@ -309,14 +358,24 @@ const toUIMessage = ({ chunk, conversation, metadata }) => {
309
358
  chunk.payload.output
310
359
  );
311
360
  parts[toolPartIndex] = {
312
- ...toolPart,
361
+ type: "dynamic-tool",
362
+ toolName,
363
+ toolCallId,
364
+ state: "input-streaming",
365
+ input,
313
366
  output: updatedWorkflowState
314
367
  };
368
+ } else if (chunk.payload.output?.from === "AGENT" || chunk.payload.output?.from === "USER" && chunk.payload.output?.payload?.output?.type?.startsWith("workflow-")) {
369
+ return toUIMessageFromAgent(chunk.payload.output, conversation);
315
370
  } else {
316
371
  const currentOutput = toolPart.output || [];
317
372
  const existingOutput = Array.isArray(currentOutput) ? currentOutput : [];
318
373
  parts[toolPartIndex] = {
319
- ...toolPart,
374
+ type: "dynamic-tool",
375
+ toolName,
376
+ toolCallId,
377
+ state: "input-streaming",
378
+ input,
320
379
  output: [...existingOutput, chunk.payload.output]
321
380
  };
322
381
  }
@@ -385,15 +444,61 @@ const toUIMessage = ({ chunk, conversation, metadata }) => {
385
444
  }
386
445
  ];
387
446
  }
447
+ case "tool-call-approval": {
448
+ const lastMessage = result[result.length - 1];
449
+ if (!lastMessage || lastMessage.role !== "assistant") return result;
450
+ const lastRequireApprovalMetadata = lastMessage.metadata?.mode === "stream" ? lastMessage.metadata?.requireApprovalMetadata : {};
451
+ return [
452
+ ...result.slice(0, -1),
453
+ {
454
+ ...lastMessage,
455
+ metadata: {
456
+ ...lastMessage.metadata,
457
+ mode: "stream",
458
+ requireApprovalMetadata: {
459
+ ...lastRequireApprovalMetadata,
460
+ [chunk.payload.toolName]: {
461
+ toolCallId: chunk.payload.toolCallId,
462
+ toolName: chunk.payload.toolName,
463
+ args: chunk.payload.args
464
+ }
465
+ }
466
+ }
467
+ }
468
+ ];
469
+ }
470
+ case "tool-call-suspended": {
471
+ const lastMessage = result[result.length - 1];
472
+ if (!lastMessage || lastMessage.role !== "assistant") return result;
473
+ const lastSuspendedTools = lastMessage.metadata?.mode === "stream" ? lastMessage.metadata?.suspendedTools : {};
474
+ return [
475
+ ...result.slice(0, -1),
476
+ {
477
+ ...lastMessage,
478
+ metadata: {
479
+ ...lastMessage.metadata,
480
+ mode: "stream",
481
+ suspendedTools: {
482
+ ...lastSuspendedTools,
483
+ [chunk.payload.toolName]: {
484
+ toolCallId: chunk.payload.toolCallId,
485
+ toolName: chunk.payload.toolName,
486
+ args: chunk.payload.args,
487
+ suspendPayload: chunk.payload.suspendPayload
488
+ }
489
+ }
490
+ }
491
+ }
492
+ ];
493
+ }
388
494
  case "finish": {
389
495
  const lastMessage = result[result.length - 1];
390
496
  if (!lastMessage || lastMessage.role !== "assistant") return result;
391
497
  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" };
498
+ if (typeof part === "object" && part !== null && "type" in part && "state" in part && part.state === "streaming") {
499
+ if (part.type === "text" || part.type === "reasoning") {
500
+ return { ...part, state: "done" };
501
+ }
397
502
  }
398
503
  return part;
399
504
  });
@@ -427,6 +532,105 @@ const toUIMessage = ({ chunk, conversation, metadata }) => {
427
532
  return result;
428
533
  }
429
534
  };
535
+ const toUIMessageFromAgent = (chunk, conversation, metadata) => {
536
+ const lastMessage = conversation[conversation.length - 1];
537
+ if (!lastMessage || lastMessage.role !== "assistant") return conversation;
538
+ const parts = [...lastMessage.parts];
539
+ if (chunk.type === "text-delta") {
540
+ const agentChunk = chunk.payload;
541
+ const toolPartIndex = parts.findIndex((part) => part.type === "dynamic-tool");
542
+ if (toolPartIndex === -1) return conversation;
543
+ const toolPart = parts[toolPartIndex];
544
+ const childMessages = toolPart?.output?.childMessages || [];
545
+ const lastChildMessage = childMessages[childMessages.length - 1];
546
+ const textMessage = { type: "text", content: (lastChildMessage?.content || "") + agentChunk.text };
547
+ const nextMessages = lastChildMessage?.type === "text" ? [...childMessages.slice(0, -1), textMessage] : [...childMessages, textMessage];
548
+ parts[toolPartIndex] = {
549
+ ...toolPart,
550
+ output: {
551
+ childMessages: nextMessages
552
+ }
553
+ };
554
+ } else if (chunk.type === "tool-call") {
555
+ const agentChunk = chunk.payload;
556
+ const toolPartIndex = parts.findIndex((part) => part.type === "dynamic-tool");
557
+ if (toolPartIndex === -1) return conversation;
558
+ const toolPart = parts[toolPartIndex];
559
+ const childMessages = toolPart?.output?.childMessages || [];
560
+ parts[toolPartIndex] = {
561
+ ...toolPart,
562
+ output: {
563
+ ...toolPart?.output,
564
+ childMessages: [
565
+ ...childMessages,
566
+ {
567
+ type: "tool",
568
+ toolCallId: agentChunk.toolCallId,
569
+ toolName: agentChunk.toolName,
570
+ args: agentChunk.args
571
+ }
572
+ ]
573
+ }
574
+ };
575
+ } else if (chunk.type === "tool-output") {
576
+ const agentChunk = chunk.payload;
577
+ const toolPartIndex = parts.findIndex((part) => part.type === "dynamic-tool");
578
+ if (toolPartIndex === -1) return conversation;
579
+ const toolPart = parts[toolPartIndex];
580
+ if (agentChunk?.output?.type?.startsWith("workflow-")) {
581
+ const childMessages = toolPart?.output?.childMessages || [];
582
+ const lastToolIndex = childMessages.length - 1;
583
+ const currentMessage = childMessages[lastToolIndex];
584
+ const actualExistingWorkflowState = currentMessage?.toolOutput || {};
585
+ const updatedWorkflowState = mapWorkflowStreamChunkToWatchResult(actualExistingWorkflowState, agentChunk.output);
586
+ if (lastToolIndex >= 0 && childMessages[lastToolIndex]?.type === "tool") {
587
+ parts[toolPartIndex] = {
588
+ ...toolPart,
589
+ output: {
590
+ ...toolPart?.output,
591
+ childMessages: [
592
+ ...childMessages.slice(0, -1),
593
+ {
594
+ ...currentMessage,
595
+ toolOutput: { ...updatedWorkflowState, runId: agentChunk.output.runId }
596
+ }
597
+ ]
598
+ }
599
+ };
600
+ }
601
+ }
602
+ } else if (chunk.type === "tool-result") {
603
+ const agentChunk = chunk.payload;
604
+ const toolPartIndex = parts.findIndex((part) => part.type === "dynamic-tool");
605
+ if (toolPartIndex === -1) return conversation;
606
+ const toolPart = parts[toolPartIndex];
607
+ const childMessages = toolPart?.output?.childMessages || [];
608
+ const lastToolIndex = childMessages.length - 1;
609
+ const isWorkflow = agentChunk?.toolName?.startsWith("workflow-");
610
+ if (lastToolIndex >= 0 && childMessages[lastToolIndex]?.type === "tool") {
611
+ parts[toolPartIndex] = {
612
+ ...toolPart,
613
+ output: {
614
+ ...toolPart?.output,
615
+ childMessages: [
616
+ ...childMessages.slice(0, -1),
617
+ {
618
+ ...childMessages[lastToolIndex],
619
+ toolOutput: isWorkflow ? { ...agentChunk.result?.result, runId: agentChunk.result?.runId } : agentChunk.result
620
+ }
621
+ ]
622
+ }
623
+ };
624
+ }
625
+ }
626
+ return [
627
+ ...conversation.slice(0, -1),
628
+ {
629
+ ...lastMessage,
630
+ parts
631
+ }
632
+ ];
633
+ };
430
634
 
431
635
  const toAssistantUIMessage = (message) => {
432
636
  const extendedMessage = message;
@@ -466,13 +670,23 @@ const toAssistantUIMessage = (message) => {
466
670
  };
467
671
  }
468
672
  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
- };
673
+ const type = part.mediaType.includes("image/") ? "image" : "file";
674
+ if (type === "file") {
675
+ return {
676
+ type,
677
+ mimeType: part.mediaType,
678
+ data: part.url,
679
+ // Use URL as data source
680
+ metadata: message.metadata
681
+ };
682
+ }
683
+ if (type === "image") {
684
+ return {
685
+ type,
686
+ image: part.url,
687
+ metadata: message.metadata
688
+ };
689
+ }
476
690
  }
477
691
  if (part.type === "dynamic-tool") {
478
692
  const baseToolCall = {
@@ -492,13 +706,14 @@ const toAssistantUIMessage = (message) => {
492
706
  return baseToolCall;
493
707
  }
494
708
  if (part.type.startsWith("tool-") && part.state !== "input-available") {
495
- const toolName = "toolName" in part && typeof part.toolName === "string" ? part.toolName : part.type.substring(5);
709
+ const toolName2 = "toolName" in part && typeof part.toolName === "string" ? part.toolName : part.type.substring(5);
710
+ const { suspendedToolRunId, ...cleanInput } = "input" in part ? part.input : {};
496
711
  const baseToolCall = {
497
712
  type: "tool-call",
498
713
  toolCallId: "toolCallId" in part && typeof part.toolCallId === "string" ? part.toolCallId : "",
499
- toolName,
500
- argsText: "input" in part ? JSON.stringify(part.input) : "{}",
501
- args: "input" in part ? part.input : {},
714
+ toolName: toolName2,
715
+ argsText: JSON.stringify(cleanInput ?? {}),
716
+ args: cleanInput ?? {},
502
717
  metadata: message.metadata
503
718
  };
504
719
  if ("output" in part) {
@@ -508,6 +723,31 @@ const toAssistantUIMessage = (message) => {
508
723
  }
509
724
  return baseToolCall;
510
725
  }
726
+ const toolName = "toolName" in part && typeof part.toolName === "string" ? part.toolName : part.type.startsWith("tool-") ? part.type.substring(5) : "";
727
+ const requireApprovalMetadata = extendedMessage.metadata?.requireApprovalMetadata;
728
+ const suspendedTools = extendedMessage.metadata?.suspendedTools;
729
+ const partToolCallId = "toolCallId" in part && typeof part.toolCallId === "string" ? part.toolCallId : void 0;
730
+ const suspensionData = toolName ? requireApprovalMetadata?.[toolName] ?? suspendedTools?.[toolName] : void 0;
731
+ if (suspensionData) {
732
+ const { suspendedToolRunId, ...cleanInput } = "input" in part ? part.input : {};
733
+ return {
734
+ type: "tool-call",
735
+ toolCallId: partToolCallId,
736
+ toolName,
737
+ argsText: JSON.stringify(cleanInput ?? {}),
738
+ args: cleanInput,
739
+ metadata: extendedMessage.metadata
740
+ };
741
+ }
742
+ if (part.type.startsWith("data-")) {
743
+ return {
744
+ type: "data",
745
+ name: part.type.substring(5),
746
+ // Extract name from 'data-{name}'
747
+ data: part.data,
748
+ metadata: message.metadata
749
+ };
750
+ }
511
751
  return {
512
752
  type: "text",
513
753
  text: "",
@@ -547,9 +787,146 @@ const toAssistantUIMessage = (message) => {
547
787
  return threadMessage;
548
788
  };
549
789
 
790
+ const resolveInitialMessages = (messages) => {
791
+ return messages.map((message) => {
792
+ const networkPart = message.parts.find(
793
+ (part) => typeof part === "object" && part !== null && "type" in part && part.type === "text" && "text" in part && typeof part.text === "string" && part.text.includes('"isNetwork":true')
794
+ );
795
+ if (networkPart && networkPart.type === "text") {
796
+ try {
797
+ const json = JSON.parse(networkPart.text);
798
+ if (json.isNetwork === true) {
799
+ const selectionReason = json.selectionReason || "";
800
+ const primitiveType = json.primitiveType || "";
801
+ const primitiveId = json.primitiveId || "";
802
+ const finalResult = json.finalResult;
803
+ const messages2 = finalResult?.messages || [];
804
+ const childMessages = [];
805
+ const toolResultMap = /* @__PURE__ */ new Map();
806
+ for (const msg of messages2) {
807
+ if (Array.isArray(msg.content)) {
808
+ for (const part of msg.content) {
809
+ if (typeof part === "object" && part.type === "tool-result") {
810
+ toolResultMap.set(part.toolCallId, part);
811
+ }
812
+ }
813
+ }
814
+ }
815
+ for (const msg of messages2) {
816
+ if (msg.type === "tool-call" && Array.isArray(msg.content)) {
817
+ for (const part of msg.content) {
818
+ if (typeof part === "object" && part.type === "tool-call") {
819
+ const toolCallContent = part;
820
+ const toolResult = toolResultMap.get(toolCallContent.toolCallId);
821
+ const isWorkflow = Boolean(toolResult?.result?.result?.steps);
822
+ childMessages.push({
823
+ type: "tool",
824
+ toolCallId: toolCallContent.toolCallId,
825
+ toolName: toolCallContent.toolName,
826
+ args: toolCallContent.args,
827
+ toolOutput: isWorkflow ? toolResult?.result?.result : toolResult?.result
828
+ });
829
+ }
830
+ }
831
+ }
832
+ }
833
+ if (finalResult && finalResult.text) {
834
+ childMessages.push({
835
+ type: "text",
836
+ content: finalResult.text
837
+ });
838
+ }
839
+ const result = {
840
+ childMessages,
841
+ result: finalResult?.text || ""
842
+ };
843
+ const nextMessage = {
844
+ role: "assistant",
845
+ parts: [
846
+ {
847
+ type: "dynamic-tool",
848
+ toolCallId: primitiveId,
849
+ toolName: primitiveId,
850
+ state: "output-available",
851
+ input: json.input,
852
+ output: result
853
+ }
854
+ ],
855
+ id: message.id,
856
+ metadata: {
857
+ ...message.metadata,
858
+ mode: "network",
859
+ selectionReason,
860
+ agentInput: json.input,
861
+ from: primitiveType === "agent" ? "AGENT" : "WORKFLOW"
862
+ }
863
+ };
864
+ return nextMessage;
865
+ }
866
+ } catch (error) {
867
+ return message;
868
+ }
869
+ }
870
+ const extendedMessage = message;
871
+ const pendingToolApprovals = extendedMessage.metadata?.pendingToolApprovals;
872
+ if (pendingToolApprovals && typeof pendingToolApprovals === "object") {
873
+ return {
874
+ ...message,
875
+ metadata: {
876
+ ...message.metadata,
877
+ mode: "stream",
878
+ requireApprovalMetadata: pendingToolApprovals
879
+ }
880
+ };
881
+ }
882
+ const suspendedTools = extendedMessage.metadata?.suspendedTools;
883
+ if (suspendedTools && typeof suspendedTools === "object") {
884
+ return {
885
+ ...message,
886
+ metadata: {
887
+ ...message.metadata,
888
+ mode: "stream",
889
+ suspendedTools
890
+ }
891
+ };
892
+ }
893
+ return message;
894
+ });
895
+ };
896
+ const resolveToChildMessages = (messages) => {
897
+ const assistantMessage = messages.find((message) => message.role === "assistant");
898
+ if (!assistantMessage) return [];
899
+ const parts = assistantMessage.parts;
900
+ let childMessages = [];
901
+ for (const part of parts) {
902
+ const toolPart = part;
903
+ if (part.type.startsWith("tool-")) {
904
+ const toolName = part.type.substring("tool-".length);
905
+ const isWorkflow = toolName.startsWith("workflow-");
906
+ childMessages.push({
907
+ type: "tool",
908
+ toolCallId: toolPart.toolCallId,
909
+ toolName,
910
+ args: toolPart.input,
911
+ toolOutput: isWorkflow ? { ...toolPart.output?.result, runId: toolPart.output?.runId } : toolPart.output
912
+ });
913
+ }
914
+ if (part.type === "text") {
915
+ childMessages.push({
916
+ type: "text",
917
+ content: toolPart.text
918
+ });
919
+ }
920
+ }
921
+ return childMessages;
922
+ };
923
+
550
924
  class AISdkNetworkTransformer {
551
925
  transform({ chunk, conversation, metadata }) {
552
926
  const newConversation = [...conversation];
927
+ if (chunk.type === "routing-agent-text-delta") {
928
+ return this.handleRoutingAgentConversation(chunk, newConversation);
929
+ }
553
930
  if (chunk.type.startsWith("agent-execution-")) {
554
931
  return this.handleAgentConversation(chunk, newConversation, metadata);
555
932
  }
@@ -560,22 +937,80 @@ class AISdkNetworkTransformer {
560
937
  return this.handleToolConversation(chunk, newConversation, metadata);
561
938
  }
562
939
  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: [
940
+ const lastMessage = newConversation[newConversation.length - 1];
941
+ if (!lastMessage || lastMessage.role !== "assistant") return newConversation;
942
+ const agentChunk = chunk.payload;
943
+ const parts = [...lastMessage.parts];
944
+ const textPartIndex = parts.findIndex((part) => part.type === "text");
945
+ if (textPartIndex === -1) {
946
+ parts.push({
947
+ type: "text",
948
+ text: agentChunk.result,
949
+ state: "done"
950
+ });
951
+ return [
952
+ ...newConversation.slice(0, -1),
567
953
  {
568
- type: "text",
569
- text: chunk.payload?.result || "",
570
- state: "done"
954
+ ...lastMessage,
955
+ parts
571
956
  }
572
- ],
573
- metadata
574
- };
575
- return [...newConversation, newMessage];
957
+ ];
958
+ }
959
+ const textPart = parts[textPartIndex];
960
+ if (textPart.type === "text") {
961
+ parts[textPartIndex] = {
962
+ ...textPart,
963
+ state: "done"
964
+ };
965
+ return [
966
+ ...newConversation.slice(0, -1),
967
+ {
968
+ ...lastMessage,
969
+ parts
970
+ }
971
+ ];
972
+ }
973
+ return newConversation;
576
974
  }
577
975
  return newConversation;
578
976
  }
977
+ handleRoutingAgentConversation = (chunk, newConversation) => {
978
+ const lastMessage = newConversation[newConversation.length - 1];
979
+ if (!lastMessage || lastMessage.role !== "assistant") return newConversation;
980
+ const agentChunk = chunk.payload;
981
+ const parts = [...lastMessage.parts];
982
+ const textPartIndex = parts.findIndex((part) => part.type === "text");
983
+ if (textPartIndex === -1) {
984
+ parts.push({
985
+ type: "text",
986
+ text: agentChunk.text,
987
+ state: "streaming"
988
+ });
989
+ return [
990
+ ...newConversation.slice(0, -1),
991
+ {
992
+ ...lastMessage,
993
+ parts
994
+ }
995
+ ];
996
+ }
997
+ const textPart = parts[textPartIndex];
998
+ if (textPart.type === "text") {
999
+ parts[textPartIndex] = {
1000
+ ...textPart,
1001
+ text: textPart.text + agentChunk.text,
1002
+ state: "streaming"
1003
+ };
1004
+ return [
1005
+ ...newConversation.slice(0, -1),
1006
+ {
1007
+ ...lastMessage,
1008
+ parts
1009
+ }
1010
+ ];
1011
+ }
1012
+ return newConversation;
1013
+ };
579
1014
  handleAgentConversation = (chunk, newConversation, metadata) => {
580
1015
  if (chunk.type === "agent-execution-start") {
581
1016
  const primitiveId = chunk.payload?.args?.primitiveId;
@@ -858,96 +1293,80 @@ class AISdkNetworkTransformer {
858
1293
  };
859
1294
  }
860
1295
 
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;
1296
+ const fromCoreUserMessageToUIMessage = (coreUserMessage) => {
1297
+ const id = `user-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
1298
+ const parts = typeof coreUserMessage.content === "string" ? [
1299
+ {
1300
+ type: "text",
1301
+ text: coreUserMessage.content
1302
+ }
1303
+ ] : coreUserMessage.content.map((part) => {
1304
+ switch (part.type) {
1305
+ case "text": {
1306
+ return {
1307
+ type: "text",
1308
+ text: part.text
1309
+ };
1310
+ }
1311
+ case "image": {
1312
+ const url = typeof part.image === "string" ? part.image : part.image instanceof URL ? part.image.toString() : "";
1313
+ return {
1314
+ type: "file",
1315
+ mediaType: part.mimeType ?? "image/*",
1316
+ url
1317
+ };
1318
+ }
1319
+ case "file": {
1320
+ const url = typeof part.data === "string" ? part.data : part.data instanceof URL ? part.data.toString() : "";
1321
+ return {
1322
+ type: "file",
1323
+ mediaType: part.mimeType,
1324
+ url,
1325
+ ...part.filename !== void 0 ? { filename: part.filename } : {}
1326
+ };
1327
+ }
1328
+ default: {
1329
+ const exhaustiveCheck = part;
1330
+ throw new Error(`Unhandled content part type: ${exhaustiveCheck.type}`);
932
1331
  }
933
1332
  }
934
- return message;
935
1333
  });
1334
+ return {
1335
+ id,
1336
+ role: "user",
1337
+ parts
1338
+ };
936
1339
  };
937
1340
 
938
- const useChat = ({ agentId, initializeMessages }) => {
939
- const [messages, setMessages] = useState(
940
- () => resolveInitialMessages(initializeMessages?.() || [])
941
- );
1341
+ const useChat = ({ agentId, resourceId, initializeMessages }) => {
1342
+ const extractRunIdFromMessages = (messages2) => {
1343
+ for (const message of messages2) {
1344
+ const pendingToolApprovals = message.metadata?.pendingToolApprovals;
1345
+ if (pendingToolApprovals && typeof pendingToolApprovals === "object") {
1346
+ const suspensionData = Object.values(pendingToolApprovals)[0];
1347
+ if (suspensionData?.runId) {
1348
+ return suspensionData.runId;
1349
+ }
1350
+ }
1351
+ }
1352
+ return void 0;
1353
+ };
1354
+ const initialMessages = initializeMessages?.() || [];
1355
+ const initialRunId = extractRunIdFromMessages(initialMessages);
1356
+ const _currentRunId = useRef(initialRunId);
1357
+ const _onChunk = useRef(void 0);
1358
+ const [messages, setMessages] = useState(() => resolveInitialMessages(initialMessages));
1359
+ const [toolCallApprovals, setToolCallApprovals] = useState({});
942
1360
  const baseClient = useMastraClient();
943
1361
  const [isRunning, setIsRunning] = useState(false);
944
1362
  const generate = async ({
945
1363
  coreUserMessages,
946
- runtimeContext,
1364
+ requestContext,
947
1365
  threadId,
948
1366
  modelSettings,
949
1367
  signal,
950
- onFinish
1368
+ onFinish,
1369
+ tracingOptions
951
1370
  }) => {
952
1371
  const {
953
1372
  frequencyPenalty,
@@ -969,7 +1388,7 @@ const useChat = ({ agentId, initializeMessages }) => {
969
1388
  const agent = clientWithAbort.getAgent(agentId);
970
1389
  const response = await agent.generate({
971
1390
  messages: coreUserMessages,
972
- runId: agentId,
1391
+ runId: v4(),
973
1392
  maxSteps,
974
1393
  modelSettings: {
975
1394
  frequencyPenalty,
@@ -981,9 +1400,10 @@ const useChat = ({ agentId, initializeMessages }) => {
981
1400
  topP
982
1401
  },
983
1402
  instructions,
984
- runtimeContext,
985
- ...threadId ? { threadId, resourceId: agentId } : {},
986
- providerOptions
1403
+ requestContext,
1404
+ ...threadId ? { threadId, resourceId: resourceId || agentId } : {},
1405
+ providerOptions,
1406
+ tracingOptions
987
1407
  });
988
1408
  setIsRunning(false);
989
1409
  if (response && "uiMessages" in response.response && response.response.uiMessages) {
@@ -997,7 +1417,15 @@ const useChat = ({ agentId, initializeMessages }) => {
997
1417
  setMessages((prev) => [...prev, ...mastraUIMessages]);
998
1418
  }
999
1419
  };
1000
- const stream = async ({ coreUserMessages, runtimeContext, threadId, onChunk, modelSettings, signal }) => {
1420
+ const stream = async ({
1421
+ coreUserMessages,
1422
+ requestContext,
1423
+ threadId,
1424
+ onChunk,
1425
+ modelSettings,
1426
+ signal,
1427
+ tracingOptions
1428
+ }) => {
1001
1429
  const {
1002
1430
  frequencyPenalty,
1003
1431
  presencePenalty,
@@ -1008,7 +1436,8 @@ const useChat = ({ agentId, initializeMessages }) => {
1008
1436
  topP,
1009
1437
  instructions,
1010
1438
  providerOptions,
1011
- maxSteps
1439
+ maxSteps,
1440
+ requireToolApproval
1012
1441
  } = modelSettings || {};
1013
1442
  setIsRunning(true);
1014
1443
  const clientWithAbort = new MastraClient({
@@ -1016,9 +1445,10 @@ const useChat = ({ agentId, initializeMessages }) => {
1016
1445
  abortSignal: signal
1017
1446
  });
1018
1447
  const agent = clientWithAbort.getAgent(agentId);
1448
+ const runId = v4();
1019
1449
  const response = await agent.stream({
1020
1450
  messages: coreUserMessages,
1021
- runId: agentId,
1451
+ runId,
1022
1452
  maxSteps,
1023
1453
  modelSettings: {
1024
1454
  frequencyPenalty,
@@ -1030,19 +1460,17 @@ const useChat = ({ agentId, initializeMessages }) => {
1030
1460
  topP
1031
1461
  },
1032
1462
  instructions,
1033
- runtimeContext,
1034
- ...threadId ? { threadId, resourceId: agentId } : {},
1035
- providerOptions
1463
+ requestContext,
1464
+ ...threadId ? { threadId, resourceId: resourceId || agentId } : {},
1465
+ providerOptions,
1466
+ requireToolApproval,
1467
+ tracingOptions
1036
1468
  });
1037
- if (!response.body) {
1038
- setIsRunning(false);
1039
- throw new Error("[Stream] No response body");
1040
- }
1469
+ _onChunk.current = onChunk;
1470
+ _currentRunId.current = runId;
1041
1471
  await response.processDataStream({
1042
1472
  onChunk: async (chunk) => {
1043
- flushSync(() => {
1044
- setMessages((prev) => toUIMessage({ chunk, conversation: prev, metadata: { mode: "stream" } }));
1045
- });
1473
+ setMessages((prev) => toUIMessage({ chunk, conversation: prev, metadata: { mode: "stream" } }));
1046
1474
  onChunk?.(chunk);
1047
1475
  }
1048
1476
  });
@@ -1050,11 +1478,12 @@ const useChat = ({ agentId, initializeMessages }) => {
1050
1478
  };
1051
1479
  const network = async ({
1052
1480
  coreUserMessages,
1053
- runtimeContext,
1481
+ requestContext,
1054
1482
  threadId,
1055
1483
  onNetworkChunk,
1056
1484
  modelSettings,
1057
- signal
1485
+ signal,
1486
+ tracingOptions
1058
1487
  }) => {
1059
1488
  const { frequencyPenalty, presencePenalty, maxRetries, maxTokens, temperature, topK, topP, maxSteps } = modelSettings || {};
1060
1489
  setIsRunning(true);
@@ -1063,6 +1492,7 @@ const useChat = ({ agentId, initializeMessages }) => {
1063
1492
  abortSignal: signal
1064
1493
  });
1065
1494
  const agent = clientWithAbort.getAgent(agentId);
1495
+ const runId = v4();
1066
1496
  const response = await agent.network({
1067
1497
  messages: coreUserMessages,
1068
1498
  maxSteps,
@@ -1075,31 +1505,73 @@ const useChat = ({ agentId, initializeMessages }) => {
1075
1505
  topK,
1076
1506
  topP
1077
1507
  },
1078
- runId: agentId,
1079
- runtimeContext,
1080
- ...threadId ? { thread: threadId, resourceId: agentId } : {}
1508
+ runId,
1509
+ requestContext,
1510
+ ...threadId ? { thread: threadId, resourceId: resourceId || agentId } : {},
1511
+ tracingOptions
1081
1512
  });
1082
1513
  const transformer = new AISdkNetworkTransformer();
1083
1514
  await response.processDataStream({
1084
1515
  onChunk: async (chunk) => {
1085
- flushSync(() => {
1086
- setMessages((prev) => transformer.transform({ chunk, conversation: prev, metadata: { mode: "network" } }));
1087
- });
1516
+ setMessages((prev) => transformer.transform({ chunk, conversation: prev, metadata: { mode: "network" } }));
1088
1517
  onNetworkChunk?.(chunk);
1089
1518
  }
1090
1519
  });
1091
1520
  setIsRunning(false);
1092
1521
  };
1522
+ const handleCancelRun = () => {
1523
+ setIsRunning(false);
1524
+ _currentRunId.current = void 0;
1525
+ _onChunk.current = void 0;
1526
+ };
1527
+ const approveToolCall = async (toolCallId) => {
1528
+ const onChunk = _onChunk.current;
1529
+ const currentRunId = _currentRunId.current;
1530
+ if (!currentRunId)
1531
+ return console.info("[approveToolCall] approveToolCall can only be called after a stream has started");
1532
+ setIsRunning(true);
1533
+ setToolCallApprovals((prev) => ({ ...prev, [toolCallId]: { status: "approved" } }));
1534
+ const agent = baseClient.getAgent(agentId);
1535
+ const response = await agent.approveToolCall({ runId: currentRunId, toolCallId });
1536
+ await response.processDataStream({
1537
+ onChunk: async (chunk) => {
1538
+ setMessages((prev) => toUIMessage({ chunk, conversation: prev, metadata: { mode: "stream" } }));
1539
+ onChunk?.(chunk);
1540
+ }
1541
+ });
1542
+ setIsRunning(false);
1543
+ };
1544
+ const declineToolCall = async (toolCallId) => {
1545
+ const onChunk = _onChunk.current;
1546
+ const currentRunId = _currentRunId.current;
1547
+ if (!currentRunId)
1548
+ return console.info("[declineToolCall] declineToolCall can only be called after a stream has started");
1549
+ setIsRunning(true);
1550
+ setToolCallApprovals((prev) => ({ ...prev, [toolCallId]: { status: "declined" } }));
1551
+ const agent = baseClient.getAgent(agentId);
1552
+ const response = await agent.declineToolCall({ runId: currentRunId, toolCallId });
1553
+ await response.processDataStream({
1554
+ onChunk: async (chunk) => {
1555
+ setMessages((prev) => toUIMessage({ chunk, conversation: prev, metadata: { mode: "stream" } }));
1556
+ onChunk?.(chunk);
1557
+ }
1558
+ });
1559
+ setIsRunning(false);
1560
+ };
1093
1561
  const sendMessage = async ({ mode = "stream", ...args }) => {
1094
1562
  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 }] }]);
1563
+ const coreUserMessages = [nextMessage];
1564
+ if (args.coreUserMessages) {
1565
+ coreUserMessages.push(...args.coreUserMessages);
1566
+ }
1567
+ const uiMessages = coreUserMessages.map(fromCoreUserMessageToUIMessage);
1568
+ setMessages((s) => [...s, ...uiMessages]);
1097
1569
  if (mode === "generate") {
1098
- await generate({ ...args, coreUserMessages: messages2 });
1570
+ await generate({ ...args, coreUserMessages });
1099
1571
  } else if (mode === "stream") {
1100
- await stream({ ...args, coreUserMessages: messages2 });
1572
+ await stream({ ...args, coreUserMessages });
1101
1573
  } else if (mode === "network") {
1102
- await network({ ...args, coreUserMessages: messages2 });
1574
+ await network({ ...args, coreUserMessages });
1103
1575
  }
1104
1576
  };
1105
1577
  return {
@@ -1107,7 +1579,10 @@ const useChat = ({ agentId, initializeMessages }) => {
1107
1579
  sendMessage,
1108
1580
  isRunning,
1109
1581
  messages,
1110
- cancelRun: () => setIsRunning(false)
1582
+ approveToolCall,
1583
+ declineToolCall,
1584
+ cancelRun: handleCancelRun,
1585
+ toolCallApprovals
1111
1586
  };
1112
1587
  };
1113
1588
 
@@ -1426,5 +1901,5 @@ const MessageStreaming = ({ className, ...props }) => {
1426
1901
  return /* @__PURE__ */ jsx("span", { className: className || MessageStreamingClass, ...props });
1427
1902
  };
1428
1903
 
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 };
1904
+ 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
1905
  //# sourceMappingURL=index.js.map