@mastra/react 0.2.30-alpha.1 → 0.2.30-alpha.3

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
@@ -310,22 +310,6 @@ var toUIMessage = ({ chunk, conversation, metadata }) => {
310
310
  }
311
311
  ];
312
312
  }
313
- case "background-task-failed":
314
- case "background-task-completed": {
315
- const lastMessage = result[result.length - 1];
316
- if (!lastMessage || lastMessage.role !== "assistant") return result;
317
- return [
318
- ...result.slice(0, -1),
319
- {
320
- ...lastMessage,
321
- metadata: {
322
- mode: metadata.mode,
323
- ...lastMessage.metadata,
324
- runningBackgroundTasksCount: void 0
325
- }
326
- }
327
- ];
328
- }
329
313
  case "text-delta": {
330
314
  const lastMessage = result[result.length - 1];
331
315
  if (!lastMessage || lastMessage.role !== "assistant") return result;
@@ -449,113 +433,140 @@ var toUIMessage = ({ chunk, conversation, metadata }) => {
449
433
  ];
450
434
  }
451
435
  case "tool-error":
452
- case "tool-result": {
453
- const lastMessage = result[result.length - 1];
454
- if (!lastMessage || lastMessage.role !== "assistant") return result;
455
- const parts = [...lastMessage.parts];
456
- const toolPartIndex = parts.findIndex(
457
- (part) => (part.type === "dynamic-tool" || typeof part.type === "string" && part.type.startsWith("tool-")) && "toolCallId" in part && part.toolCallId === chunk.payload.toolCallId
458
- );
459
- if (toolPartIndex !== -1) {
460
- const toolPart = parts[toolPartIndex];
461
- if (toolPart.type === "dynamic-tool" || typeof toolPart.type === "string" && toolPart.type.startsWith("tool-")) {
462
- const toolName = "toolName" in toolPart && typeof toolPart.toolName === "string" ? toolPart.toolName : toolPart.type.startsWith("tool-") ? toolPart.type.substring(5) : "";
463
- const toolCallId = toolPart.toolCallId;
464
- if (chunk.type === "tool-result" && chunk.payload.isError || chunk.type === "tool-error") {
465
- const error = chunk.type === "tool-error" ? chunk.payload.error : chunk.payload.result;
466
- parts[toolPartIndex] = {
467
- type: "dynamic-tool",
468
- toolName,
469
- toolCallId,
470
- state: "output-error",
471
- input: toolPart.input,
472
- errorText: typeof error === "string" ? error : error instanceof Error ? error.message : error?.message ?? String(error),
473
- callProviderMetadata: chunk.payload.providerMetadata
474
- };
436
+ case "tool-result":
437
+ case "background-task-completed":
438
+ case "background-task-failed": {
439
+ const isBgTaskEvent = chunk.type === "background-task-completed" || chunk.type === "background-task-failed";
440
+ const location = locateToolPart(result, chunk.payload.toolCallId, isBgTaskEvent);
441
+ if (!location) return result;
442
+ const { messageIndex, toolPartIndex } = location;
443
+ const targetMessage = result[messageIndex];
444
+ if (!targetMessage || targetMessage.role !== "assistant") return result;
445
+ const parts = [...targetMessage.parts];
446
+ const toolPart = toolPartIndex >= 0 ? parts[toolPartIndex] : void 0;
447
+ if (toolPart && (toolPart.type === "dynamic-tool" || typeof toolPart.type === "string" && toolPart.type.startsWith("tool-"))) {
448
+ const toolName = "toolName" in toolPart && typeof toolPart.toolName === "string" ? toolPart.toolName : typeof toolPart.type === "string" && toolPart.type.startsWith("tool-") ? toolPart.type.substring(5) : "";
449
+ const toolCallId = toolPart.toolCallId;
450
+ if ((chunk.type === "tool-result" || chunk.type === "background-task-completed") && chunk.payload.isError || chunk.type === "tool-error" || chunk.type === "background-task-failed") {
451
+ const error = chunk.type === "tool-error" || chunk.type === "background-task-failed" ? chunk.payload.error : chunk.payload.result;
452
+ parts[toolPartIndex] = {
453
+ type: "dynamic-tool",
454
+ toolName,
455
+ toolCallId,
456
+ state: "output-error",
457
+ input: toolPart.input,
458
+ errorText: typeof error === "string" ? error : error instanceof Error ? error.message : error?.message ?? String(error),
459
+ callProviderMetadata: chunk.payload.providerMetadata
460
+ };
461
+ } else {
462
+ const isWorkflow = Boolean(chunk.payload.result?.result?.steps);
463
+ const isAgent = chunk?.from === "AGENT";
464
+ let output;
465
+ if (isWorkflow) {
466
+ output = chunk.payload.result?.result;
467
+ } else if (isAgent) {
468
+ const existingOutput = parts[toolPartIndex].output;
469
+ output = existingOutput ? {
470
+ ...chunk.payload.result,
471
+ childMessages: existingOutput.childMessages?.length ? existingOutput.childMessages : chunk.payload.result?.childMessages
472
+ } : chunk.payload.result;
475
473
  } else {
476
- const isWorkflow = Boolean(chunk.payload.result?.result?.steps);
477
- const isAgent = chunk?.from === "AGENT";
478
- let output;
479
- if (isWorkflow) {
480
- output = chunk.payload.result?.result;
481
- } else if (isAgent) {
482
- const existingOutput = parts[toolPartIndex].output;
483
- output = existingOutput ? {
484
- ...chunk.payload.result,
485
- childMessages: existingOutput.childMessages?.length ? existingOutput.childMessages : chunk.payload.result?.childMessages
486
- } : chunk.payload.result;
487
- } else {
488
- output = chunk.payload.result;
489
- }
490
- parts[toolPartIndex] = {
491
- type: "dynamic-tool",
492
- toolName,
493
- toolCallId,
494
- state: "output-available",
495
- input: toolPart.input,
496
- output,
497
- callProviderMetadata: chunk.payload.providerMetadata
498
- };
474
+ output = chunk.payload.result;
499
475
  }
476
+ parts[toolPartIndex] = {
477
+ type: "dynamic-tool",
478
+ toolName,
479
+ toolCallId,
480
+ state: "output-available",
481
+ input: toolPart.input,
482
+ output,
483
+ callProviderMetadata: chunk.payload.providerMetadata
484
+ };
500
485
  }
501
486
  }
487
+ const nextMessage = {
488
+ ...targetMessage,
489
+ parts,
490
+ metadata: mergeBgTaskMetadata(targetMessage.metadata, metadata.mode, {
491
+ resetRunningCount: isBgTaskEvent,
492
+ perTaskEntry: isBgTaskEvent ? {
493
+ toolCallId: chunk.payload.toolCallId,
494
+ completedAt: chunk.payload.completedAt,
495
+ taskId: chunk.payload.taskId
496
+ } : void 0
497
+ })
498
+ };
499
+ return [...result.slice(0, messageIndex), nextMessage, ...result.slice(messageIndex + 1)];
500
+ }
501
+ case "background-task-running": {
502
+ const location = locateToolPart(result, chunk.payload.toolCallId, true);
503
+ if (!location) return result;
504
+ const { messageIndex } = location;
505
+ const targetMessage = result[messageIndex];
506
+ if (!targetMessage || targetMessage.role !== "assistant") return result;
502
507
  return [
503
- ...result.slice(0, -1),
508
+ ...result.slice(0, messageIndex),
504
509
  {
505
- ...lastMessage,
506
- parts
507
- }
510
+ ...targetMessage,
511
+ metadata: mergeBgTaskMetadata(targetMessage.metadata, metadata.mode, {
512
+ perTaskEntry: {
513
+ toolCallId: chunk.payload.toolCallId,
514
+ startedAt: chunk.payload.startedAt,
515
+ taskId: chunk.payload.taskId
516
+ }
517
+ })
518
+ },
519
+ ...result.slice(messageIndex + 1)
508
520
  ];
509
521
  }
510
- case "tool-output": {
511
- const lastMessage = result[result.length - 1];
512
- if (!lastMessage || lastMessage.role !== "assistant") return result;
513
- const parts = [...lastMessage.parts];
514
- const toolPartIndex = parts.findIndex(
515
- (part) => (part.type === "dynamic-tool" || typeof part.type === "string" && part.type.startsWith("tool-")) && "toolCallId" in part && part.toolCallId === chunk.payload.toolCallId
516
- );
517
- if (toolPartIndex !== -1) {
518
- const toolPart = parts[toolPartIndex];
519
- if (toolPart.type === "dynamic-tool" || typeof toolPart.type === "string" && toolPart.type.startsWith("tool-")) {
520
- const toolName = "toolName" in toolPart && typeof toolPart.toolName === "string" ? toolPart.toolName : typeof toolPart.type === "string" && toolPart.type.startsWith("tool-") ? toolPart.type.substring(5) : "";
521
- const toolCallId = toolPart.toolCallId;
522
- const input = toolPart.input;
523
- if (chunk.payload.output?.type?.startsWith("workflow-")) {
524
- const existingWorkflowState = toolPart.output || {};
525
- const updatedWorkflowState = mapWorkflowStreamChunkToWatchResult(
526
- existingWorkflowState,
527
- chunk.payload.output
528
- );
529
- parts[toolPartIndex] = {
530
- type: "dynamic-tool",
531
- toolName,
532
- toolCallId,
533
- state: "input-streaming",
534
- input,
535
- output: updatedWorkflowState
536
- };
537
- } else if (chunk.payload.output?.from === "AGENT" || chunk.payload.output?.from === "USER" && chunk.payload.output?.payload?.output?.type?.startsWith("workflow-")) {
538
- return toUIMessageFromAgent(chunk.payload.output, conversation, metadata, toolCallId, toolName);
539
- } else {
540
- const currentOutput = toolPart.output || [];
541
- const existingOutput = Array.isArray(currentOutput) ? currentOutput : [];
542
- parts[toolPartIndex] = {
543
- type: "dynamic-tool",
544
- toolName,
545
- toolCallId,
546
- state: "input-streaming",
547
- input,
548
- output: [...existingOutput, chunk.payload.output]
549
- };
550
- }
522
+ case "tool-output":
523
+ case "background-task-output": {
524
+ const isBgTaskOutput = chunk.type === "background-task-output";
525
+ const location = locateToolPart(result, chunk.payload.toolCallId, isBgTaskOutput);
526
+ if (!location || location.toolPartIndex < 0) return result;
527
+ const { messageIndex, toolPartIndex } = location;
528
+ const targetMessage = result[messageIndex];
529
+ if (!targetMessage || targetMessage.role !== "assistant") return result;
530
+ const parts = [...targetMessage.parts];
531
+ const toolPart = parts[toolPartIndex];
532
+ if (toolPart.type === "dynamic-tool" || typeof toolPart.type === "string" && toolPart.type.startsWith("tool-")) {
533
+ const toolName = "toolName" in toolPart && typeof toolPart.toolName === "string" ? toolPart.toolName : typeof toolPart.type === "string" && toolPart.type.startsWith("tool-") ? toolPart.type.substring(5) : "";
534
+ const toolCallId = toolPart.toolCallId;
535
+ const input = toolPart.input;
536
+ const payloadOutput = chunk.type === "background-task-output" ? chunk.payload.payload.payload.output : chunk.payload.output;
537
+ if (payloadOutput?.type?.startsWith("workflow-")) {
538
+ const existingWorkflowState = toolPart.output || {};
539
+ const updatedWorkflowState = mapWorkflowStreamChunkToWatchResult(existingWorkflowState, payloadOutput);
540
+ parts[toolPartIndex] = {
541
+ type: "dynamic-tool",
542
+ toolName,
543
+ toolCallId,
544
+ state: "input-streaming",
545
+ input,
546
+ output: updatedWorkflowState
547
+ };
548
+ } else if (payloadOutput?.from === "AGENT" || payloadOutput?.from === "USER" && payloadOutput?.payload?.output?.type?.startsWith("workflow-")) {
549
+ return toUIMessageFromAgent(payloadOutput, conversation, metadata, toolCallId, toolName);
550
+ } else {
551
+ const currentOutput = toolPart.output || [];
552
+ const existingOutput = Array.isArray(currentOutput) ? currentOutput : [];
553
+ parts[toolPartIndex] = {
554
+ type: "dynamic-tool",
555
+ toolName,
556
+ toolCallId,
557
+ state: "input-streaming",
558
+ input,
559
+ output: [...existingOutput, payloadOutput]
560
+ };
551
561
  }
552
562
  }
553
563
  return [
554
- ...result.slice(0, -1),
564
+ ...result.slice(0, messageIndex),
555
565
  {
556
- ...lastMessage,
566
+ ...targetMessage,
557
567
  parts
558
- }
568
+ },
569
+ ...result.slice(messageIndex + 1)
559
570
  ];
560
571
  }
561
572
  case "is-task-complete": {
@@ -836,6 +847,69 @@ var toUIMessageFromAgent = (chunk, conversation, metadata, parentToolCallId, par
836
847
  }
837
848
  ];
838
849
  };
850
+ var findMessageIndexByToolCallId = (messages, toolCallId) => {
851
+ let count = 0;
852
+ for (let i = messages.length - 1; i >= 0; i--) {
853
+ const maxMessagesBack = 10;
854
+ if (count > maxMessagesBack) {
855
+ return -1;
856
+ }
857
+ const message = messages[i];
858
+ if (message.role !== "assistant") {
859
+ continue;
860
+ }
861
+ for (const part of message.parts) {
862
+ if (part.type === "dynamic-tool" && part.toolCallId === toolCallId) {
863
+ return i;
864
+ }
865
+ }
866
+ count++;
867
+ }
868
+ return -1;
869
+ };
870
+ var locateToolPart = (messages, toolCallId, allowMetadataOnlyMatch) => {
871
+ const lastMessage = messages[messages.length - 1];
872
+ if (lastMessage && lastMessage.role === "assistant") {
873
+ const toolPartIndex2 = lastMessage.parts.findIndex(
874
+ (part) => (part.type === "dynamic-tool" || typeof part.type === "string" && part.type.startsWith("tool-")) && "toolCallId" in part && part.toolCallId === toolCallId
875
+ );
876
+ if (toolPartIndex2 !== -1) {
877
+ return { messageIndex: messages.length - 1, toolPartIndex: toolPartIndex2 };
878
+ }
879
+ }
880
+ const messageIndex = findMessageIndexByToolCallId(messages, toolCallId);
881
+ if (messageIndex === -1) return null;
882
+ const message = messages[messageIndex];
883
+ if (!message || message.role !== "assistant") return null;
884
+ const toolPartIndex = message.parts.findIndex(
885
+ (part) => (part.type === "dynamic-tool" || typeof part.type === "string" && part.type.startsWith("tool-")) && "toolCallId" in part && part.toolCallId === toolCallId
886
+ );
887
+ if (toolPartIndex === -1) {
888
+ return allowMetadataOnlyMatch ? { messageIndex, toolPartIndex: -1 } : null;
889
+ }
890
+ return { messageIndex, toolPartIndex };
891
+ };
892
+ var mergeBgTaskMetadata = (existing, mode, args) => {
893
+ const existingAny = existing ?? {};
894
+ const existingBgTasks = existingAny.backgroundTasks ?? {};
895
+ const nextBgTasks = { ...existingBgTasks };
896
+ if (args.perTaskEntry) {
897
+ const { toolCallId, startedAt, completedAt, taskId } = args.perTaskEntry;
898
+ const prev = existingBgTasks[toolCallId] ?? { taskId };
899
+ nextBgTasks[toolCallId] = {
900
+ ...prev,
901
+ taskId,
902
+ ...startedAt !== void 0 ? { startedAt } : {},
903
+ ...completedAt !== void 0 ? { completedAt } : {}
904
+ };
905
+ }
906
+ return {
907
+ ...existingAny,
908
+ mode,
909
+ ...args.resetRunningCount ? { runningBackgroundTasksCount: void 0 } : {},
910
+ backgroundTasks: nextBgTasks
911
+ };
912
+ };
839
913
 
840
914
  // src/lib/ai-sdk/utils/toAssistantUIMessage.ts
841
915
  var toAssistantUIMessage = (message) => {
@@ -1743,6 +1817,7 @@ var useChat = ({
1743
1817
  const _networkRunId = useRef(void 0);
1744
1818
  const _onNetworkChunk = useRef(void 0);
1745
1819
  const _requestContext = useRef(propsRequestContext);
1820
+ const _streamAbortRef = useRef(null);
1746
1821
  const [messages, setMessages] = useState([]);
1747
1822
  const [toolCallApprovals, setToolCallApprovals] = useState({});
1748
1823
  const [networkToolCallApprovals, setNetworkToolCallApprovals] = useState({});
@@ -1865,13 +1940,20 @@ var useChat = ({
1865
1940
  const resolvedRequestContext = requestContext ?? propsRequestContext;
1866
1941
  _requestContext.current = resolvedRequestContext;
1867
1942
  setIsRunning(true);
1943
+ _streamAbortRef.current?.abort();
1944
+ const internalAbort = new AbortController();
1945
+ _streamAbortRef.current = internalAbort;
1946
+ if (signal) {
1947
+ if (signal.aborted) internalAbort.abort();
1948
+ else signal.addEventListener("abort", () => internalAbort.abort(), { once: true });
1949
+ }
1868
1950
  const clientWithAbort = new MastraClient({
1869
1951
  ...baseClient.options,
1870
- abortSignal: signal
1952
+ abortSignal: internalAbort.signal
1871
1953
  });
1872
1954
  const agent = clientWithAbort.getAgent(agentId);
1873
1955
  const runId = v4();
1874
- const response = await agent.stream(coreUserMessages, {
1956
+ const response = await agent.streamUntilIdle(coreUserMessages, {
1875
1957
  runId,
1876
1958
  maxSteps,
1877
1959
  modelSettings: {
@@ -1898,6 +1980,9 @@ var useChat = ({
1898
1980
  void onChunk?.(chunk);
1899
1981
  }
1900
1982
  });
1983
+ if (_streamAbortRef.current === internalAbort) {
1984
+ _streamAbortRef.current = null;
1985
+ }
1901
1986
  setIsRunning(false);
1902
1987
  };
1903
1988
  const network = async ({