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