@mastra/react 1.5.1-alpha.2 → 1.6.0-alpha.10

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,4 +1,3 @@
1
- import { $ as useEntity, A as TooltipContentClass, B as ToolApprovalContent, C as CodeBlock, D as IconButtonClass, E as IconButton, F as EntryTitleClass, G as ToolApprovalTitleClass, H as ToolApprovalHeader, I as ToolApproval, J as EntityContent, K as Entity, L as ToolApprovalActions, M as Entry, N as EntryClass, O as Tooltip, P as EntryTitle, Q as EntityTriggerVariantClasses, R as ToolApprovalActionsClass, S as AgentIcon, T as CodeCopyButton, U as ToolApprovalHeaderClass, V as ToolApprovalContentClass, W as ToolApprovalTitle, X as EntityTrigger, Y as EntityContentClass, Z as EntityTriggerClass, _ as MessageUsageValueClass, a as MessageClass, b as WorkflowIcon, c as MessageList, d as MessageStreamingClass, et as Icon, f as MessageUsage, g as MessageUsageValue, h as MessageUsageEntryClass, i as MessageActionsClass, j as TooltipTrigger, k as TooltipContent, l as MessageListClass, m as MessageUsageEntry, n as Message, o as MessageContent, p as MessageUsageClass, q as EntityCaret, r as MessageActions, s as MessageContentClass, t as MessageFactory, tt as IconSizes, u as MessageStreaming, v as MessageUsages, w as CodeBlockClass, x as ToolsIcon, y as MessageUsagesClass, z as ToolApprovalClass } from "./ui-BQyoxYsH.js";
2
1
  import { MastraClient } from "@mastra/client-js";
3
2
  import { createContext, memo, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
4
3
  import { Fragment as Fragment$1, jsx } from "react/jsx-runtime";
@@ -140,6 +139,37 @@ function toolErrorText(error) {
140
139
  }
141
140
  return String(error);
142
141
  }
142
+ /**
143
+ * Agent-as-tool boundaries wrap progressive chunks in nested `tool-output`
144
+ * envelopes. Peel only agent boundaries; workflow/tool envelopes remain the
145
+ * leaf so existing child-message accumulation can process them normally.
146
+ */
147
+ const normalizeNestedAgentOutput = (output) => {
148
+ const boundaries = [];
149
+ const seen = /* @__PURE__ */ new Set();
150
+ let current = output;
151
+ while (current !== null && typeof current === "object") {
152
+ if (seen.has(current)) return void 0;
153
+ seen.add(current);
154
+ const chunk = current;
155
+ if (chunk.type !== "tool-output") return {
156
+ leaf: current,
157
+ boundaries
158
+ };
159
+ const toolCallId = chunk.payload?.toolCallId;
160
+ const toolName = chunk.payload?.toolName;
161
+ if (typeof toolName !== "string" || !toolName.startsWith("agent-")) return {
162
+ leaf: current,
163
+ boundaries
164
+ };
165
+ if (typeof toolCallId !== "string" || !("output" in (chunk.payload ?? {}))) return void 0;
166
+ boundaries.push({
167
+ toolCallId,
168
+ toolName
169
+ });
170
+ current = chunk.payload?.output;
171
+ }
172
+ };
143
173
  const cloneMetadata = (metadata) => metadata ? { ...metadata } : {};
144
174
  const withParts = (message, parts) => ({
145
175
  ...message,
@@ -267,39 +297,47 @@ const mergeBgTaskMetadata = (existing, mode, args, otherMetadata) => {
267
297
  * `mapWorkflowStreamChunkToWatchResult` from the previous accumulator.
268
298
  */
269
299
  const mapWorkflowStreamChunkToWatchResult = (prev, chunk) => {
300
+ const previous = prev ?? {
301
+ status: "running",
302
+ input: void 0,
303
+ steps: {}
304
+ };
270
305
  if (chunk.type === "workflow-start") return {
271
- input: prev?.input,
306
+ input: previous.input,
272
307
  status: "running",
273
- steps: prev?.steps || {}
308
+ steps: previous.steps
274
309
  };
275
310
  if (chunk.type === "workflow-canceled") return {
276
- ...prev,
311
+ ...previous,
277
312
  status: "canceled"
278
313
  };
314
+ if (chunk.type === "workflow-paused") return {
315
+ ...previous,
316
+ status: "paused"
317
+ };
279
318
  if (chunk.type === "workflow-finish") {
280
319
  const finalStatus = chunk.payload.workflowStatus;
281
- const prevSteps = prev?.steps ?? {};
282
- const lastStep = Object.values(prevSteps).pop();
320
+ const lastStep = Object.values(previous.steps).pop();
283
321
  return {
284
- ...prev,
322
+ ...previous,
285
323
  status: chunk.payload.workflowStatus,
286
324
  ...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 } : {}
287
325
  };
288
326
  }
289
327
  const { stepCallId: _stepCallId, stepName: _stepName, ...newPayload } = chunk.payload ?? {};
290
328
  const newSteps = {
291
- ...prev?.steps,
329
+ ...previous.steps,
292
330
  [chunk.payload.id]: {
293
- ...prev?.steps?.[chunk.payload.id],
331
+ ...previous.steps[chunk.payload.id],
294
332
  ...newPayload
295
333
  }
296
334
  };
297
335
  if (chunk.type === "workflow-step-start") return {
298
- ...prev,
336
+ ...previous,
299
337
  steps: newSteps
300
338
  };
301
339
  if (chunk.type === "workflow-step-suspended") {
302
- const suspendedStepIds = Object.entries(newSteps).flatMap(([stepId, stepResult]) => {
340
+ const suspended = Object.entries(newSteps).flatMap(([stepId, stepResult]) => {
303
341
  if (stepResult?.status === "suspended") {
304
342
  const nestedPath = stepResult?.suspendPayload?.__workflow_meta?.path;
305
343
  return nestedPath ? [[stepId, ...nestedPath]] : [[stepId]];
@@ -307,24 +345,24 @@ const mapWorkflowStreamChunkToWatchResult = (prev, chunk) => {
307
345
  return [];
308
346
  });
309
347
  return {
310
- ...prev,
348
+ ...previous,
311
349
  status: "suspended",
312
350
  steps: newSteps,
313
351
  suspendPayload: chunk.payload.suspendPayload,
314
- suspended: suspendedStepIds
352
+ suspended
315
353
  };
316
354
  }
317
355
  if (chunk.type === "workflow-step-waiting") return {
318
- ...prev,
356
+ ...previous,
319
357
  status: "waiting",
320
358
  steps: newSteps
321
359
  };
322
360
  if (chunk.type === "workflow-step-progress") return {
323
- ...prev,
361
+ ...previous,
324
362
  steps: {
325
- ...prev?.steps,
363
+ ...previous.steps,
326
364
  [chunk.payload.id]: {
327
- ...prev?.steps?.[chunk.payload.id],
365
+ ...previous.steps[chunk.payload.id],
328
366
  foreachProgress: {
329
367
  completedCount: chunk.payload.completedCount,
330
368
  totalCount: chunk.payload.totalCount,
@@ -336,10 +374,10 @@ const mapWorkflowStreamChunkToWatchResult = (prev, chunk) => {
336
374
  }
337
375
  };
338
376
  if (chunk.type === "workflow-step-result") return {
339
- ...prev,
377
+ ...previous,
340
378
  steps: newSteps
341
379
  };
342
- return prev;
380
+ return previous;
343
381
  };
344
382
  const signalContentsToUserMessages = (contents, metadata) => {
345
383
  const makeUserMessage = (parts) => ({
@@ -829,7 +867,7 @@ const accumulateChunk = ({ chunk, conversation, metadata }) => {
829
867
  toolName,
830
868
  args,
831
869
  errorText,
832
- ...toolName?.startsWith("agent-") ? { result: toolPart.toolInvocation.result } : {}
870
+ ...chunk.from === "AGENT" ? { result: toolPart.toolInvocation.result } : {}
833
871
  }
834
872
  };
835
873
  } else {
@@ -851,7 +889,7 @@ const accumulateChunk = ({ chunk, conversation, metadata }) => {
851
889
  };
852
890
  else output = payloadResult;
853
891
  } else if (isAgent) {
854
- const existingOutput = toolPart.toolInvocation.state === "result" ? toolPart.toolInvocation.result : void 0;
892
+ const existingOutput = toolPart.toolInvocation.state === "partial-call" || toolPart.toolInvocation.state === "result" ? toolPart.toolInvocation.result : void 0;
855
893
  const existingChild = existingOutput?.childMessages;
856
894
  output = existingOutput ? {
857
895
  ...payloadResult,
@@ -916,7 +954,7 @@ const accumulateChunk = ({ chunk, conversation, metadata }) => {
916
954
  const { toolName, toolCallId, args } = toolPart.toolInvocation;
917
955
  const payloadOutput = chunk.type === "background-task-output" ? chunk.payload.payload.payload.output : chunk.payload.output;
918
956
  if (payloadOutput?.type?.startsWith("workflow-")) {
919
- const existingWorkflowState = toolPart.toolInvocation.result || {};
957
+ const existingWorkflowState = toolPart.toolInvocation.result;
920
958
  const updated = mapWorkflowStreamChunkToWatchResult(existingWorkflowState, payloadOutput);
921
959
  parts[toolPartIndex] = {
922
960
  ...toolPart,
@@ -928,8 +966,13 @@ const accumulateChunk = ({ chunk, conversation, metadata }) => {
928
966
  result: updated
929
967
  }
930
968
  };
931
- } else if (payloadOutput?.from === "AGENT" || payloadOutput?.from === "USER" && payloadOutput?.payload?.output?.type?.startsWith("workflow-")) return accumulateAgentChunk(payloadOutput, result, metadata, toolCallId, toolName);
932
- else {
969
+ } else if (toolName.startsWith("agent-") || payloadOutput?.from === "AGENT") {
970
+ const normalized = normalizeNestedAgentOutput(payloadOutput);
971
+ if (!normalized) return result;
972
+ const { leaf, boundaries } = normalized;
973
+ if (leaf.from === "AGENT" || leaf.type === "tool-output" && leaf.payload?.output?.type?.startsWith("workflow-")) return accumulateAgentChunk(leaf, result, metadata, toolCallId, toolName, boundaries);
974
+ return result;
975
+ } else {
933
976
  const currentResult = toolPart.toolInvocation.result;
934
977
  const existing = Array.isArray(currentResult) ? currentResult : [];
935
978
  parts[toolPartIndex] = {
@@ -1146,8 +1189,7 @@ const accumulateChunk = ({ chunk, conversation, metadata }) => {
1146
1189
  case "network-validation-start":
1147
1190
  case "network-validation-end":
1148
1191
  case "network-object":
1149
- case "network-object-result":
1150
- case "tool-output-denied": return result;
1192
+ case "network-object-result": return result;
1151
1193
  default: return assertExhaustive(chunk, result);
1152
1194
  }
1153
1195
  };
@@ -1158,112 +1200,130 @@ const accumulateChunk = ({ chunk, conversation, metadata }) => {
1158
1200
  * that isn't enumerated above.
1159
1201
  */
1160
1202
  const assertExhaustive = (_chunk, fallback) => fallback;
1161
- const accumulateAgentChunk = (chunk, conversation, _metadata, parentToolCallId, parentToolName) => {
1162
- const lastMessage = conversation[conversation.length - 1];
1163
- if (!lastMessage || lastMessage.role !== "assistant") return conversation;
1164
- const parts = [...lastMessage.content.parts];
1165
- const findToolPartIndex = () => parts.findIndex((part) => isToolPart(part) && (parentToolCallId && part.toolInvocation.toolCallId === parentToolCallId || parentToolName && part.toolInvocation.toolName === parentToolName));
1203
+ const findAgentChildToolIndex = (childMessages, toolCallId, toolName) => {
1204
+ for (let index = childMessages.length - 1; index >= 0; index--) {
1205
+ const message = childMessages[index];
1206
+ if (message?.type !== "tool") continue;
1207
+ if (toolCallId ? message.toolCallId === toolCallId : toolName && message.toolName === toolName) return index;
1208
+ }
1209
+ return -1;
1210
+ };
1211
+ const accumulateAgentChildMessages = (chunk, childMessages) => {
1166
1212
  if (chunk.type === "text-delta") {
1167
- const agentChunk = chunk.payload;
1168
- const toolPartIndex = findToolPartIndex();
1169
- if (toolPartIndex === -1) return conversation;
1170
- const toolPart = parts[toolPartIndex];
1171
- const existingResult = toolPart.toolInvocation.result || {};
1172
- const childMessages = existingResult.childMessages || [];
1213
+ const text = chunk.payload.text;
1173
1214
  const lastChildMessage = childMessages[childMessages.length - 1];
1174
1215
  const textMessage = {
1175
1216
  type: "text",
1176
- content: (lastChildMessage?.content || "") + agentChunk.text
1217
+ content: (lastChildMessage?.type === "text" ? lastChildMessage.content : "") + text
1177
1218
  };
1178
- const nextChildren = lastChildMessage?.type === "text" ? [...childMessages.slice(0, -1), textMessage] : [...childMessages, textMessage];
1179
- parts[toolPartIndex] = {
1180
- ...toolPart,
1181
- toolInvocation: {
1182
- ...toolPart.toolInvocation,
1183
- result: {
1184
- ...existingResult,
1185
- childMessages: nextChildren
1186
- }
1187
- }
1219
+ return lastChildMessage?.type === "text" ? [...childMessages.slice(0, -1), textMessage] : [...childMessages, textMessage];
1220
+ }
1221
+ if (chunk.type === "tool-call") {
1222
+ const existingIndex = findAgentChildToolIndex(childMessages, chunk.payload.toolCallId);
1223
+ const toolMessage = {
1224
+ ...existingIndex === -1 ? {} : childMessages[existingIndex],
1225
+ type: "tool",
1226
+ toolCallId: chunk.payload.toolCallId,
1227
+ toolName: chunk.payload.toolName,
1228
+ args: chunk.payload.args
1188
1229
  };
1189
- } else if (chunk.type === "tool-call") {
1190
- const agentChunk = chunk.payload;
1191
- const toolPartIndex = findToolPartIndex();
1192
- if (toolPartIndex === -1) return conversation;
1193
- const toolPart = parts[toolPartIndex];
1194
- const existingResult = toolPart.toolInvocation.result || {};
1195
- const childMessages = existingResult.childMessages || [];
1196
- parts[toolPartIndex] = {
1197
- ...toolPart,
1198
- toolInvocation: {
1199
- ...toolPart.toolInvocation,
1200
- result: {
1201
- ...existingResult,
1202
- childMessages: [...childMessages, {
1203
- type: "tool",
1204
- toolCallId: agentChunk.toolCallId,
1205
- toolName: agentChunk.toolName,
1206
- args: agentChunk.args
1207
- }]
1208
- }
1230
+ if (existingIndex === -1) return [...childMessages, toolMessage];
1231
+ const nextMessages = [...childMessages];
1232
+ nextMessages[existingIndex] = toolMessage;
1233
+ return nextMessages;
1234
+ }
1235
+ if (chunk.type === "tool-output" && chunk.payload.output?.type?.startsWith("workflow-")) {
1236
+ const toolIndex = findAgentChildToolIndex(childMessages, chunk.payload.toolCallId, chunk.payload.toolName);
1237
+ if (toolIndex === -1) return childMessages;
1238
+ const currentMessage = childMessages[toolIndex];
1239
+ if (currentMessage?.type !== "tool") return childMessages;
1240
+ const existingWorkflowState = currentMessage.toolOutput && typeof currentMessage.toolOutput === "object" ? currentMessage.toolOutput : {};
1241
+ const updated = mapWorkflowStreamChunkToWatchResult(existingWorkflowState, chunk.payload.output);
1242
+ const nextMessages = [...childMessages];
1243
+ nextMessages[toolIndex] = {
1244
+ ...currentMessage,
1245
+ toolOutput: {
1246
+ ...updated,
1247
+ runId: chunk.payload.output.runId
1209
1248
  }
1210
1249
  };
1211
- } else if (chunk.type === "tool-output") {
1212
- const agentChunk = chunk.payload;
1213
- const toolPartIndex = findToolPartIndex();
1214
- if (toolPartIndex === -1) return conversation;
1215
- const toolPart = parts[toolPartIndex];
1216
- if (agentChunk?.output?.type?.startsWith("workflow-")) {
1217
- const existingResult = toolPart.toolInvocation.result || {};
1218
- const childMessages = existingResult.childMessages || [];
1219
- const lastIndex = childMessages.length - 1;
1220
- const currentMessage = childMessages[lastIndex];
1221
- const actualExistingWorkflowState = currentMessage?.toolOutput || {};
1222
- const updated = mapWorkflowStreamChunkToWatchResult(actualExistingWorkflowState, agentChunk.output);
1223
- if (lastIndex >= 0 && childMessages[lastIndex]?.type === "tool") parts[toolPartIndex] = {
1224
- ...toolPart,
1225
- toolInvocation: {
1226
- ...toolPart.toolInvocation,
1227
- result: {
1228
- ...existingResult,
1229
- childMessages: [...childMessages.slice(0, -1), {
1230
- ...currentMessage,
1231
- toolOutput: {
1232
- ...updated,
1233
- runId: agentChunk.output.runId
1234
- }
1235
- }]
1236
- }
1237
- }
1238
- };
1239
- }
1240
- } else if (chunk.type === "tool-result") {
1241
- const agentChunk = chunk.payload;
1242
- const toolPartIndex = findToolPartIndex();
1243
- if (toolPartIndex === -1) return conversation;
1244
- const toolPart = parts[toolPartIndex];
1245
- const existingResult = toolPart.toolInvocation.result || {};
1246
- const childMessages = existingResult.childMessages || [];
1247
- const lastIndex = childMessages.length - 1;
1248
- const isWorkflow = agentChunk?.toolName?.startsWith("workflow-");
1249
- if (lastIndex >= 0 && childMessages[lastIndex]?.type === "tool") parts[toolPartIndex] = {
1250
- ...toolPart,
1251
- toolInvocation: {
1252
- ...toolPart.toolInvocation,
1253
- result: {
1254
- ...existingResult,
1255
- childMessages: [...childMessages.slice(0, -1), {
1256
- ...childMessages[lastIndex],
1257
- toolOutput: isWorkflow ? {
1258
- ...agentChunk.result?.result,
1259
- runId: agentChunk.result?.runId
1260
- } : agentChunk.result
1261
- }]
1262
- }
1263
- }
1250
+ return nextMessages;
1251
+ }
1252
+ if (chunk.type === "tool-result") {
1253
+ const toolIndex = findAgentChildToolIndex(childMessages, chunk.payload.toolCallId, chunk.payload.toolName);
1254
+ if (toolIndex === -1) return childMessages;
1255
+ const currentMessage = childMessages[toolIndex];
1256
+ if (currentMessage?.type !== "tool") return childMessages;
1257
+ const result = chunk.payload.result;
1258
+ const nextMessages = [...childMessages];
1259
+ nextMessages[toolIndex] = {
1260
+ ...currentMessage,
1261
+ toolOutput: chunk.payload.toolName?.startsWith("workflow-") ? {
1262
+ ...result?.result ?? {},
1263
+ runId: result?.runId
1264
+ } : chunk.payload.result
1264
1265
  };
1266
+ return nextMessages;
1265
1267
  }
1266
- return replaceLast(conversation, withParts(lastMessage, parts));
1268
+ return childMessages;
1269
+ };
1270
+ const routeAgentChunk = (chunk, childMessages, boundaries) => {
1271
+ const [boundary, ...remaining] = boundaries;
1272
+ if (!boundary) return accumulateAgentChildMessages(chunk, childMessages);
1273
+ let toolIndex = findAgentChildToolIndex(childMessages, boundary.toolCallId, boundary.toolName);
1274
+ let nextMessages = childMessages;
1275
+ if (toolIndex === -1) {
1276
+ toolIndex = childMessages.length;
1277
+ nextMessages = [...childMessages, {
1278
+ type: "tool",
1279
+ toolCallId: boundary.toolCallId,
1280
+ toolName: boundary.toolName,
1281
+ args: {}
1282
+ }];
1283
+ }
1284
+ const currentMessage = nextMessages[toolIndex];
1285
+ if (currentMessage?.type !== "tool" || !currentMessage.toolName.startsWith("agent-")) return childMessages;
1286
+ const currentOutput = currentMessage.toolOutput !== null && typeof currentMessage.toolOutput === "object" ? currentMessage.toolOutput : {};
1287
+ const nestedMessages = Array.isArray(currentOutput.childMessages) ? currentOutput.childMessages : [];
1288
+ const updatedNestedMessages = routeAgentChunk(chunk, nestedMessages, remaining);
1289
+ if (updatedNestedMessages === nestedMessages) return childMessages;
1290
+ const updatedMessages = [...nextMessages];
1291
+ updatedMessages[toolIndex] = {
1292
+ ...currentMessage,
1293
+ toolOutput: {
1294
+ ...currentOutput,
1295
+ childMessages: updatedNestedMessages
1296
+ }
1297
+ };
1298
+ return updatedMessages;
1299
+ };
1300
+ const accumulateAgentChunk = (chunk, conversation, _metadata, parentToolCallId, parentToolName, boundaries = []) => {
1301
+ if (!parentToolCallId) return conversation;
1302
+ const location = locateToolPart(conversation, parentToolCallId, false);
1303
+ if (!location) return conversation;
1304
+ const targetMessage = conversation[location.messageIndex];
1305
+ if (!targetMessage || targetMessage.role !== "assistant") return conversation;
1306
+ const parts = [...targetMessage.content.parts];
1307
+ const toolPart = parts[location.toolPartIndex];
1308
+ if (!toolPart || !isToolPart(toolPart)) return conversation;
1309
+ if (parentToolName && toolPart.toolInvocation.toolName !== parentToolName) return conversation;
1310
+ const invocationResult = "result" in toolPart.toolInvocation && toolPart.toolInvocation.result !== null ? toolPart.toolInvocation.result : void 0;
1311
+ const existingResult = invocationResult && typeof invocationResult === "object" ? invocationResult : {};
1312
+ const childMessages = Array.isArray(existingResult.childMessages) ? existingResult.childMessages : [];
1313
+ const updatedChildMessages = routeAgentChunk(chunk, childMessages, boundaries);
1314
+ if (updatedChildMessages === childMessages) return conversation;
1315
+ parts[location.toolPartIndex] = {
1316
+ ...toolPart,
1317
+ toolInvocation: {
1318
+ ...toolPart.toolInvocation,
1319
+ state: "partial-call",
1320
+ result: {
1321
+ ...existingResult,
1322
+ childMessages: updatedChildMessages
1323
+ }
1324
+ }
1325
+ };
1326
+ return replaceAt(conversation, location.messageIndex, withParts(targetMessage, parts));
1267
1327
  };
1268
1328
  const networkMode = (metadata) => ({
1269
1329
  ...metadata,
@@ -2816,275 +2876,133 @@ function useMutation(mutationFn) {
2816
2876
  }
2817
2877
  //#endregion
2818
2878
  //#region src/workflows/use-stream-workflow.ts
2819
- /**
2820
- * Hook for streaming workflow execution with support for observing, resuming, and time-travel.
2821
- *
2822
- * @example
2823
- * ```tsx
2824
- * const {
2825
- * streamWorkflow,
2826
- * streamResult,
2827
- * isStreaming,
2828
- * observeWorkflowStream,
2829
- * closeStreamsAndReset,
2830
- * resumeWorkflowStream,
2831
- * timeTravelWorkflowStream,
2832
- * } = useStreamWorkflow({
2833
- * debugMode: true,
2834
- * tracingOptions: { enabled: true },
2835
- * onError: (error, defaultMessage) => console.error(defaultMessage, error),
2836
- * });
2837
- *
2838
- * // Start streaming a workflow
2839
- * await streamWorkflow.mutateAsync({
2840
- * workflowId: 'my-workflow',
2841
- * runId: 'run-123',
2842
- * inputData: { key: 'value' },
2843
- * requestContext: {},
2844
- * });
2845
- * ```
2846
- */
2847
2879
  function useStreamWorkflow({ debugMode, tracingOptions, onError }) {
2848
2880
  const client = useMastraClient();
2849
- const [streamResult, setStreamResult] = useState({});
2881
+ const [streamResult, setStreamResult] = useState();
2850
2882
  const [isStreaming, setIsStreaming] = useState(false);
2851
- const readerRef = useRef(null);
2852
- const observerRef = useRef(null);
2853
- const resumeStreamRef = useRef(null);
2854
- const timeTravelStreamRef = useRef(null);
2883
+ const activeStream = useRef(void 0);
2855
2884
  const isMountedRef = useRef(true);
2885
+ const closeActiveStream = useCallback(() => {
2886
+ const operation = activeStream.current;
2887
+ activeStream.current = void 0;
2888
+ if (!operation) return;
2889
+ operation.controller.abort();
2890
+ operation.reader?.cancel().catch(() => void 0);
2891
+ operation.reader?.releaseLock();
2892
+ operation.reader = void 0;
2893
+ }, []);
2856
2894
  useEffect(() => {
2857
2895
  isMountedRef.current = true;
2858
2896
  return () => {
2859
2897
  isMountedRef.current = false;
2860
- if (readerRef.current) {
2861
- try {
2862
- readerRef.current.releaseLock();
2863
- } catch {}
2864
- readerRef.current = null;
2865
- }
2866
- if (observerRef.current) {
2867
- try {
2868
- observerRef.current.releaseLock();
2869
- } catch {}
2870
- observerRef.current = null;
2871
- }
2872
- if (resumeStreamRef.current) {
2873
- try {
2874
- resumeStreamRef.current.releaseLock();
2875
- } catch {}
2876
- resumeStreamRef.current = null;
2877
- }
2878
- if (timeTravelStreamRef.current) {
2879
- try {
2880
- timeTravelStreamRef.current.releaseLock();
2881
- } catch {}
2882
- timeTravelStreamRef.current = null;
2883
- }
2898
+ closeActiveStream();
2884
2899
  };
2885
- }, []);
2886
- const handleStreamError = useCallback((err, defaultMessage, setStreamingState) => {
2887
- if (err instanceof TypeError) return;
2888
- const error = err instanceof Error ? err : new Error(defaultMessage);
2889
- onError?.(error, defaultMessage);
2890
- setStreamingState?.(false);
2891
- }, [onError]);
2892
- const handleWorkflowFinish = useCallback((value) => {
2893
- if (value.type === "workflow-finish") {
2894
- const streamStatus = value.payload?.workflowStatus;
2895
- const metadata = value.payload?.metadata;
2896
- setStreamResult((prev) => ({
2897
- ...prev,
2898
- status: streamStatus
2899
- }));
2900
- if (streamStatus === "failed") throw new Error(metadata?.errorMessage || "Workflow execution failed");
2901
- }
2902
- }, []);
2903
- const streamWorkflow = useMutation(async ({ workflowId, runId, inputData, initialState, requestContext: playgroundRequestContext, perStep }) => {
2904
- if (readerRef.current) readerRef.current.releaseLock();
2900
+ }, [closeActiveStream]);
2901
+ async function consumeStream(openStream, defaultMessage) {
2905
2902
  if (!isMountedRef.current) return;
2903
+ closeActiveStream();
2904
+ const operation = { controller: new AbortController() };
2905
+ activeStream.current = operation;
2906
+ const { signal } = operation.controller;
2906
2907
  setIsStreaming(true);
2907
- setStreamResult({ input: inputData });
2908
- const stream = await (await client.getWorkflow(workflowId).createRun({ runId })).stream({
2909
- inputData,
2910
- initialState,
2911
- requestContext: playgroundRequestContext,
2912
- closeOnSuspend: true,
2913
- tracingOptions,
2914
- perStep: perStep ?? debugMode
2915
- });
2916
- if (!stream) return handleStreamError(/* @__PURE__ */ new Error("No stream returned"), "No stream returned", setIsStreaming);
2917
- const reader = stream.getReader();
2918
- readerRef.current = reader;
2919
2908
  try {
2920
- while (true) {
2921
- if (!isMountedRef.current) break;
2909
+ const stream = await openStream(signal);
2910
+ if (signal.aborted) {
2911
+ await stream?.cancel();
2912
+ return;
2913
+ }
2914
+ if (!stream) {
2915
+ onError?.(/* @__PURE__ */ new Error("No stream returned"), "No stream returned");
2916
+ return;
2917
+ }
2918
+ const reader = stream.getReader();
2919
+ operation.reader = reader;
2920
+ while (!signal.aborted) {
2922
2921
  const { done, value } = await reader.read();
2923
- if (done) break;
2924
- if (isMountedRef.current) {
2925
- setStreamResult((prev) => {
2926
- return mapWorkflowStreamChunkToWatchResult(prev, value);
2927
- });
2928
- if (value.type === "workflow-step-start") setIsStreaming(true);
2929
- if (value.type === "workflow-step-suspended") setIsStreaming(false);
2930
- if (value.type === "workflow-finish") handleWorkflowFinish(value);
2931
- }
2922
+ if (done || signal.aborted) break;
2923
+ setStreamResult((previous) => mapWorkflowStreamChunkToWatchResult(previous, value));
2924
+ if (value.type === "workflow-step-start") setIsStreaming(true);
2925
+ if (value.type === "workflow-step-suspended") setIsStreaming(false);
2926
+ if (value.type === "workflow-finish" && value.payload.workflowStatus === "failed") throw new Error(value.payload.metadata?.errorMessage || "Workflow execution failed");
2927
+ }
2928
+ } catch (error) {
2929
+ if (!signal.aborted) {
2930
+ if (!operation.reader) throw error;
2931
+ onError?.(error instanceof Error ? error : new Error(defaultMessage), defaultMessage);
2932
2932
  }
2933
- } catch (err) {
2934
- handleStreamError(err, "Error streaming workflow");
2935
2933
  } finally {
2936
- if (isMountedRef.current) setIsStreaming(false);
2937
- if (readerRef.current) {
2938
- readerRef.current.releaseLock();
2939
- readerRef.current = null;
2934
+ operation.reader?.releaseLock();
2935
+ operation.reader = void 0;
2936
+ if (activeStream.current === operation) {
2937
+ activeStream.current = void 0;
2938
+ setIsStreaming(false);
2940
2939
  }
2941
2940
  }
2941
+ }
2942
+ const streamWorkflow = useMutation(({ workflowId, runId, inputData, initialState, requestContext, perStep }) => {
2943
+ setStreamResult({
2944
+ status: "pending",
2945
+ input: inputData,
2946
+ steps: {}
2947
+ });
2948
+ return consumeStream(async (signal) => {
2949
+ const run = await client.getWorkflow(workflowId).createRun({ runId });
2950
+ if (signal.aborted) return;
2951
+ return run.stream({
2952
+ inputData,
2953
+ initialState,
2954
+ requestContext,
2955
+ closeOnSuspend: true,
2956
+ tracingOptions,
2957
+ perStep: perStep ?? debugMode
2958
+ });
2959
+ }, "Error streaming workflow");
2942
2960
  });
2943
2961
  const observeWorkflowStream = useMutation(async ({ workflowId, runId, storeRunResult }) => {
2944
- if (observerRef.current) observerRef.current.releaseLock();
2945
2962
  if (!isMountedRef.current) return;
2946
- setIsStreaming(true);
2947
- setStreamResult(storeRunResult || {});
2963
+ setStreamResult(storeRunResult ?? void 0);
2948
2964
  if (storeRunResult?.status === "suspended") {
2965
+ closeActiveStream();
2949
2966
  setIsStreaming(false);
2950
2967
  return;
2951
2968
  }
2952
- const stream = await (await client.getWorkflow(workflowId).createRun({ runId })).observeStream();
2953
- if (!stream) return handleStreamError(/* @__PURE__ */ new Error("No stream returned"), "No stream returned", setIsStreaming);
2954
- const reader = stream.getReader();
2955
- observerRef.current = reader;
2956
- try {
2957
- while (true) {
2958
- if (!isMountedRef.current) break;
2959
- const { done, value } = await reader.read();
2960
- if (done) break;
2961
- if (isMountedRef.current) {
2962
- setStreamResult((prev) => {
2963
- return mapWorkflowStreamChunkToWatchResult(prev, value);
2964
- });
2965
- if (value.type === "workflow-step-start") setIsStreaming(true);
2966
- if (value.type === "workflow-step-suspended") setIsStreaming(false);
2967
- if (value.type === "workflow-finish") handleWorkflowFinish(value);
2968
- }
2969
- }
2970
- } catch (err) {
2971
- handleStreamError(err, "Error observing workflow");
2972
- } finally {
2973
- if (isMountedRef.current) setIsStreaming(false);
2974
- if (observerRef.current) {
2975
- observerRef.current.releaseLock();
2976
- observerRef.current = null;
2977
- }
2978
- }
2969
+ await consumeStream(async (signal) => {
2970
+ const run = await client.getWorkflow(workflowId).createRun({ runId });
2971
+ if (signal.aborted) return;
2972
+ return run.observeStream();
2973
+ }, "Error observing workflow");
2979
2974
  });
2980
- const resumeWorkflowStream = useMutation(async ({ workflowId, runId, step, resumeData, requestContext: playgroundRequestContext, perStep }) => {
2981
- if (resumeStreamRef.current) resumeStreamRef.current.releaseLock();
2982
- if (!isMountedRef.current) return;
2983
- setIsStreaming(true);
2984
- const stream = await (await client.getWorkflow(workflowId).createRun({ runId })).resumeStream({
2975
+ const resumeWorkflowStream = useMutation(({ workflowId, runId, step, resumeData, requestContext, perStep }) => consumeStream(async (signal) => {
2976
+ const run = await client.getWorkflow(workflowId).createRun({ runId });
2977
+ if (signal.aborted) return;
2978
+ return run.resumeStream({
2985
2979
  step,
2986
2980
  resumeData,
2987
- requestContext: playgroundRequestContext,
2981
+ requestContext,
2988
2982
  tracingOptions,
2989
2983
  perStep: perStep ?? debugMode
2990
2984
  });
2991
- if (!stream) return handleStreamError(/* @__PURE__ */ new Error("No stream returned"), "No stream returned", setIsStreaming);
2992
- const reader = stream.getReader();
2993
- resumeStreamRef.current = reader;
2994
- try {
2995
- while (true) {
2996
- if (!isMountedRef.current) break;
2997
- const { done, value } = await reader.read();
2998
- if (done) break;
2999
- if (isMountedRef.current) {
3000
- setStreamResult((prev) => {
3001
- return mapWorkflowStreamChunkToWatchResult(prev, value);
3002
- });
3003
- if (value.type === "workflow-step-start") setIsStreaming(true);
3004
- if (value.type === "workflow-step-suspended") setIsStreaming(false);
3005
- if (value.type === "workflow-finish") handleWorkflowFinish(value);
3006
- }
3007
- }
3008
- } catch (err) {
3009
- handleStreamError(err, "Error resuming workflow stream");
3010
- } finally {
3011
- if (isMountedRef.current) setIsStreaming(false);
3012
- if (resumeStreamRef.current) {
3013
- resumeStreamRef.current.releaseLock();
3014
- resumeStreamRef.current = null;
3015
- }
3016
- }
3017
- });
3018
- const timeTravelWorkflowStream = useMutation(async ({ workflowId, requestContext: playgroundRequestContext, runId, perStep, ...params }) => {
3019
- if (timeTravelStreamRef.current) timeTravelStreamRef.current.releaseLock();
3020
- if (!isMountedRef.current) return;
3021
- setIsStreaming(true);
3022
- const stream = await (await client.getWorkflow(workflowId).createRun({ runId })).timeTravelStream({
2985
+ }, "Error resuming workflow stream"));
2986
+ const timeTravelWorkflowStream = useMutation(({ workflowId, requestContext, runId, perStep, ...params }) => consumeStream(async (signal) => {
2987
+ const run = await client.getWorkflow(workflowId).createRun({ runId });
2988
+ if (signal.aborted) return;
2989
+ return run.timeTravelStream({
3023
2990
  ...params,
3024
2991
  perStep: perStep ?? debugMode,
3025
- requestContext: playgroundRequestContext,
2992
+ requestContext,
3026
2993
  tracingOptions
3027
2994
  });
3028
- if (!stream) return handleStreamError(/* @__PURE__ */ new Error("No stream returned"), "No stream returned", setIsStreaming);
3029
- const reader = stream.getReader();
3030
- timeTravelStreamRef.current = reader;
3031
- try {
3032
- while (true) {
3033
- if (!isMountedRef.current) break;
3034
- const { done, value } = await reader.read();
3035
- if (done) break;
3036
- if (isMountedRef.current) {
3037
- setStreamResult((prev) => {
3038
- return mapWorkflowStreamChunkToWatchResult(prev, value);
3039
- });
3040
- if (value.type === "workflow-step-start") setIsStreaming(true);
3041
- if (value.type === "workflow-step-suspended") setIsStreaming(false);
3042
- if (value.type === "workflow-finish") handleWorkflowFinish(value);
3043
- }
3044
- }
3045
- } catch (err) {
3046
- handleStreamError(err, "Error time traveling workflow stream");
3047
- } finally {
3048
- if (isMountedRef.current) setIsStreaming(false);
3049
- if (timeTravelStreamRef.current) {
3050
- timeTravelStreamRef.current.releaseLock();
3051
- timeTravelStreamRef.current = null;
3052
- }
3053
- }
3054
- });
2995
+ }, "Error time traveling workflow stream"));
3055
2996
  return {
3056
2997
  streamWorkflow,
3057
2998
  streamResult,
3058
2999
  isStreaming,
3059
3000
  observeWorkflowStream,
3060
3001
  closeStreamsAndReset: useCallback(() => {
3002
+ closeActiveStream();
3061
3003
  setIsStreaming(false);
3062
- setStreamResult({});
3063
- if (readerRef.current) {
3064
- try {
3065
- readerRef.current.releaseLock();
3066
- } catch {}
3067
- readerRef.current = null;
3068
- }
3069
- if (observerRef.current) {
3070
- try {
3071
- observerRef.current.releaseLock();
3072
- } catch {}
3073
- observerRef.current = null;
3074
- }
3075
- if (resumeStreamRef.current) {
3076
- try {
3077
- resumeStreamRef.current.releaseLock();
3078
- } catch {}
3079
- resumeStreamRef.current = null;
3080
- }
3081
- if (timeTravelStreamRef.current) {
3082
- try {
3083
- timeTravelStreamRef.current.releaseLock();
3084
- } catch {}
3085
- timeTravelStreamRef.current = null;
3086
- }
3087
- }, []),
3004
+ setStreamResult(void 0);
3005
+ }, [closeActiveStream]),
3088
3006
  resumeWorkflowStream,
3089
3007
  timeTravelWorkflowStream
3090
3008
  };
@@ -3421,6 +3339,6 @@ const useMastraSpeechToText = ({ agent, language }) => {
3421
3339
  };
3422
3340
  };
3423
3341
  //#endregion
3424
- export { AgentIcon, CLIENT_MESSAGE_ID_KEY, 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, MessageFactory, 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, WorkflowStepFactory, accumulateChunk, accumulateNetworkChunk, finishStreamingAssistantMessage, fromCoreUserMessageToMastraDBMessage, fromCoreUserMessagesToMastraDBMessage, mapWorkflowStreamChunkToWatchResult, playStreamWithWebAudio, recordMicrophoneToFile, useCancelWorkflowRun, useChat, useCreateWorkflowRun, useEntity, useMastraClient, useSpeechRecognition, useStreamWorkflow };
3342
+ export { CLIENT_MESSAGE_ID_KEY, MastraReactProvider, WorkflowStepFactory, accumulateChunk, accumulateNetworkChunk, finishStreamingAssistantMessage, fromCoreUserMessageToMastraDBMessage, fromCoreUserMessagesToMastraDBMessage, mapWorkflowStreamChunkToWatchResult, playStreamWithWebAudio, recordMicrophoneToFile, useCancelWorkflowRun, useChat, useCreateWorkflowRun, useMastraClient, useSpeechRecognition, useStreamWorkflow };
3425
3343
 
3426
3344
  //# sourceMappingURL=index.js.map