@mastra/react 0.4.3-alpha.5 → 0.4.3-alpha.8

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
@@ -301,7 +301,7 @@ function signalContentsToUserMessages(contents, metadata) {
301
301
  var toUIMessage = ({ chunk, conversation, metadata }) => {
302
302
  const result = [...conversation];
303
303
  if (chunk.type.startsWith("data-")) {
304
- if (chunk.type === "data-user-message" && "data" in chunk && chunk.data?.type === "user-message") {
304
+ if (chunk.type === "data-user-message" && "data" in chunk && (chunk.data?.type === "user-message" || chunk.data?.type === "user")) {
305
305
  const signalId = chunk.data.id;
306
306
  if (typeof signalId === "string" && result.some((message) => message.id === signalId)) {
307
307
  return result;
@@ -2020,6 +2020,27 @@ function convertSignalDataToBase64String(content) {
2020
2020
  }
2021
2021
 
2022
2022
  // src/agent/hooks.ts
2023
+ var extractPendingToolApprovalIdsFromMessages = (messages) => {
2024
+ const pendingToolApprovalIds = /* @__PURE__ */ new Set();
2025
+ for (const message of messages) {
2026
+ const metadata = message.metadata;
2027
+ const metadataSources = [
2028
+ metadata?.pendingToolApprovals,
2029
+ metadata?.requireApprovalMetadata,
2030
+ metadata?.suspendedTools
2031
+ ];
2032
+ for (const source of metadataSources) {
2033
+ if (!source || typeof source !== "object") continue;
2034
+ for (const suspensionData of Object.values(source)) {
2035
+ const toolCallId = suspensionData?.toolCallId;
2036
+ if (typeof toolCallId === "string" && toolCallId.length > 0) {
2037
+ pendingToolApprovalIds.add(toolCallId);
2038
+ }
2039
+ }
2040
+ }
2041
+ }
2042
+ return pendingToolApprovalIds;
2043
+ };
2023
2044
  var isThreadSignalUnsupportedError = (error) => {
2024
2045
  const candidate = error;
2025
2046
  const status = candidate?.status;
@@ -2048,17 +2069,24 @@ var useChat = ({
2048
2069
  const _requestContext = useRef(propsRequestContext);
2049
2070
  const _streamAbortRef = useRef(null);
2050
2071
  const _threadSubscriptionAbortRef = useRef(null);
2072
+ const _threadSubscriptionRef = useRef(
2073
+ null
2074
+ );
2051
2075
  const _threadSubscriptionKeyRef = useRef(void 0);
2052
2076
  const _threadSubscriptionPromiseRef = useRef(null);
2053
2077
  const _threadSignalsUnsupportedRef = useRef(false);
2054
2078
  const [messages, setMessages] = useState([]);
2055
2079
  const [toolCallApprovals, setToolCallApprovals] = useState({});
2056
2080
  const [networkToolCallApprovals, setNetworkToolCallApprovals] = useState({});
2081
+ const pendingToolApprovalIdsRef = useRef(/* @__PURE__ */ new Set());
2082
+ const [isAwaitingToolApproval, setIsAwaitingToolApproval] = useState(false);
2057
2083
  const baseClient = useMastraClient();
2058
2084
  const [isRunning, setIsRunning] = useState(false);
2059
2085
  useEffect(() => {
2060
2086
  const formattedMessages = resolveInitialMessages(initialMessages || []);
2061
2087
  setMessages(formattedMessages);
2088
+ pendingToolApprovalIdsRef.current = extractPendingToolApprovalIdsFromMessages(formattedMessages);
2089
+ setIsAwaitingToolApproval(pendingToolApprovalIdsRef.current.size > 0);
2062
2090
  _currentRunId.current = extractRunIdFromMessages(formattedMessages);
2063
2091
  }, [initialMessages]);
2064
2092
  useEffect(() => {
@@ -2114,7 +2142,13 @@ var useChat = ({
2114
2142
  return preview || "Attachment";
2115
2143
  };
2116
2144
  const closeThreadSubscription = useCallback(() => {
2117
- _threadSubscriptionAbortRef.current?.abort();
2145
+ const subscription = _threadSubscriptionRef.current;
2146
+ if (subscription?.unsubscribe) {
2147
+ subscription.unsubscribe();
2148
+ } else {
2149
+ _threadSubscriptionAbortRef.current?.abort();
2150
+ }
2151
+ _threadSubscriptionRef.current = null;
2118
2152
  _threadSubscriptionAbortRef.current = null;
2119
2153
  _threadSubscriptionKeyRef.current = void 0;
2120
2154
  _threadSubscriptionPromiseRef.current = null;
@@ -2131,7 +2165,17 @@ var useChat = ({
2131
2165
  _currentRunId.current = chunk.runId;
2132
2166
  }
2133
2167
  }
2168
+ if (chunk.type === "tool-call-approval" || chunk.type === "tool-call-suspended") {
2169
+ const toolCallId = chunk.payload?.toolCallId;
2170
+ if (typeof toolCallId === "string") {
2171
+ pendingToolApprovalIdsRef.current.add(toolCallId);
2172
+ setIsAwaitingToolApproval(true);
2173
+ }
2174
+ setIsRunning(false);
2175
+ }
2134
2176
  if (chunk.type === "finish" || chunk.type === "abort" || chunk.type === "error") {
2177
+ pendingToolApprovalIdsRef.current.clear();
2178
+ setIsAwaitingToolApproval(false);
2135
2179
  setIsRunning(false);
2136
2180
  }
2137
2181
  void (onChunk ?? _onChunk.current)?.(chunk);
@@ -2145,7 +2189,7 @@ var useChat = ({
2145
2189
  await _threadSubscriptionPromiseRef.current;
2146
2190
  return;
2147
2191
  }
2148
- _threadSubscriptionAbortRef.current?.abort();
2192
+ closeThreadSubscription();
2149
2193
  const subscriptionAbort = new AbortController();
2150
2194
  _threadSubscriptionAbortRef.current = subscriptionAbort;
2151
2195
  _threadSubscriptionKeyRef.current = subscriptionKey;
@@ -2155,7 +2199,17 @@ var useChat = ({
2155
2199
  });
2156
2200
  const subscriptionAgent = clientWithAbort.getAgent(agentId);
2157
2201
  _threadSubscriptionPromiseRef.current = subscriptionAgent.subscribeToThread({ resourceId: resourceId2, threadId: threadId2 }).then((response) => {
2158
- void response.processDataStream({
2202
+ const subscription = response;
2203
+ if (_threadSubscriptionAbortRef.current !== subscriptionAbort) {
2204
+ if (subscription.unsubscribe) {
2205
+ subscription.unsubscribe();
2206
+ } else {
2207
+ subscriptionAbort.abort();
2208
+ }
2209
+ return;
2210
+ }
2211
+ _threadSubscriptionRef.current = subscription;
2212
+ void subscription.processDataStream({
2159
2213
  onChunk: (chunk) => processStreamChunk(chunk)
2160
2214
  }).catch((error) => {
2161
2215
  if (error.name !== "AbortError") {
@@ -2163,6 +2217,9 @@ var useChat = ({
2163
2217
  setIsRunning(false);
2164
2218
  }
2165
2219
  }).finally(() => {
2220
+ if (_threadSubscriptionRef.current === subscription) {
2221
+ _threadSubscriptionRef.current = null;
2222
+ }
2166
2223
  if (_threadSubscriptionAbortRef.current === subscriptionAbort) {
2167
2224
  _threadSubscriptionAbortRef.current = null;
2168
2225
  _threadSubscriptionKeyRef.current = void 0;
@@ -2173,6 +2230,7 @@ var useChat = ({
2173
2230
  if (isThreadSignalUnsupportedError(error)) {
2174
2231
  markThreadSignalsUnsupported();
2175
2232
  if (_threadSubscriptionAbortRef.current === subscriptionAbort) {
2233
+ _threadSubscriptionRef.current = null;
2176
2234
  _threadSubscriptionAbortRef.current = null;
2177
2235
  _threadSubscriptionKeyRef.current = void 0;
2178
2236
  _threadSubscriptionPromiseRef.current = null;
@@ -2187,7 +2245,7 @@ var useChat = ({
2187
2245
  });
2188
2246
  await _threadSubscriptionPromiseRef.current;
2189
2247
  },
2190
- [agentId, baseClient, markThreadSignalsUnsupported, processStreamChunk]
2248
+ [agentId, baseClient, closeThreadSubscription, markThreadSignalsUnsupported, processStreamChunk]
2191
2249
  );
2192
2250
  useEffect(() => {
2193
2251
  _threadSignalsUnsupportedRef.current = false;
@@ -2408,6 +2466,9 @@ var useChat = ({
2408
2466
  }
2409
2467
  }
2410
2468
  });
2469
+ if (pendingToolApprovalIdsRef.current.size > 0) {
2470
+ setIsRunning(false);
2471
+ }
2411
2472
  } catch (error) {
2412
2473
  onSignalEcho?.(resolvedSignalId);
2413
2474
  if (isThreadSignalUnsupportedError(error)) {
@@ -2471,8 +2532,14 @@ var useChat = ({
2471
2532
  const handleCancelRun = () => {
2472
2533
  _streamAbortRef.current?.abort();
2473
2534
  _streamAbortRef.current = null;
2535
+ const threadSubscription = _threadSubscriptionRef.current;
2536
+ void Promise.resolve(threadSubscription?.abort?.()).catch((error) => {
2537
+ console.error("[useChat] Failed to abort thread subscription", error);
2538
+ });
2474
2539
  closeThreadSubscription();
2475
2540
  setMessages((prev) => finishStreamingAssistantMessage(prev));
2541
+ pendingToolApprovalIdsRef.current.clear();
2542
+ setIsAwaitingToolApproval(false);
2476
2543
  setIsRunning(false);
2477
2544
  _currentRunId.current = void 0;
2478
2545
  _onChunk.current = void 0;
@@ -2488,18 +2555,37 @@ var useChat = ({
2488
2555
  setIsRunning(true);
2489
2556
  setToolCallApprovals((prev) => ({ ...prev, [toolCallId]: { status: "approved" } }));
2490
2557
  const agent = baseClient.getAgent(agentId);
2558
+ if (_threadSubscriptionKeyRef.current && threadId) {
2559
+ try {
2560
+ await agent.sendToolApproval({
2561
+ resourceId: resourceId || agentId,
2562
+ threadId,
2563
+ toolCallId,
2564
+ approved: true,
2565
+ requestContext: _requestContext.current
2566
+ });
2567
+ pendingToolApprovalIdsRef.current.delete(toolCallId);
2568
+ setIsAwaitingToolApproval(pendingToolApprovalIdsRef.current.size > 0);
2569
+ setIsRunning(false);
2570
+ } catch (error) {
2571
+ setToolCallApprovals((prev) => {
2572
+ const next = { ...prev };
2573
+ delete next[toolCallId];
2574
+ return next;
2575
+ });
2576
+ setIsRunning(false);
2577
+ throw error;
2578
+ }
2579
+ return;
2580
+ }
2491
2581
  const response = await agent.approveToolCall({
2492
2582
  runId: currentRunId,
2493
2583
  toolCallId,
2494
2584
  requestContext: _requestContext.current
2495
2585
  });
2496
- if (_threadSubscriptionKeyRef.current) {
2497
- return;
2498
- }
2499
2586
  await response.processDataStream({
2500
2587
  onChunk: async (chunk) => {
2501
- setMessages((prev) => toUIMessage({ chunk, conversation: prev, metadata: { mode: "stream" } }));
2502
- void (onChunk ?? _onChunk.current)?.(chunk);
2588
+ await processStreamChunk(chunk, onChunk);
2503
2589
  }
2504
2590
  });
2505
2591
  setIsRunning(false);
@@ -2512,18 +2598,37 @@ var useChat = ({
2512
2598
  setIsRunning(true);
2513
2599
  setToolCallApprovals((prev) => ({ ...prev, [toolCallId]: { status: "declined" } }));
2514
2600
  const agent = baseClient.getAgent(agentId);
2601
+ if (_threadSubscriptionKeyRef.current && threadId) {
2602
+ try {
2603
+ await agent.sendToolApproval({
2604
+ resourceId: resourceId || agentId,
2605
+ threadId,
2606
+ toolCallId,
2607
+ approved: false,
2608
+ requestContext: _requestContext.current
2609
+ });
2610
+ pendingToolApprovalIdsRef.current.delete(toolCallId);
2611
+ setIsAwaitingToolApproval(pendingToolApprovalIdsRef.current.size > 0);
2612
+ setIsRunning(false);
2613
+ } catch (error) {
2614
+ setToolCallApprovals((prev) => {
2615
+ const next = { ...prev };
2616
+ delete next[toolCallId];
2617
+ return next;
2618
+ });
2619
+ setIsRunning(false);
2620
+ throw error;
2621
+ }
2622
+ return;
2623
+ }
2515
2624
  const response = await agent.declineToolCall({
2516
2625
  runId: currentRunId,
2517
2626
  toolCallId,
2518
2627
  requestContext: _requestContext.current
2519
2628
  });
2520
- if (_threadSubscriptionKeyRef.current) {
2521
- return;
2522
- }
2523
2629
  await response.processDataStream({
2524
2630
  onChunk: async (chunk) => {
2525
- setMessages((prev) => toUIMessage({ chunk, conversation: prev, metadata: { mode: "stream" } }));
2526
- void (onChunk ?? _onChunk.current)?.(chunk);
2631
+ await processStreamChunk(chunk, onChunk);
2527
2632
  }
2528
2633
  });
2529
2634
  setIsRunning(false);
@@ -2653,6 +2758,7 @@ var useChat = ({
2653
2758
  setMessages,
2654
2759
  sendMessage,
2655
2760
  isRunning,
2761
+ isAwaitingToolApproval,
2656
2762
  messages,
2657
2763
  approveToolCall,
2658
2764
  declineToolCall,