@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/CHANGELOG.md CHANGED
@@ -1,5 +1,46 @@
1
1
  # @mastra/react
2
2
 
3
+ ## 0.4.3-alpha.8
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [[`0c1ed1d`](https://github.com/mastra-ai/mastra/commit/0c1ed1d00c7d87b5ac99ca95896211a2fa9189fa), [`849efb9`](https://github.com/mastra-ai/mastra/commit/849efb9fca6dc976589c1f90a303fea618769109)]:
8
+ - @mastra/core@1.38.0-alpha.8
9
+ - @mastra/client-js@1.22.0-alpha.8
10
+
11
+ ## 0.4.3-alpha.7
12
+
13
+ ### Patch Changes
14
+
15
+ - Updated dependencies:
16
+ - @mastra/core@1.38.0-alpha.7
17
+ - @mastra/client-js@1.22.0-alpha.7
18
+
19
+ ## 0.4.3-alpha.6
20
+
21
+ ### Patch Changes
22
+
23
+ - Fixed canonical user signal echoes so messages sent through the agent-signals path appear in chat history when they move from pending to active. ([#17309](https://github.com/mastra-ai/mastra/pull/17309))
24
+
25
+ - Separated thread subscription cleanup from active-run aborts so closing or switching a listener only unsubscribes that listener, while explicit cancel still aborts the active run. ([#17310](https://github.com/mastra-ai/mastra/pull/17310))
26
+
27
+ - Added subscription-native tool approval APIs so approving or declining a tool call resumes through the active thread subscription instead of requiring a separate continuation stream. New messages are queued while a tool approval is waiting, preventing overlapping runs from duplicating approval requests. ([#17311](https://github.com/mastra-ai/mastra/pull/17311))
28
+
29
+ ```ts
30
+ await agent.sendToolApproval({
31
+ resourceId: 'user-123',
32
+ threadId: 'thread-123',
33
+ toolCallId: 'tool-call-123',
34
+ approved: true,
35
+ });
36
+ ```
37
+
38
+ - Enabled Studio via the CLI and deployers to use agent signal subscriptions by default while preserving `MASTRA_AGENT_SIGNALS=false`, `enableThreadSignals: false`, and explicit legacy Stream as opt-outs. The React `useChat()` hook remains opt-in for SDK consumers via `enableThreadSignals: true`. ([#17313](https://github.com/mastra-ai/mastra/pull/17313))
39
+
40
+ - Updated dependencies [[`bb3fce8`](https://github.com/mastra-ai/mastra/commit/bb3fce8f8d80079170c0f98cb2efbb29ae34375d), [`19a8658`](https://github.com/mastra-ai/mastra/commit/19a86589c788ef48bb6c1b0612cc82a201857379), [`a659a77`](https://github.com/mastra-ai/mastra/commit/a659a779bdebe3a52a518c56d2260592d0240fe0), [`3332be9`](https://github.com/mastra-ai/mastra/commit/3332be9701ecd77aba840959d9a1d1ce7aef02d3)]:
41
+ - @mastra/client-js@1.22.0-alpha.6
42
+ - @mastra/core@1.38.0-alpha.6
43
+
3
44
  ## 0.4.3-alpha.5
4
45
 
5
46
  ### Patch Changes
@@ -23,9 +23,8 @@ export interface MastraChatProps {
23
23
  onSignalEcho?: (signalId: string) => void;
24
24
  onThreadSignalsUnsupported?: () => void;
25
25
  /**
26
- * Opt into the agent-signals streaming path (sendSignal + subscribeToThread).
27
- * Defaults to `false` so consumers stay on the legacy `streamUntilIdle` route
28
- * unless they explicitly enable the signals path.
26
+ * Use the agent-signals streaming path (sendSignal + subscribeToThread).
27
+ * Defaults to `false`; set to `true` to opt into thread signals.
29
28
  */
30
29
  enableThreadSignals?: boolean;
31
30
  }
@@ -65,6 +64,7 @@ export declare const useChat: ({ agentId, resourceId, threadId, initialMessages,
65
64
  setMessages: import("react").Dispatch<import("react").SetStateAction<MastraUIMessage[]>>;
66
65
  sendMessage: ({ mode, ...args }: SendMessageArgs) => Promise<void>;
67
66
  isRunning: boolean;
67
+ isAwaitingToolApproval: boolean;
68
68
  messages: MastraUIMessage[];
69
69
  approveToolCall: (toolCallId: string) => Promise<void>;
70
70
  declineToolCall: (toolCallId: string) => Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/agent/hooks.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/agent/hooks.ts"],"names":[],"mappings":"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"}
package/dist/index.cjs CHANGED
@@ -303,7 +303,7 @@ function signalContentsToUserMessages(contents, metadata) {
303
303
  var toUIMessage = ({ chunk, conversation, metadata }) => {
304
304
  const result = [...conversation];
305
305
  if (chunk.type.startsWith("data-")) {
306
- if (chunk.type === "data-user-message" && "data" in chunk && chunk.data?.type === "user-message") {
306
+ if (chunk.type === "data-user-message" && "data" in chunk && (chunk.data?.type === "user-message" || chunk.data?.type === "user")) {
307
307
  const signalId = chunk.data.id;
308
308
  if (typeof signalId === "string" && result.some((message) => message.id === signalId)) {
309
309
  return result;
@@ -2022,6 +2022,27 @@ function convertSignalDataToBase64String(content) {
2022
2022
  }
2023
2023
 
2024
2024
  // src/agent/hooks.ts
2025
+ var extractPendingToolApprovalIdsFromMessages = (messages) => {
2026
+ const pendingToolApprovalIds = /* @__PURE__ */ new Set();
2027
+ for (const message of messages) {
2028
+ const metadata = message.metadata;
2029
+ const metadataSources = [
2030
+ metadata?.pendingToolApprovals,
2031
+ metadata?.requireApprovalMetadata,
2032
+ metadata?.suspendedTools
2033
+ ];
2034
+ for (const source of metadataSources) {
2035
+ if (!source || typeof source !== "object") continue;
2036
+ for (const suspensionData of Object.values(source)) {
2037
+ const toolCallId = suspensionData?.toolCallId;
2038
+ if (typeof toolCallId === "string" && toolCallId.length > 0) {
2039
+ pendingToolApprovalIds.add(toolCallId);
2040
+ }
2041
+ }
2042
+ }
2043
+ }
2044
+ return pendingToolApprovalIds;
2045
+ };
2025
2046
  var isThreadSignalUnsupportedError = (error) => {
2026
2047
  const candidate = error;
2027
2048
  const status = candidate?.status;
@@ -2050,17 +2071,24 @@ var useChat = ({
2050
2071
  const _requestContext = react.useRef(propsRequestContext);
2051
2072
  const _streamAbortRef = react.useRef(null);
2052
2073
  const _threadSubscriptionAbortRef = react.useRef(null);
2074
+ const _threadSubscriptionRef = react.useRef(
2075
+ null
2076
+ );
2053
2077
  const _threadSubscriptionKeyRef = react.useRef(void 0);
2054
2078
  const _threadSubscriptionPromiseRef = react.useRef(null);
2055
2079
  const _threadSignalsUnsupportedRef = react.useRef(false);
2056
2080
  const [messages, setMessages] = react.useState([]);
2057
2081
  const [toolCallApprovals, setToolCallApprovals] = react.useState({});
2058
2082
  const [networkToolCallApprovals, setNetworkToolCallApprovals] = react.useState({});
2083
+ const pendingToolApprovalIdsRef = react.useRef(/* @__PURE__ */ new Set());
2084
+ const [isAwaitingToolApproval, setIsAwaitingToolApproval] = react.useState(false);
2059
2085
  const baseClient = useMastraClient();
2060
2086
  const [isRunning, setIsRunning] = react.useState(false);
2061
2087
  react.useEffect(() => {
2062
2088
  const formattedMessages = resolveInitialMessages(initialMessages || []);
2063
2089
  setMessages(formattedMessages);
2090
+ pendingToolApprovalIdsRef.current = extractPendingToolApprovalIdsFromMessages(formattedMessages);
2091
+ setIsAwaitingToolApproval(pendingToolApprovalIdsRef.current.size > 0);
2064
2092
  _currentRunId.current = extractRunIdFromMessages(formattedMessages);
2065
2093
  }, [initialMessages]);
2066
2094
  react.useEffect(() => {
@@ -2116,7 +2144,13 @@ var useChat = ({
2116
2144
  return preview || "Attachment";
2117
2145
  };
2118
2146
  const closeThreadSubscription = react.useCallback(() => {
2119
- _threadSubscriptionAbortRef.current?.abort();
2147
+ const subscription = _threadSubscriptionRef.current;
2148
+ if (subscription?.unsubscribe) {
2149
+ subscription.unsubscribe();
2150
+ } else {
2151
+ _threadSubscriptionAbortRef.current?.abort();
2152
+ }
2153
+ _threadSubscriptionRef.current = null;
2120
2154
  _threadSubscriptionAbortRef.current = null;
2121
2155
  _threadSubscriptionKeyRef.current = void 0;
2122
2156
  _threadSubscriptionPromiseRef.current = null;
@@ -2133,7 +2167,17 @@ var useChat = ({
2133
2167
  _currentRunId.current = chunk.runId;
2134
2168
  }
2135
2169
  }
2170
+ if (chunk.type === "tool-call-approval" || chunk.type === "tool-call-suspended") {
2171
+ const toolCallId = chunk.payload?.toolCallId;
2172
+ if (typeof toolCallId === "string") {
2173
+ pendingToolApprovalIdsRef.current.add(toolCallId);
2174
+ setIsAwaitingToolApproval(true);
2175
+ }
2176
+ setIsRunning(false);
2177
+ }
2136
2178
  if (chunk.type === "finish" || chunk.type === "abort" || chunk.type === "error") {
2179
+ pendingToolApprovalIdsRef.current.clear();
2180
+ setIsAwaitingToolApproval(false);
2137
2181
  setIsRunning(false);
2138
2182
  }
2139
2183
  void (onChunk ?? _onChunk.current)?.(chunk);
@@ -2147,7 +2191,7 @@ var useChat = ({
2147
2191
  await _threadSubscriptionPromiseRef.current;
2148
2192
  return;
2149
2193
  }
2150
- _threadSubscriptionAbortRef.current?.abort();
2194
+ closeThreadSubscription();
2151
2195
  const subscriptionAbort = new AbortController();
2152
2196
  _threadSubscriptionAbortRef.current = subscriptionAbort;
2153
2197
  _threadSubscriptionKeyRef.current = subscriptionKey;
@@ -2157,7 +2201,17 @@ var useChat = ({
2157
2201
  });
2158
2202
  const subscriptionAgent = clientWithAbort.getAgent(agentId);
2159
2203
  _threadSubscriptionPromiseRef.current = subscriptionAgent.subscribeToThread({ resourceId: resourceId2, threadId: threadId2 }).then((response) => {
2160
- void response.processDataStream({
2204
+ const subscription = response;
2205
+ if (_threadSubscriptionAbortRef.current !== subscriptionAbort) {
2206
+ if (subscription.unsubscribe) {
2207
+ subscription.unsubscribe();
2208
+ } else {
2209
+ subscriptionAbort.abort();
2210
+ }
2211
+ return;
2212
+ }
2213
+ _threadSubscriptionRef.current = subscription;
2214
+ void subscription.processDataStream({
2161
2215
  onChunk: (chunk) => processStreamChunk(chunk)
2162
2216
  }).catch((error) => {
2163
2217
  if (error.name !== "AbortError") {
@@ -2165,6 +2219,9 @@ var useChat = ({
2165
2219
  setIsRunning(false);
2166
2220
  }
2167
2221
  }).finally(() => {
2222
+ if (_threadSubscriptionRef.current === subscription) {
2223
+ _threadSubscriptionRef.current = null;
2224
+ }
2168
2225
  if (_threadSubscriptionAbortRef.current === subscriptionAbort) {
2169
2226
  _threadSubscriptionAbortRef.current = null;
2170
2227
  _threadSubscriptionKeyRef.current = void 0;
@@ -2175,6 +2232,7 @@ var useChat = ({
2175
2232
  if (isThreadSignalUnsupportedError(error)) {
2176
2233
  markThreadSignalsUnsupported();
2177
2234
  if (_threadSubscriptionAbortRef.current === subscriptionAbort) {
2235
+ _threadSubscriptionRef.current = null;
2178
2236
  _threadSubscriptionAbortRef.current = null;
2179
2237
  _threadSubscriptionKeyRef.current = void 0;
2180
2238
  _threadSubscriptionPromiseRef.current = null;
@@ -2189,7 +2247,7 @@ var useChat = ({
2189
2247
  });
2190
2248
  await _threadSubscriptionPromiseRef.current;
2191
2249
  },
2192
- [agentId, baseClient, markThreadSignalsUnsupported, processStreamChunk]
2250
+ [agentId, baseClient, closeThreadSubscription, markThreadSignalsUnsupported, processStreamChunk]
2193
2251
  );
2194
2252
  react.useEffect(() => {
2195
2253
  _threadSignalsUnsupportedRef.current = false;
@@ -2410,6 +2468,9 @@ var useChat = ({
2410
2468
  }
2411
2469
  }
2412
2470
  });
2471
+ if (pendingToolApprovalIdsRef.current.size > 0) {
2472
+ setIsRunning(false);
2473
+ }
2413
2474
  } catch (error) {
2414
2475
  onSignalEcho?.(resolvedSignalId);
2415
2476
  if (isThreadSignalUnsupportedError(error)) {
@@ -2473,8 +2534,14 @@ var useChat = ({
2473
2534
  const handleCancelRun = () => {
2474
2535
  _streamAbortRef.current?.abort();
2475
2536
  _streamAbortRef.current = null;
2537
+ const threadSubscription = _threadSubscriptionRef.current;
2538
+ void Promise.resolve(threadSubscription?.abort?.()).catch((error) => {
2539
+ console.error("[useChat] Failed to abort thread subscription", error);
2540
+ });
2476
2541
  closeThreadSubscription();
2477
2542
  setMessages((prev) => finishStreamingAssistantMessage(prev));
2543
+ pendingToolApprovalIdsRef.current.clear();
2544
+ setIsAwaitingToolApproval(false);
2478
2545
  setIsRunning(false);
2479
2546
  _currentRunId.current = void 0;
2480
2547
  _onChunk.current = void 0;
@@ -2490,18 +2557,37 @@ var useChat = ({
2490
2557
  setIsRunning(true);
2491
2558
  setToolCallApprovals((prev) => ({ ...prev, [toolCallId]: { status: "approved" } }));
2492
2559
  const agent = baseClient.getAgent(agentId);
2560
+ if (_threadSubscriptionKeyRef.current && threadId) {
2561
+ try {
2562
+ await agent.sendToolApproval({
2563
+ resourceId: resourceId || agentId,
2564
+ threadId,
2565
+ toolCallId,
2566
+ approved: true,
2567
+ requestContext: _requestContext.current
2568
+ });
2569
+ pendingToolApprovalIdsRef.current.delete(toolCallId);
2570
+ setIsAwaitingToolApproval(pendingToolApprovalIdsRef.current.size > 0);
2571
+ setIsRunning(false);
2572
+ } catch (error) {
2573
+ setToolCallApprovals((prev) => {
2574
+ const next = { ...prev };
2575
+ delete next[toolCallId];
2576
+ return next;
2577
+ });
2578
+ setIsRunning(false);
2579
+ throw error;
2580
+ }
2581
+ return;
2582
+ }
2493
2583
  const response = await agent.approveToolCall({
2494
2584
  runId: currentRunId,
2495
2585
  toolCallId,
2496
2586
  requestContext: _requestContext.current
2497
2587
  });
2498
- if (_threadSubscriptionKeyRef.current) {
2499
- return;
2500
- }
2501
2588
  await response.processDataStream({
2502
2589
  onChunk: async (chunk) => {
2503
- setMessages((prev) => toUIMessage({ chunk, conversation: prev, metadata: { mode: "stream" } }));
2504
- void (onChunk ?? _onChunk.current)?.(chunk);
2590
+ await processStreamChunk(chunk, onChunk);
2505
2591
  }
2506
2592
  });
2507
2593
  setIsRunning(false);
@@ -2514,18 +2600,37 @@ var useChat = ({
2514
2600
  setIsRunning(true);
2515
2601
  setToolCallApprovals((prev) => ({ ...prev, [toolCallId]: { status: "declined" } }));
2516
2602
  const agent = baseClient.getAgent(agentId);
2603
+ if (_threadSubscriptionKeyRef.current && threadId) {
2604
+ try {
2605
+ await agent.sendToolApproval({
2606
+ resourceId: resourceId || agentId,
2607
+ threadId,
2608
+ toolCallId,
2609
+ approved: false,
2610
+ requestContext: _requestContext.current
2611
+ });
2612
+ pendingToolApprovalIdsRef.current.delete(toolCallId);
2613
+ setIsAwaitingToolApproval(pendingToolApprovalIdsRef.current.size > 0);
2614
+ setIsRunning(false);
2615
+ } catch (error) {
2616
+ setToolCallApprovals((prev) => {
2617
+ const next = { ...prev };
2618
+ delete next[toolCallId];
2619
+ return next;
2620
+ });
2621
+ setIsRunning(false);
2622
+ throw error;
2623
+ }
2624
+ return;
2625
+ }
2517
2626
  const response = await agent.declineToolCall({
2518
2627
  runId: currentRunId,
2519
2628
  toolCallId,
2520
2629
  requestContext: _requestContext.current
2521
2630
  });
2522
- if (_threadSubscriptionKeyRef.current) {
2523
- return;
2524
- }
2525
2631
  await response.processDataStream({
2526
2632
  onChunk: async (chunk) => {
2527
- setMessages((prev) => toUIMessage({ chunk, conversation: prev, metadata: { mode: "stream" } }));
2528
- void (onChunk ?? _onChunk.current)?.(chunk);
2633
+ await processStreamChunk(chunk, onChunk);
2529
2634
  }
2530
2635
  });
2531
2636
  setIsRunning(false);
@@ -2655,6 +2760,7 @@ var useChat = ({
2655
2760
  setMessages,
2656
2761
  sendMessage,
2657
2762
  isRunning,
2763
+ isAwaitingToolApproval,
2658
2764
  messages,
2659
2765
  approveToolCall,
2660
2766
  declineToolCall,