@mastra/react 0.0.10 → 0.0.11-alpha.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
@@ -1,5 +1,5 @@
1
1
  import { jsx, jsxs } from 'react/jsx-runtime';
2
- import { createContext, useContext, useState, Fragment, useLayoutEffect, useRef, useEffect } from 'react';
2
+ import { createContext, useContext, useRef, useState, Fragment, useLayoutEffect, useEffect } from 'react';
3
3
  import { MastraClient } from '@mastra/client-js';
4
4
  import { ChevronDownIcon, CheckIcon, CopyIcon } from 'lucide-react';
5
5
  import { twMerge } from 'tailwind-merge';
@@ -249,6 +249,7 @@ const toUIMessage = ({ chunk, conversation, metadata }) => {
249
249
  }
250
250
  ];
251
251
  }
252
+ case "tool-error":
252
253
  case "tool-result": {
253
254
  const lastMessage = result[result.length - 1];
254
255
  if (!lastMessage || lastMessage.role !== "assistant") return result;
@@ -259,14 +260,15 @@ const toUIMessage = ({ chunk, conversation, metadata }) => {
259
260
  if (toolPartIndex !== -1) {
260
261
  const toolPart = parts[toolPartIndex];
261
262
  if (toolPart.type === "dynamic-tool") {
262
- if (chunk.payload.isError) {
263
+ if (chunk.type === "tool-result" && chunk.payload.isError || chunk.type === "tool-error") {
264
+ const error = chunk.type === "tool-error" ? chunk.payload.error : chunk.payload.result;
263
265
  parts[toolPartIndex] = {
264
266
  type: "dynamic-tool",
265
267
  toolName: toolPart.toolName,
266
268
  toolCallId: toolPart.toolCallId,
267
269
  state: "output-error",
268
270
  input: toolPart.input,
269
- errorText: String(chunk.payload.result),
271
+ errorText: String(error),
270
272
  callProviderMetadata: chunk.payload.providerMetadata
271
273
  };
272
274
  } else {
@@ -395,6 +397,29 @@ const toUIMessage = ({ chunk, conversation, metadata }) => {
395
397
  }
396
398
  ];
397
399
  }
400
+ case "tool-call-approval": {
401
+ const lastMessage = result[result.length - 1];
402
+ if (!lastMessage || lastMessage.role !== "assistant") return result;
403
+ const lastRequireApprovalMetadata = lastMessage.metadata?.mode === "stream" ? lastMessage.metadata?.requireApprovalMetadata : {};
404
+ return [
405
+ ...result.slice(0, -1),
406
+ {
407
+ ...lastMessage,
408
+ metadata: {
409
+ ...lastMessage.metadata,
410
+ mode: "stream",
411
+ requireApprovalMetadata: {
412
+ ...lastRequireApprovalMetadata,
413
+ [chunk.payload.toolCallId]: {
414
+ toolCallId: chunk.payload.toolCallId,
415
+ toolName: chunk.payload.toolName,
416
+ args: chunk.payload.args
417
+ }
418
+ }
419
+ }
420
+ }
421
+ ];
422
+ }
398
423
  case "finish": {
399
424
  const lastMessage = result[result.length - 1];
400
425
  if (!lastMessage || lastMessage.role !== "assistant") return result;
@@ -1133,9 +1158,12 @@ class AISdkNetworkTransformer {
1133
1158
  }
1134
1159
 
1135
1160
  const useChat = ({ agentId, initializeMessages }) => {
1161
+ const _currentRunId = useRef(void 0);
1162
+ const _onChunk = useRef(void 0);
1136
1163
  const [messages, setMessages] = useState(
1137
1164
  () => resolveInitialMessages(initializeMessages?.() || [])
1138
1165
  );
1166
+ const [toolCallApprovals, setToolCallApprovals] = useState({});
1139
1167
  const baseClient = useMastraClient();
1140
1168
  const [isRunning, setIsRunning] = useState(false);
1141
1169
  const generate = async ({
@@ -1205,7 +1233,8 @@ const useChat = ({ agentId, initializeMessages }) => {
1205
1233
  topP,
1206
1234
  instructions,
1207
1235
  providerOptions,
1208
- maxSteps
1236
+ maxSteps,
1237
+ requireToolApproval
1209
1238
  } = modelSettings || {};
1210
1239
  setIsRunning(true);
1211
1240
  const clientWithAbort = new MastraClient({
@@ -1213,9 +1242,10 @@ const useChat = ({ agentId, initializeMessages }) => {
1213
1242
  abortSignal: signal
1214
1243
  });
1215
1244
  const agent = clientWithAbort.getAgent(agentId);
1245
+ const runId = agentId;
1216
1246
  const response = await agent.stream({
1217
1247
  messages: coreUserMessages,
1218
- runId: agentId,
1248
+ runId,
1219
1249
  maxSteps,
1220
1250
  modelSettings: {
1221
1251
  frequencyPenalty,
@@ -1229,12 +1259,11 @@ const useChat = ({ agentId, initializeMessages }) => {
1229
1259
  instructions,
1230
1260
  runtimeContext,
1231
1261
  ...threadId ? { threadId, resourceId: agentId } : {},
1232
- providerOptions
1262
+ providerOptions,
1263
+ requireToolApproval
1233
1264
  });
1234
- if (!response.body) {
1235
- setIsRunning(false);
1236
- throw new Error("[Stream] No response body");
1237
- }
1265
+ _onChunk.current = onChunk;
1266
+ _currentRunId.current = runId;
1238
1267
  await response.processDataStream({
1239
1268
  onChunk: async (chunk) => {
1240
1269
  setMessages((prev) => toUIMessage({ chunk, conversation: prev, metadata: { mode: "stream" } }));
@@ -1283,6 +1312,45 @@ const useChat = ({ agentId, initializeMessages }) => {
1283
1312
  });
1284
1313
  setIsRunning(false);
1285
1314
  };
1315
+ const handleCancelRun = () => {
1316
+ setIsRunning(false);
1317
+ _currentRunId.current = void 0;
1318
+ _onChunk.current = void 0;
1319
+ };
1320
+ const approveToolCall = async (toolCallId) => {
1321
+ const onChunk = _onChunk.current;
1322
+ const currentRunId = _currentRunId.current;
1323
+ if (!currentRunId)
1324
+ return console.info("[approveToolCall] approveToolCall can only be called after a stream has started");
1325
+ setIsRunning(true);
1326
+ setToolCallApprovals((prev) => ({ ...prev, [toolCallId]: { status: "approved" } }));
1327
+ const agent = baseClient.getAgent(agentId);
1328
+ const response = await agent.approveToolCall({ runId: currentRunId, toolCallId });
1329
+ await response.processDataStream({
1330
+ onChunk: async (chunk) => {
1331
+ setMessages((prev) => toUIMessage({ chunk, conversation: prev, metadata: { mode: "stream" } }));
1332
+ onChunk?.(chunk);
1333
+ }
1334
+ });
1335
+ setIsRunning(false);
1336
+ };
1337
+ const declineToolCall = async (toolCallId) => {
1338
+ const onChunk = _onChunk.current;
1339
+ const currentRunId = _currentRunId.current;
1340
+ if (!currentRunId)
1341
+ return console.info("[declineToolCall] declineToolCall can only be called after a stream has started");
1342
+ setIsRunning(true);
1343
+ setToolCallApprovals((prev) => ({ ...prev, [toolCallId]: { status: "declined" } }));
1344
+ const agent = baseClient.getAgent(agentId);
1345
+ const response = await agent.declineToolCall({ runId: currentRunId, toolCallId });
1346
+ await response.processDataStream({
1347
+ onChunk: async (chunk) => {
1348
+ setMessages((prev) => toUIMessage({ chunk, conversation: prev, metadata: { mode: "stream" } }));
1349
+ onChunk?.(chunk);
1350
+ }
1351
+ });
1352
+ setIsRunning(false);
1353
+ };
1286
1354
  const sendMessage = async ({ mode = "stream", ...args }) => {
1287
1355
  const nextMessage = { role: "user", content: [{ type: "text", text: args.message }] };
1288
1356
  const messages2 = args.coreUserMessages ? [nextMessage, ...args.coreUserMessages] : [nextMessage];
@@ -1300,7 +1368,10 @@ const useChat = ({ agentId, initializeMessages }) => {
1300
1368
  sendMessage,
1301
1369
  isRunning,
1302
1370
  messages,
1303
- cancelRun: () => setIsRunning(false)
1371
+ approveToolCall,
1372
+ declineToolCall,
1373
+ cancelRun: handleCancelRun,
1374
+ toolCallApprovals
1304
1375
  };
1305
1376
  };
1306
1377