@mastra/react 1.6.0-alpha.9 → 1.6.0

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
@@ -139,6 +139,37 @@ function toolErrorText(error) {
139
139
  }
140
140
  return String(error);
141
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
+ };
142
173
  const cloneMetadata = (metadata) => metadata ? { ...metadata } : {};
143
174
  const withParts = (message, parts) => ({
144
175
  ...message,
@@ -836,7 +867,7 @@ const accumulateChunk = ({ chunk, conversation, metadata }) => {
836
867
  toolName,
837
868
  args,
838
869
  errorText,
839
- ...toolName?.startsWith("agent-") ? { result: toolPart.toolInvocation.result } : {}
870
+ ...chunk.from === "AGENT" ? { result: toolPart.toolInvocation.result } : {}
840
871
  }
841
872
  };
842
873
  } else {
@@ -858,7 +889,7 @@ const accumulateChunk = ({ chunk, conversation, metadata }) => {
858
889
  };
859
890
  else output = payloadResult;
860
891
  } else if (isAgent) {
861
- 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;
862
893
  const existingChild = existingOutput?.childMessages;
863
894
  output = existingOutput ? {
864
895
  ...payloadResult,
@@ -935,8 +966,13 @@ const accumulateChunk = ({ chunk, conversation, metadata }) => {
935
966
  result: updated
936
967
  }
937
968
  };
938
- } else if (payloadOutput?.from === "AGENT" || payloadOutput?.from === "USER" && payloadOutput?.payload?.output?.type?.startsWith("workflow-")) return accumulateAgentChunk(payloadOutput, result, metadata, toolCallId, toolName);
939
- 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 {
940
976
  const currentResult = toolPart.toolInvocation.result;
941
977
  const existing = Array.isArray(currentResult) ? currentResult : [];
942
978
  parts[toolPartIndex] = {
@@ -1164,112 +1200,130 @@ const accumulateChunk = ({ chunk, conversation, metadata }) => {
1164
1200
  * that isn't enumerated above.
1165
1201
  */
1166
1202
  const assertExhaustive = (_chunk, fallback) => fallback;
1167
- const accumulateAgentChunk = (chunk, conversation, _metadata, parentToolCallId, parentToolName) => {
1168
- const lastMessage = conversation[conversation.length - 1];
1169
- if (!lastMessage || lastMessage.role !== "assistant") return conversation;
1170
- const parts = [...lastMessage.content.parts];
1171
- 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) => {
1172
1212
  if (chunk.type === "text-delta") {
1173
- const agentChunk = chunk.payload;
1174
- const toolPartIndex = findToolPartIndex();
1175
- if (toolPartIndex === -1) return conversation;
1176
- const toolPart = parts[toolPartIndex];
1177
- const existingResult = toolPart.toolInvocation.result || {};
1178
- const childMessages = existingResult.childMessages || [];
1213
+ const text = chunk.payload.text;
1179
1214
  const lastChildMessage = childMessages[childMessages.length - 1];
1180
1215
  const textMessage = {
1181
1216
  type: "text",
1182
- content: (lastChildMessage?.content || "") + agentChunk.text
1217
+ content: (lastChildMessage?.type === "text" ? lastChildMessage.content : "") + text
1183
1218
  };
1184
- const nextChildren = lastChildMessage?.type === "text" ? [...childMessages.slice(0, -1), textMessage] : [...childMessages, textMessage];
1185
- parts[toolPartIndex] = {
1186
- ...toolPart,
1187
- toolInvocation: {
1188
- ...toolPart.toolInvocation,
1189
- result: {
1190
- ...existingResult,
1191
- childMessages: nextChildren
1192
- }
1193
- }
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
1194
1229
  };
1195
- } else if (chunk.type === "tool-call") {
1196
- const agentChunk = chunk.payload;
1197
- const toolPartIndex = findToolPartIndex();
1198
- if (toolPartIndex === -1) return conversation;
1199
- const toolPart = parts[toolPartIndex];
1200
- const existingResult = toolPart.toolInvocation.result || {};
1201
- const childMessages = existingResult.childMessages || [];
1202
- parts[toolPartIndex] = {
1203
- ...toolPart,
1204
- toolInvocation: {
1205
- ...toolPart.toolInvocation,
1206
- result: {
1207
- ...existingResult,
1208
- childMessages: [...childMessages, {
1209
- type: "tool",
1210
- toolCallId: agentChunk.toolCallId,
1211
- toolName: agentChunk.toolName,
1212
- args: agentChunk.args
1213
- }]
1214
- }
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
1215
1248
  }
1216
1249
  };
1217
- } else if (chunk.type === "tool-output") {
1218
- const agentChunk = chunk.payload;
1219
- const toolPartIndex = findToolPartIndex();
1220
- if (toolPartIndex === -1) return conversation;
1221
- const toolPart = parts[toolPartIndex];
1222
- if (agentChunk?.output?.type?.startsWith("workflow-")) {
1223
- const existingResult = toolPart.toolInvocation.result || {};
1224
- const childMessages = existingResult.childMessages || [];
1225
- const lastIndex = childMessages.length - 1;
1226
- const currentMessage = childMessages[lastIndex];
1227
- const actualExistingWorkflowState = currentMessage?.toolOutput || {};
1228
- const updated = mapWorkflowStreamChunkToWatchResult(actualExistingWorkflowState, agentChunk.output);
1229
- if (lastIndex >= 0 && childMessages[lastIndex]?.type === "tool") parts[toolPartIndex] = {
1230
- ...toolPart,
1231
- toolInvocation: {
1232
- ...toolPart.toolInvocation,
1233
- result: {
1234
- ...existingResult,
1235
- childMessages: [...childMessages.slice(0, -1), {
1236
- ...currentMessage,
1237
- toolOutput: {
1238
- ...updated,
1239
- runId: agentChunk.output.runId
1240
- }
1241
- }]
1242
- }
1243
- }
1244
- };
1245
- }
1246
- } else if (chunk.type === "tool-result") {
1247
- const agentChunk = chunk.payload;
1248
- const toolPartIndex = findToolPartIndex();
1249
- if (toolPartIndex === -1) return conversation;
1250
- const toolPart = parts[toolPartIndex];
1251
- const existingResult = toolPart.toolInvocation.result || {};
1252
- const childMessages = existingResult.childMessages || [];
1253
- const lastIndex = childMessages.length - 1;
1254
- const isWorkflow = agentChunk?.toolName?.startsWith("workflow-");
1255
- if (lastIndex >= 0 && childMessages[lastIndex]?.type === "tool") parts[toolPartIndex] = {
1256
- ...toolPart,
1257
- toolInvocation: {
1258
- ...toolPart.toolInvocation,
1259
- result: {
1260
- ...existingResult,
1261
- childMessages: [...childMessages.slice(0, -1), {
1262
- ...childMessages[lastIndex],
1263
- toolOutput: isWorkflow ? {
1264
- ...agentChunk.result?.result,
1265
- runId: agentChunk.result?.runId
1266
- } : agentChunk.result
1267
- }]
1268
- }
1269
- }
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
1270
1265
  };
1266
+ return nextMessages;
1271
1267
  }
1272
- 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));
1273
1327
  };
1274
1328
  const networkMode = (metadata) => ({
1275
1329
  ...metadata,
@@ -2885,14 +2939,17 @@ function useStreamWorkflow({ debugMode, tracingOptions, onError }) {
2885
2939
  }
2886
2940
  }
2887
2941
  }
2888
- const streamWorkflow = useMutation(({ workflowId, runId, inputData, initialState, requestContext, perStep }) => {
2942
+ const streamWorkflow = useMutation(({ workflowId, runId, inputData, initialState, requestContext, perStep, resourceId }) => {
2889
2943
  setStreamResult({
2890
2944
  status: "pending",
2891
2945
  input: inputData,
2892
2946
  steps: {}
2893
2947
  });
2894
2948
  return consumeStream(async (signal) => {
2895
- const run = await client.getWorkflow(workflowId).createRun({ runId });
2949
+ const run = await client.getWorkflow(workflowId).createRun({
2950
+ runId,
2951
+ resourceId
2952
+ });
2896
2953
  if (signal.aborted) return;
2897
2954
  return run.stream({
2898
2955
  inputData,
@@ -2900,7 +2957,8 @@ function useStreamWorkflow({ debugMode, tracingOptions, onError }) {
2900
2957
  requestContext,
2901
2958
  closeOnSuspend: true,
2902
2959
  tracingOptions,
2903
- perStep: perStep ?? debugMode
2960
+ perStep: perStep ?? debugMode,
2961
+ resourceId
2904
2962
  });
2905
2963
  }, "Error streaming workflow");
2906
2964
  });
@@ -2971,9 +3029,12 @@ function useStreamWorkflow({ debugMode, tracingOptions, onError }) {
2971
3029
  */
2972
3030
  function useCreateWorkflowRun() {
2973
3031
  const client = useMastraClient();
2974
- return useMutation(async ({ workflowId, prevRunId }) => {
3032
+ return useMutation(async ({ workflowId, prevRunId, resourceId }) => {
2975
3033
  try {
2976
- const { runId: newRunId } = await client.getWorkflow(workflowId).createRun({ runId: prevRunId });
3034
+ const { runId: newRunId } = await client.getWorkflow(workflowId).createRun({
3035
+ runId: prevRunId,
3036
+ resourceId
3037
+ });
2977
3038
  return { runId: newRunId };
2978
3039
  } catch (error) {
2979
3040
  console.error("Error creating workflow run:", error);
@@ -3155,7 +3216,8 @@ const useBrowserSpeechRecognition = ({ language = "en-US" }) => {
3155
3216
  setState((prev) => ({
3156
3217
  ...prev,
3157
3218
  isListening: true,
3158
- error: null
3219
+ error: null,
3220
+ transcript: ""
3159
3221
  }));
3160
3222
  };
3161
3223
  recognition.onresult = (event) => {
@@ -3164,9 +3226,10 @@ const useBrowserSpeechRecognition = ({ language = "en-US" }) => {
3164
3226
  const transcript = event.results[i][0].transcript;
3165
3227
  if (event.results[i].isFinal) finalTranscript += transcript + " ";
3166
3228
  }
3229
+ if (!finalTranscript) return;
3167
3230
  setState((prev) => ({
3168
3231
  ...prev,
3169
- transcript: finalTranscript
3232
+ transcript: prev.transcript + finalTranscript
3170
3233
  }));
3171
3234
  };
3172
3235
  recognition.onerror = (event) => {