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