@mastra/react 0.0.0-error-handler-fix-20251020202607 → 0.0.0-esbuild-bundle-worker-20260807182016

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.
Files changed (142) hide show
  1. package/CHANGELOG.md +4402 -3
  2. package/LICENSE.md +15 -0
  3. package/dist/agent/extract-tasks.d.ts +10 -0
  4. package/dist/agent/extract-tasks.d.ts.map +1 -0
  5. package/dist/agent/extractRunIdFromMessages.d.ts +10 -0
  6. package/dist/agent/extractRunIdFromMessages.d.ts.map +1 -0
  7. package/dist/agent/hooks.d.ts +98 -0
  8. package/dist/agent/hooks.d.ts.map +1 -0
  9. package/dist/agent/signal-data.d.ts +5 -0
  10. package/dist/agent/signal-data.d.ts.map +1 -0
  11. package/dist/{src/agent → agent}/types.d.ts +7 -1
  12. package/dist/agent/types.d.ts.map +1 -0
  13. package/dist/index.cjs +3314 -1506
  14. package/dist/index.cjs.map +1 -1
  15. package/dist/index.d.ts +12 -2
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +3243 -1444
  18. package/dist/index.js.map +1 -1
  19. package/dist/lib/mastra-db/accumulator.d.ts +48 -0
  20. package/dist/lib/mastra-db/accumulator.d.ts.map +1 -0
  21. package/dist/lib/mastra-db/formatCompletionFeedback.d.ts +19 -0
  22. package/dist/lib/mastra-db/formatCompletionFeedback.d.ts.map +1 -0
  23. package/dist/lib/mastra-db/fromCoreUserMessage.d.ts +14 -0
  24. package/dist/lib/mastra-db/fromCoreUserMessage.d.ts.map +1 -0
  25. package/dist/lib/mastra-db/index.d.ts +6 -0
  26. package/dist/lib/mastra-db/index.d.ts.map +1 -0
  27. package/dist/lib/mastra-db/types.d.ts +171 -0
  28. package/dist/lib/mastra-db/types.d.ts.map +1 -0
  29. package/dist/lib/use-mutation.d.ts +28 -0
  30. package/dist/lib/use-mutation.d.ts.map +1 -0
  31. package/dist/mastra-client-context.d.ts +26 -0
  32. package/dist/mastra-client-context.d.ts.map +1 -0
  33. package/dist/mastra-react-provider.d.ts +5 -0
  34. package/dist/mastra-react-provider.d.ts.map +1 -0
  35. package/dist/react.css +74 -267
  36. package/dist/{src/ui → ui}/Code/Code.d.ts +5 -3
  37. package/dist/ui/Code/Code.d.ts.map +1 -0
  38. package/dist/ui/Code/highlight.d.ts +4 -0
  39. package/dist/ui/Code/highlight.d.ts.map +1 -0
  40. package/dist/ui/Code/index.d.ts +2 -0
  41. package/dist/ui/Code/index.d.ts.map +1 -0
  42. package/dist/{src/ui → ui}/Entity/Entity.d.ts +6 -5
  43. package/dist/ui/Entity/Entity.d.ts.map +1 -0
  44. package/dist/{src/ui → ui}/Entity/Entry.d.ts +4 -3
  45. package/dist/ui/Entity/Entry.d.ts.map +1 -0
  46. package/dist/{src/ui → ui}/Entity/ToolApproval.d.ts +6 -5
  47. package/dist/ui/Entity/ToolApproval.d.ts.map +1 -0
  48. package/dist/{src/ui → ui}/Entity/context.d.ts +4 -3
  49. package/dist/ui/Entity/context.d.ts.map +1 -0
  50. package/dist/ui/Entity/index.d.ts +5 -0
  51. package/dist/ui/Entity/index.d.ts.map +1 -0
  52. package/dist/{src/ui → ui}/Entity/types.d.ts +1 -0
  53. package/dist/ui/Entity/types.d.ts.map +1 -0
  54. package/dist/{src/ui → ui}/Icon/Icon.d.ts +3 -2
  55. package/dist/ui/Icon/Icon.d.ts.map +1 -0
  56. package/dist/ui/Icon/index.d.ts +2 -0
  57. package/dist/ui/Icon/index.d.ts.map +1 -0
  58. package/dist/{src/ui → ui}/IconButton/IconButton.d.ts +3 -2
  59. package/dist/ui/IconButton/IconButton.d.ts.map +1 -0
  60. package/dist/ui/IconButton/index.d.ts +2 -0
  61. package/dist/ui/IconButton/index.d.ts.map +1 -0
  62. package/dist/ui/Icons/AgentIcon.d.ts +3 -0
  63. package/dist/ui/Icons/AgentIcon.d.ts.map +1 -0
  64. package/dist/ui/Icons/ToolsIcon.d.ts +3 -0
  65. package/dist/ui/Icons/ToolsIcon.d.ts.map +1 -0
  66. package/dist/ui/Icons/WorkflowIcon.d.ts +3 -0
  67. package/dist/ui/Icons/WorkflowIcon.d.ts.map +1 -0
  68. package/dist/ui/Icons/index.d.ts +4 -0
  69. package/dist/ui/Icons/index.d.ts.map +1 -0
  70. package/dist/{src/ui → ui}/Message/Message.d.ts +11 -10
  71. package/dist/ui/Message/Message.d.ts.map +1 -0
  72. package/dist/ui/Message/index.d.ts +2 -0
  73. package/dist/ui/Message/index.d.ts.map +1 -0
  74. package/dist/ui/MessageFactory/MessageFactory.d.ts +22 -0
  75. package/dist/ui/MessageFactory/MessageFactory.d.ts.map +1 -0
  76. package/dist/ui/MessageFactory/index.d.ts +3 -0
  77. package/dist/ui/MessageFactory/index.d.ts.map +1 -0
  78. package/dist/ui/MessageFactory/types.d.ts +181 -0
  79. package/dist/ui/MessageFactory/types.d.ts.map +1 -0
  80. package/dist/{src/ui → ui}/Tooltip/Tooltip.d.ts +5 -4
  81. package/dist/ui/Tooltip/Tooltip.d.ts.map +1 -0
  82. package/dist/ui/Tooltip/index.d.ts +2 -0
  83. package/dist/ui/Tooltip/index.d.ts.map +1 -0
  84. package/dist/ui/index.cjs +57 -0
  85. package/dist/ui/index.d.ts +9 -0
  86. package/dist/ui/index.d.ts.map +1 -0
  87. package/dist/ui/index.js +2 -0
  88. package/dist/ui-Bd2cu1B9.js +497 -0
  89. package/dist/ui-Bd2cu1B9.js.map +1 -0
  90. package/dist/ui-DL8WaLmH.cjs +849 -0
  91. package/dist/ui-DL8WaLmH.cjs.map +1 -0
  92. package/dist/voice/index.d.ts +5 -0
  93. package/dist/voice/index.d.ts.map +1 -0
  94. package/dist/voice/play-stream-with-web-audio.d.ts +2 -0
  95. package/dist/voice/play-stream-with-web-audio.d.ts.map +1 -0
  96. package/dist/voice/record-mic-to-file.d.ts +2 -0
  97. package/dist/voice/record-mic-to-file.d.ts.map +1 -0
  98. package/dist/voice/use-speech-recognition.d.ts +20 -0
  99. package/dist/voice/use-speech-recognition.d.ts.map +1 -0
  100. package/dist/workflows/WorkflowStepFactory/WorkflowStepFactory.d.ts +8 -0
  101. package/dist/workflows/WorkflowStepFactory/WorkflowStepFactory.d.ts.map +1 -0
  102. package/dist/workflows/WorkflowStepFactory/index.d.ts +3 -0
  103. package/dist/workflows/WorkflowStepFactory/index.d.ts.map +1 -0
  104. package/dist/workflows/WorkflowStepFactory/types.d.ts +90 -0
  105. package/dist/workflows/WorkflowStepFactory/types.d.ts.map +1 -0
  106. package/dist/workflows/hooks.d.ts +34 -0
  107. package/dist/workflows/hooks.d.ts.map +1 -0
  108. package/dist/workflows/index.d.ts +4 -0
  109. package/dist/workflows/index.d.ts.map +1 -0
  110. package/dist/workflows/types.d.ts +122 -0
  111. package/dist/workflows/types.d.ts.map +1 -0
  112. package/dist/workflows/use-stream-workflow.d.ts +39 -0
  113. package/dist/workflows/use-stream-workflow.d.ts.map +1 -0
  114. package/package.json +52 -30
  115. package/dist/src/agent/hooks.d.ts +0 -46
  116. package/dist/src/index.d.ts +0 -6
  117. package/dist/src/lib/ai-sdk/index.d.ts +0 -3
  118. package/dist/src/lib/ai-sdk/memory/resolveInitialMessages.d.ts +0 -2
  119. package/dist/src/lib/ai-sdk/transformers/AISdkNetworkTransformer.d.ts +0 -10
  120. package/dist/src/lib/ai-sdk/transformers/types.d.ts +0 -10
  121. package/dist/src/lib/ai-sdk/types.d.ts +0 -14
  122. package/dist/src/lib/ai-sdk/utils/toAssistantUIMessage.d.ts +0 -14
  123. package/dist/src/lib/ai-sdk/utils/toAssistantUIMessage.test.d.ts +0 -1
  124. package/dist/src/lib/ai-sdk/utils/toUIMessage.d.ts +0 -17
  125. package/dist/src/mastra-client-context.d.ts +0 -10
  126. package/dist/src/mastra-react-provider.d.ts +0 -4
  127. package/dist/src/ui/Code/highlight.d.ts +0 -3
  128. package/dist/src/ui/Code/index.d.ts +0 -1
  129. package/dist/src/ui/Entity/Entity.stories.d.ts +0 -22
  130. package/dist/src/ui/Entity/index.d.ts +0 -4
  131. package/dist/src/ui/Icon/index.d.ts +0 -1
  132. package/dist/src/ui/IconButton/IconButton.stories.d.ts +0 -12
  133. package/dist/src/ui/IconButton/index.d.ts +0 -1
  134. package/dist/src/ui/Icons/AgentIcon.d.ts +0 -2
  135. package/dist/src/ui/Icons/ToolsIcon.d.ts +0 -2
  136. package/dist/src/ui/Icons/WorkflowIcon.d.ts +0 -2
  137. package/dist/src/ui/Icons/index.d.ts +0 -3
  138. package/dist/src/ui/Message/Message.stories.d.ts +0 -13
  139. package/dist/src/ui/Message/index.d.ts +0 -1
  140. package/dist/src/ui/Tooltip/Tooltip.stories.d.ts +0 -12
  141. package/dist/src/ui/Tooltip/index.d.ts +0 -1
  142. package/dist/src/ui/index.d.ts +0 -7
package/dist/index.js CHANGED
@@ -1,1486 +1,3285 @@
1
- import { jsx, jsxs } from 'react/jsx-runtime';
2
- import { createContext, useContext, useState, Fragment, useLayoutEffect, useRef, useEffect } from 'react';
3
- import { MastraClient } from '@mastra/client-js';
4
- import { ChevronDownIcon, CheckIcon, CopyIcon } from 'lucide-react';
5
- import { twMerge } from 'tailwind-merge';
6
- import { toJsxRuntime } from 'hast-util-to-jsx-runtime';
7
- import { codeToHast } from 'shiki/bundle/web';
8
- import { TooltipProvider, Root, TooltipPortal, TooltipContent as TooltipContent$1, TooltipTrigger as TooltipTrigger$1 } from '@radix-ui/react-tooltip';
9
-
1
+ import { $ as useEntity, A as TooltipContentClass, B as ToolApprovalContent, C as CodeBlock, D as IconButtonClass, E as IconButton, F as EntryTitleClass, G as ToolApprovalTitleClass, H as ToolApprovalHeader, I as ToolApproval, J as EntityContent, K as Entity, L as ToolApprovalActions, M as Entry, N as EntryClass, O as Tooltip, P as EntryTitle, Q as EntityTriggerVariantClasses, R as ToolApprovalActionsClass, S as AgentIcon, T as CodeCopyButton, U as ToolApprovalHeaderClass, V as ToolApprovalContentClass, W as ToolApprovalTitle, X as EntityTrigger, Y as EntityContentClass, Z as EntityTriggerClass, _ as MessageUsageValueClass, a as MessageClass, b as WorkflowIcon, c as MessageList, d as MessageStreamingClass, et as Icon, f as MessageUsage, g as MessageUsageValue, h as MessageUsageEntryClass, i as MessageActionsClass, j as TooltipTrigger, k as TooltipContent, l as MessageListClass, m as MessageUsageEntry, n as Message, o as MessageContent, p as MessageUsageClass, q as EntityCaret, r as MessageActions, s as MessageContentClass, t as MessageFactory, tt as IconSizes, u as MessageStreaming, v as MessageUsages, w as CodeBlockClass, x as ToolsIcon, y as MessageUsagesClass, z as ToolApprovalClass } from "./ui-Bd2cu1B9.js";
2
+ import { MastraClient } from "@mastra/client-js";
3
+ import { createContext, memo, useCallback, useContext, useEffect, useRef, useState } from "react";
4
+ import { Fragment as Fragment$1, jsx } from "react/jsx-runtime";
5
+ import { v4 } from "@lukeed/uuid";
6
+ import { AIV5Adapter } from "@mastra/core/agent/message-list";
7
+ //#region src/mastra-client-context.tsx
10
8
  const MastraClientContext = createContext({});
11
- const MastraClientProvider = ({ children, baseUrl, headers }) => {
12
- const client = createMastraClient(baseUrl, headers);
13
- return /* @__PURE__ */ jsx(MastraClientContext.Provider, { value: client, children });
9
+ const MastraClientProvider = ({ children, baseUrl, headers, apiPrefix, credentials = "include", customFetch }) => {
10
+ const client = createMastraClient(baseUrl, headers, apiPrefix, credentials, customFetch);
11
+ return /* @__PURE__ */ jsx(MastraClientContext.Provider, {
12
+ value: client,
13
+ children
14
+ });
14
15
  };
15
16
  const useMastraClient = () => useContext(MastraClientContext);
16
- const createMastraClient = (baseUrl, mastraClientHeaders = {}) => {
17
- return new MastraClient({
18
- baseUrl: baseUrl || "",
19
- // only add the header if the baseUrl is not provided i.e it's a local dev environment
20
- headers: !baseUrl ? { ...mastraClientHeaders, "x-mastra-dev-playground": "true" } : mastraClientHeaders
21
- });
17
+ const IPV4_LOOPBACK_RE = /^127\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/;
18
+ const isIPv4Loopback = (hostname) => {
19
+ const m = IPV4_LOOPBACK_RE.exec(hostname);
20
+ if (!m) return false;
21
+ return +m[1] <= 255 && +m[2] <= 255 && +m[3] <= 255;
22
22
  };
23
-
24
- const MastraReactProvider = ({ children, baseUrl, headers }) => {
25
- return /* @__PURE__ */ jsx(MastraClientProvider, { baseUrl, headers, children });
23
+ const isLocalUrl = (url) => {
24
+ if (!url) return true;
25
+ try {
26
+ const { hostname } = new URL(url);
27
+ return hostname === "localhost" || hostname.endsWith(".localhost") || isIPv4Loopback(hostname) || hostname === "::1" || hostname === "[::1]";
28
+ } catch {
29
+ return false;
30
+ }
26
31
  };
27
-
28
- const mapWorkflowStreamChunkToWatchResult = (prev, chunk) => {
29
- if (chunk.type === "workflow-start") {
30
- return {
31
- input: prev?.input,
32
- status: "running",
33
- steps: prev?.steps || {}
34
- };
35
- }
36
- if (chunk.type === "workflow-canceled") {
37
- return {
38
- ...prev,
39
- status: "canceled"
40
- };
41
- }
42
- if (chunk.type === "workflow-finish") {
43
- const finalStatus = chunk.payload.workflowStatus;
44
- const prevSteps = prev?.steps ?? {};
45
- const lastStep = Object.values(prevSteps).pop();
46
- return {
47
- ...prev,
48
- status: chunk.payload.workflowStatus,
49
- ...finalStatus === "success" && lastStep?.status === "success" ? { result: lastStep?.output } : finalStatus === "failed" && lastStep?.status === "failed" ? { error: lastStep?.error } : {}
50
- };
51
- }
52
- const { stepCallId, stepName, ...newPayload } = chunk.payload ?? {};
53
- const newSteps = {
54
- ...prev?.steps,
55
- [chunk.payload.id]: {
56
- ...prev?.steps?.[chunk.payload.id],
57
- ...newPayload
58
- }
59
- };
60
- if (chunk.type === "workflow-step-start") {
61
- return {
62
- ...prev,
63
- steps: newSteps
64
- };
65
- }
66
- if (chunk.type === "workflow-step-suspended") {
67
- const suspendedStepIds = Object.entries(newSteps).flatMap(
68
- ([stepId, stepResult]) => {
69
- if (stepResult?.status === "suspended") {
70
- const nestedPath = stepResult?.suspendPayload?.__workflow_meta?.path;
71
- return nestedPath ? [[stepId, ...nestedPath]] : [[stepId]];
72
- }
73
- return [];
74
- }
75
- );
76
- return {
77
- ...prev,
78
- status: "suspended",
79
- steps: newSteps,
80
- suspendPayload: chunk.payload.suspendPayload,
81
- suspended: suspendedStepIds
82
- };
83
- }
84
- if (chunk.type === "workflow-step-waiting") {
85
- return {
86
- ...prev,
87
- status: "waiting",
88
- steps: newSteps
89
- };
90
- }
91
- if (chunk.type === "workflow-step-result") {
92
- return {
93
- ...prev,
94
- steps: newSteps
95
- };
96
- }
97
- return prev;
32
+ const createMastraClient = (baseUrl, mastraClientHeaders = {}, apiPrefix, credentials = "include", customFetch) => {
33
+ return new MastraClient({
34
+ baseUrl: baseUrl || "",
35
+ headers: isLocalUrl(baseUrl) ? {
36
+ ...mastraClientHeaders,
37
+ "x-mastra-dev-playground": "true"
38
+ } : mastraClientHeaders,
39
+ apiPrefix,
40
+ credentials,
41
+ fetch: customFetch
42
+ });
98
43
  };
99
- const toUIMessage = ({ chunk, conversation, metadata }) => {
100
- const result = [...conversation];
101
- switch (chunk.type) {
102
- case "tripwire": {
103
- const newMessage = {
104
- id: `tripwire-${chunk.runId + Date.now()}`,
105
- role: "assistant",
106
- parts: [
107
- {
108
- type: "text",
109
- text: chunk.payload.tripwireReason
110
- }
111
- ],
112
- metadata: {
113
- ...metadata,
114
- status: "warning"
115
- }
116
- };
117
- return [...result, newMessage];
118
- }
119
- case "start": {
120
- const newMessage = {
121
- id: `start-${chunk.runId + Date.now()}`,
122
- role: "assistant",
123
- parts: [],
124
- metadata
125
- };
126
- return [...result, newMessage];
127
- }
128
- case "text-start":
129
- case "text-delta": {
130
- const lastMessage = result[result.length - 1];
131
- if (!lastMessage || lastMessage.role !== "assistant") return result;
132
- const parts = [...lastMessage.parts];
133
- let textPartIndex = parts.findIndex((part) => part.type === "text");
134
- if (chunk.type === "text-start") {
135
- if (textPartIndex === -1) {
136
- parts.push({
137
- type: "text",
138
- text: "",
139
- state: "streaming",
140
- providerMetadata: chunk.payload.providerMetadata
141
- });
142
- }
143
- } else {
144
- if (textPartIndex === -1) {
145
- parts.push({
146
- type: "text",
147
- text: chunk.payload.text,
148
- state: "streaming",
149
- providerMetadata: chunk.payload.providerMetadata
150
- });
151
- } else {
152
- const textPart = parts[textPartIndex];
153
- if (textPart.type === "text") {
154
- parts[textPartIndex] = {
155
- ...textPart,
156
- text: textPart.text + chunk.payload.text,
157
- state: "streaming"
158
- };
159
- }
160
- }
161
- }
162
- return [
163
- ...result.slice(0, -1),
164
- {
165
- ...lastMessage,
166
- parts
167
- }
168
- ];
169
- }
170
- case "reasoning-delta": {
171
- const lastMessage = result[result.length - 1];
172
- if (!lastMessage || lastMessage.role !== "assistant") {
173
- const newMessage = {
174
- id: `reasoning-${chunk.runId + Date.now()}`,
175
- role: "assistant",
176
- parts: [
177
- {
178
- type: "reasoning",
179
- text: chunk.payload.text,
180
- state: "streaming",
181
- providerMetadata: chunk.payload.providerMetadata
182
- }
183
- ],
184
- metadata
185
- };
186
- return [...result, newMessage];
187
- }
188
- const parts = [...lastMessage.parts];
189
- let reasoningPartIndex = parts.findIndex((part) => part.type === "reasoning");
190
- if (reasoningPartIndex === -1) {
191
- parts.push({
192
- type: "reasoning",
193
- text: chunk.payload.text,
194
- state: "streaming",
195
- providerMetadata: chunk.payload.providerMetadata
196
- });
197
- } else {
198
- const reasoningPart = parts[reasoningPartIndex];
199
- if (reasoningPart.type === "reasoning") {
200
- parts[reasoningPartIndex] = {
201
- ...reasoningPart,
202
- text: reasoningPart.text + chunk.payload.text,
203
- state: "streaming"
204
- };
205
- }
206
- }
207
- return [
208
- ...result.slice(0, -1),
209
- {
210
- ...lastMessage,
211
- parts
212
- }
213
- ];
214
- }
215
- case "tool-call": {
216
- const lastMessage = result[result.length - 1];
217
- if (!lastMessage || lastMessage.role !== "assistant") {
218
- const newMessage = {
219
- id: `tool-call-${chunk.runId + Date.now()}`,
220
- role: "assistant",
221
- parts: [
222
- {
223
- type: "dynamic-tool",
224
- toolName: chunk.payload.toolName,
225
- toolCallId: chunk.payload.toolCallId,
226
- state: "input-available",
227
- input: chunk.payload.args,
228
- callProviderMetadata: chunk.payload.providerMetadata
229
- }
230
- ],
231
- metadata
232
- };
233
- return [...result, newMessage];
234
- }
235
- const parts = [...lastMessage.parts];
236
- parts.push({
237
- type: "dynamic-tool",
238
- toolName: chunk.payload.toolName,
239
- toolCallId: chunk.payload.toolCallId,
240
- state: "input-available",
241
- input: chunk.payload.args,
242
- callProviderMetadata: chunk.payload.providerMetadata
243
- });
244
- return [
245
- ...result.slice(0, -1),
246
- {
247
- ...lastMessage,
248
- parts
249
- }
250
- ];
251
- }
252
- case "tool-result": {
253
- const lastMessage = result[result.length - 1];
254
- if (!lastMessage || lastMessage.role !== "assistant") return result;
255
- const parts = [...lastMessage.parts];
256
- const toolPartIndex = parts.findIndex(
257
- (part) => part.type === "dynamic-tool" && "toolCallId" in part && part.toolCallId === chunk.payload.toolCallId
258
- );
259
- if (toolPartIndex !== -1) {
260
- const toolPart = parts[toolPartIndex];
261
- if (toolPart.type === "dynamic-tool") {
262
- if (chunk.payload.isError) {
263
- parts[toolPartIndex] = {
264
- type: "dynamic-tool",
265
- toolName: toolPart.toolName,
266
- toolCallId: toolPart.toolCallId,
267
- state: "output-error",
268
- input: toolPart.input,
269
- errorText: String(chunk.payload.result),
270
- callProviderMetadata: chunk.payload.providerMetadata
271
- };
272
- } else {
273
- const isWorkflow = Boolean(chunk.payload.result?.result?.steps);
274
- parts[toolPartIndex] = {
275
- type: "dynamic-tool",
276
- toolName: toolPart.toolName,
277
- toolCallId: toolPart.toolCallId,
278
- state: "output-available",
279
- input: toolPart.input,
280
- output: isWorkflow ? chunk.payload.result?.result : chunk.payload.result,
281
- callProviderMetadata: chunk.payload.providerMetadata
282
- };
283
- }
284
- }
285
- }
286
- return [
287
- ...result.slice(0, -1),
288
- {
289
- ...lastMessage,
290
- parts
291
- }
292
- ];
293
- }
294
- case "tool-output": {
295
- const lastMessage = result[result.length - 1];
296
- if (!lastMessage || lastMessage.role !== "assistant") return result;
297
- const parts = [...lastMessage.parts];
298
- const toolPartIndex = parts.findIndex(
299
- (part) => part.type === "dynamic-tool" && "toolCallId" in part && part.toolCallId === chunk.payload.toolCallId
300
- );
301
- if (toolPartIndex !== -1) {
302
- const toolPart = parts[toolPartIndex];
303
- if (toolPart.type === "dynamic-tool") {
304
- if (chunk.payload.output?.type?.startsWith("workflow-")) {
305
- const existingWorkflowState = toolPart.output || {};
306
- const updatedWorkflowState = mapWorkflowStreamChunkToWatchResult(
307
- existingWorkflowState,
308
- chunk.payload.output
309
- );
310
- parts[toolPartIndex] = {
311
- ...toolPart,
312
- output: updatedWorkflowState
313
- };
314
- } else {
315
- const currentOutput = toolPart.output || [];
316
- const existingOutput = Array.isArray(currentOutput) ? currentOutput : [];
317
- parts[toolPartIndex] = {
318
- ...toolPart,
319
- output: [...existingOutput, chunk.payload.output]
320
- };
321
- }
322
- }
323
- }
324
- return [
325
- ...result.slice(0, -1),
326
- {
327
- ...lastMessage,
328
- parts
329
- }
330
- ];
331
- }
332
- case "source": {
333
- const lastMessage = result[result.length - 1];
334
- if (!lastMessage || lastMessage.role !== "assistant") return result;
335
- const parts = [...lastMessage.parts];
336
- if (chunk.payload.sourceType === "url") {
337
- parts.push({
338
- type: "source-url",
339
- sourceId: chunk.payload.id,
340
- url: chunk.payload.url || "",
341
- title: chunk.payload.title,
342
- providerMetadata: chunk.payload.providerMetadata
343
- });
344
- } else if (chunk.payload.sourceType === "document") {
345
- parts.push({
346
- type: "source-document",
347
- sourceId: chunk.payload.id,
348
- mediaType: chunk.payload.mimeType || "application/octet-stream",
349
- title: chunk.payload.title,
350
- filename: chunk.payload.filename,
351
- providerMetadata: chunk.payload.providerMetadata
352
- });
353
- }
354
- return [
355
- ...result.slice(0, -1),
356
- {
357
- ...lastMessage,
358
- parts
359
- }
360
- ];
361
- }
362
- case "file": {
363
- const lastMessage = result[result.length - 1];
364
- if (!lastMessage || lastMessage.role !== "assistant") return result;
365
- const parts = [...lastMessage.parts];
366
- let url;
367
- if (typeof chunk.payload.data === "string") {
368
- url = chunk.payload.base64 ? `data:${chunk.payload.mimeType};base64,${chunk.payload.data}` : `data:${chunk.payload.mimeType},${encodeURIComponent(chunk.payload.data)}`;
369
- } else {
370
- const base64 = btoa(String.fromCharCode(...chunk.payload.data));
371
- url = `data:${chunk.payload.mimeType};base64,${base64}`;
372
- }
373
- parts.push({
374
- type: "file",
375
- mediaType: chunk.payload.mimeType,
376
- url,
377
- providerMetadata: chunk.payload.providerMetadata
378
- });
379
- return [
380
- ...result.slice(0, -1),
381
- {
382
- ...lastMessage,
383
- parts
384
- }
385
- ];
386
- }
387
- case "finish": {
388
- const lastMessage = result[result.length - 1];
389
- if (!lastMessage || lastMessage.role !== "assistant") return result;
390
- const parts = lastMessage.parts.map((part) => {
391
- if (part.type === "text" && part.state === "streaming") {
392
- return { ...part, state: "done" };
393
- }
394
- if (part.type === "reasoning" && part.state === "streaming") {
395
- return { ...part, state: "done" };
396
- }
397
- return part;
398
- });
399
- return [
400
- ...result.slice(0, -1),
401
- {
402
- ...lastMessage,
403
- parts
404
- }
405
- ];
406
- }
407
- case "error": {
408
- const newMessage = {
409
- id: `error-${chunk.runId + Date.now()}`,
410
- role: "assistant",
411
- parts: [
412
- {
413
- type: "text",
414
- text: typeof chunk.payload.error === "string" ? chunk.payload.error : JSON.stringify(chunk.payload.error)
415
- }
416
- ],
417
- metadata: {
418
- ...metadata,
419
- status: "error"
420
- }
421
- };
422
- return [...result, newMessage];
423
- }
424
- // For all other chunk types, return conversation unchanged
425
- default:
426
- return result;
427
- }
44
+ //#endregion
45
+ //#region src/mastra-react-provider.tsx
46
+ const MastraReactProvider = ({ children, baseUrl, headers, apiPrefix, credentials, customFetch }) => {
47
+ return /* @__PURE__ */ jsx(MastraClientProvider, {
48
+ baseUrl,
49
+ headers,
50
+ apiPrefix,
51
+ credentials,
52
+ customFetch,
53
+ children
54
+ });
428
55
  };
429
-
430
- const toAssistantUIMessage = (message) => {
431
- const extendedMessage = message;
432
- const content = message.parts.map((part) => {
433
- if (part.type === "text") {
434
- return {
435
- type: "text",
436
- text: part.text,
437
- metadata: message.metadata
438
- };
439
- }
440
- if (part.type === "reasoning") {
441
- return {
442
- type: "reasoning",
443
- text: part.text,
444
- metadata: message.metadata
445
- };
446
- }
447
- if (part.type === "source-url") {
448
- return {
449
- type: "source",
450
- sourceType: "url",
451
- id: part.sourceId,
452
- url: part.url,
453
- title: part.title,
454
- metadata: message.metadata
455
- };
456
- }
457
- if (part.type === "source-document") {
458
- return {
459
- type: "file",
460
- filename: part.filename,
461
- mimeType: part.mediaType,
462
- data: "",
463
- // Source documents don't have inline data
464
- metadata: message.metadata
465
- };
466
- }
467
- if (part.type === "file") {
468
- return {
469
- type: "file",
470
- mimeType: part.mediaType,
471
- data: part.url,
472
- // Use URL as data source
473
- metadata: message.metadata
474
- };
475
- }
476
- if (part.type === "dynamic-tool") {
477
- const baseToolCall = {
478
- type: "tool-call",
479
- toolCallId: part.toolCallId,
480
- toolName: part.toolName,
481
- argsText: JSON.stringify(part.input),
482
- args: part.input,
483
- metadata: message.metadata
484
- };
485
- if (part.state === "output-error" && "errorText" in part) {
486
- return { ...baseToolCall, result: part.errorText, isError: true };
487
- }
488
- if ("output" in part) {
489
- return { ...baseToolCall, result: part.output };
490
- }
491
- return baseToolCall;
492
- }
493
- if (part.type.startsWith("tool-") && part.state !== "input-available") {
494
- const toolName = "toolName" in part && typeof part.toolName === "string" ? part.toolName : part.type.substring(5);
495
- const baseToolCall = {
496
- type: "tool-call",
497
- toolCallId: "toolCallId" in part && typeof part.toolCallId === "string" ? part.toolCallId : "",
498
- toolName,
499
- argsText: "input" in part ? JSON.stringify(part.input) : "{}",
500
- args: "input" in part ? part.input : {},
501
- metadata: message.metadata
502
- };
503
- if ("output" in part) {
504
- return { ...baseToolCall, result: part.output };
505
- } else if ("error" in part) {
506
- return { ...baseToolCall, result: part.error, isError: true };
507
- }
508
- return baseToolCall;
509
- }
510
- return {
511
- type: "text",
512
- text: "",
513
- metadata: message.metadata
514
- };
515
- });
516
- let status;
517
- if (message.role === "assistant" && content.length > 0) {
518
- const hasStreamingParts = message.parts.some(
519
- (part) => part.type === "text" && "state" in part && part.state === "streaming" || part.type === "reasoning" && "state" in part && part.state === "streaming"
520
- );
521
- const hasToolCalls = message.parts.some((part) => part.type === "dynamic-tool" || part.type.startsWith("tool-"));
522
- const hasInputAvailableTools = message.parts.some(
523
- (part) => part.type === "dynamic-tool" && part.state === "input-available"
524
- );
525
- const hasErrorTools = message.parts.some(
526
- (part) => part.type === "dynamic-tool" && part.state === "output-error" || part.type.startsWith("tool-") && "error" in part
527
- );
528
- if (hasStreamingParts) {
529
- status = { type: "running" };
530
- } else if (hasInputAvailableTools && hasToolCalls) {
531
- status = { type: "requires-action", reason: "tool-calls" };
532
- } else if (hasErrorTools) {
533
- status = { type: "incomplete", reason: "error" };
534
- } else {
535
- status = { type: "complete", reason: "stop" };
536
- }
537
- }
538
- const threadMessage = {
539
- role: message.role,
540
- content,
541
- id: message.id,
542
- createdAt: extendedMessage.createdAt,
543
- status,
544
- attachments: extendedMessage.experimental_attachments
545
- };
546
- return threadMessage;
56
+ //#endregion
57
+ //#region src/agent/signal-data.ts
58
+ const uint8ArrayToBase64 = (bytes) => {
59
+ const chunkSize = 32768;
60
+ let binary = "";
61
+ for (let i = 0; i < bytes.length; i += chunkSize) binary += String.fromCharCode(...bytes.subarray(i, i + chunkSize));
62
+ return btoa(binary);
547
63
  };
548
-
549
- class AISdkNetworkTransformer {
550
- transform({ chunk, conversation, metadata }) {
551
- const newConversation = [...conversation];
552
- if (chunk.type === "routing-agent-text-delta") {
553
- return this.handleRoutingAgentConversation(chunk, newConversation);
554
- }
555
- if (chunk.type.startsWith("agent-execution-")) {
556
- return this.handleAgentConversation(chunk, newConversation, metadata);
557
- }
558
- if (chunk.type.startsWith("workflow-execution-")) {
559
- return this.handleWorkflowConversation(chunk, newConversation, metadata);
560
- }
561
- if (chunk.type.startsWith("tool-execution-")) {
562
- return this.handleToolConversation(chunk, newConversation, metadata);
563
- }
564
- if (chunk.type === "network-execution-event-step-finish") {
565
- const lastMessage = newConversation[newConversation.length - 1];
566
- if (!lastMessage || lastMessage.role !== "assistant") return newConversation;
567
- const agentChunk = chunk.payload;
568
- const parts = [...lastMessage.parts];
569
- const textPartIndex = parts.findIndex((part) => part.type === "text");
570
- if (textPartIndex === -1) {
571
- parts.push({
572
- type: "text",
573
- text: agentChunk.result,
574
- state: "done"
575
- });
576
- return [
577
- ...newConversation.slice(0, -1),
578
- {
579
- ...lastMessage,
580
- parts
581
- }
582
- ];
583
- }
584
- const textPart = parts[textPartIndex];
585
- if (textPart.type === "text") {
586
- parts[textPartIndex] = {
587
- ...textPart,
588
- state: "done"
589
- };
590
- return [
591
- ...newConversation.slice(0, -1),
592
- {
593
- ...lastMessage,
594
- parts
595
- }
596
- ];
597
- }
598
- return newConversation;
599
- }
600
- return newConversation;
601
- }
602
- handleRoutingAgentConversation = (chunk, newConversation) => {
603
- const lastMessage = newConversation[newConversation.length - 1];
604
- if (!lastMessage || lastMessage.role !== "assistant") return newConversation;
605
- const agentChunk = chunk.payload;
606
- const parts = [...lastMessage.parts];
607
- const textPartIndex = parts.findIndex((part) => part.type === "text");
608
- if (textPartIndex === -1) {
609
- parts.push({
610
- type: "text",
611
- text: agentChunk.text,
612
- state: "streaming"
613
- });
614
- return [
615
- ...newConversation.slice(0, -1),
616
- {
617
- ...lastMessage,
618
- parts
619
- }
620
- ];
621
- }
622
- const textPart = parts[textPartIndex];
623
- if (textPart.type === "text") {
624
- parts[textPartIndex] = {
625
- ...textPart,
626
- text: textPart.text + agentChunk.text,
627
- state: "streaming"
628
- };
629
- return [
630
- ...newConversation.slice(0, -1),
631
- {
632
- ...lastMessage,
633
- parts
634
- }
635
- ];
636
- }
637
- return newConversation;
638
- };
639
- handleAgentConversation = (chunk, newConversation, metadata) => {
640
- if (chunk.type === "agent-execution-start") {
641
- const primitiveId = chunk.payload?.args?.primitiveId;
642
- const runId = chunk.payload.runId;
643
- if (!primitiveId || !runId) return newConversation;
644
- const newMessage = {
645
- id: `agent-execution-start-${runId}-${Date.now()}`,
646
- role: "assistant",
647
- parts: [
648
- {
649
- type: "dynamic-tool",
650
- toolName: primitiveId,
651
- toolCallId: runId,
652
- state: "input-available",
653
- input: chunk.payload.args
654
- }
655
- ],
656
- metadata: {
657
- ...metadata,
658
- selectionReason: chunk.payload?.args?.selectionReason || "",
659
- agentInput: chunk.payload?.args?.task,
660
- mode: "network",
661
- from: "AGENT"
662
- }
663
- };
664
- return [...newConversation, newMessage];
665
- }
666
- if (chunk.type === "agent-execution-end") {
667
- const lastMessage = newConversation[newConversation.length - 1];
668
- if (!lastMessage || lastMessage.role !== "assistant") return newConversation;
669
- const parts = [...lastMessage.parts];
670
- const toolPartIndex = parts.findIndex((part) => part.type === "dynamic-tool");
671
- if (toolPartIndex !== -1) {
672
- const toolPart = parts[toolPartIndex];
673
- if (toolPart.type === "dynamic-tool") {
674
- const currentOutput = toolPart.output;
675
- parts[toolPartIndex] = {
676
- type: "dynamic-tool",
677
- toolName: toolPart.toolName,
678
- toolCallId: toolPart.toolCallId,
679
- state: "output-available",
680
- input: toolPart.input,
681
- output: {
682
- ...currentOutput,
683
- result: currentOutput?.result || chunk.payload?.result || ""
684
- }
685
- };
686
- }
687
- }
688
- return [
689
- ...newConversation.slice(0, -1),
690
- {
691
- ...lastMessage,
692
- parts
693
- }
694
- ];
695
- }
696
- if (chunk.type.startsWith("agent-execution-event-")) {
697
- const lastMessage = newConversation[newConversation.length - 1];
698
- if (!lastMessage || lastMessage.role !== "assistant") return newConversation;
699
- const agentChunk = chunk.payload;
700
- const parts = [...lastMessage.parts];
701
- const toolPartIndex = parts.findIndex((part) => part.type === "dynamic-tool");
702
- if (toolPartIndex === -1) return newConversation;
703
- const toolPart = parts[toolPartIndex];
704
- if (agentChunk.type === "text-delta") {
705
- const childMessages = toolPart?.output?.childMessages || [];
706
- const lastChildMessage = childMessages[childMessages.length - 1];
707
- const textMessage = { type: "text", content: (lastChildMessage?.content || "") + agentChunk.payload.text };
708
- const nextMessages = lastChildMessage?.type === "text" ? [...childMessages.slice(0, -1), textMessage] : [...childMessages, textMessage];
709
- parts[toolPartIndex] = {
710
- ...toolPart,
711
- output: {
712
- childMessages: nextMessages
713
- }
714
- };
715
- } else if (agentChunk.type === "tool-call") {
716
- const childMessages = toolPart?.output?.childMessages || [];
717
- parts[toolPartIndex] = {
718
- ...toolPart,
719
- output: {
720
- ...toolPart?.output,
721
- childMessages: [
722
- ...childMessages,
723
- {
724
- type: "tool",
725
- toolCallId: agentChunk.payload.toolCallId,
726
- toolName: agentChunk.payload.toolName,
727
- args: agentChunk.payload.args
728
- }
729
- ]
730
- }
731
- };
732
- } else if (agentChunk.type === "tool-output") {
733
- if (agentChunk.payload?.output?.type?.startsWith("workflow-")) {
734
- const childMessages = toolPart?.output?.childMessages || [];
735
- const lastToolIndex = childMessages.length - 1;
736
- const currentMessage = childMessages[lastToolIndex];
737
- const actualExistingWorkflowState = currentMessage?.toolOutput || {};
738
- const updatedWorkflowState = mapWorkflowStreamChunkToWatchResult(
739
- actualExistingWorkflowState,
740
- agentChunk.payload.output
741
- );
742
- if (lastToolIndex >= 0 && childMessages[lastToolIndex]?.type === "tool") {
743
- parts[toolPartIndex] = {
744
- ...toolPart,
745
- output: {
746
- ...toolPart?.output,
747
- childMessages: [
748
- ...childMessages.slice(0, -1),
749
- {
750
- ...currentMessage,
751
- toolOutput: updatedWorkflowState
752
- }
753
- ]
754
- }
755
- };
756
- }
757
- }
758
- } else if (agentChunk.type === "tool-result") {
759
- const childMessages = toolPart?.output?.childMessages || [];
760
- const lastToolIndex = childMessages.length - 1;
761
- const isWorkflow = Boolean(agentChunk.payload?.result?.result?.steps);
762
- if (lastToolIndex >= 0 && childMessages[lastToolIndex]?.type === "tool") {
763
- parts[toolPartIndex] = {
764
- ...toolPart,
765
- output: {
766
- ...toolPart?.output,
767
- childMessages: [
768
- ...childMessages.slice(0, -1),
769
- {
770
- ...childMessages[lastToolIndex],
771
- toolOutput: isWorkflow ? agentChunk.payload.result.result : agentChunk.payload.result
772
- }
773
- ]
774
- }
775
- };
776
- }
777
- }
778
- return [
779
- ...newConversation.slice(0, -1),
780
- {
781
- ...lastMessage,
782
- parts
783
- }
784
- ];
785
- }
786
- return newConversation;
787
- };
788
- handleWorkflowConversation = (chunk, newConversation, metadata) => {
789
- if (chunk.type === "workflow-execution-start") {
790
- const primitiveId = chunk.payload?.args?.primitiveId;
791
- const runId = chunk.payload.runId;
792
- if (!primitiveId || !runId) return newConversation;
793
- let agentInput;
794
- try {
795
- agentInput = JSON.parse(chunk?.payload?.args?.prompt);
796
- } catch (e) {
797
- agentInput = chunk?.payload?.args?.prompt;
798
- }
799
- const newMessage = {
800
- id: `workflow-start-${runId}-${Date.now()}`,
801
- role: "assistant",
802
- parts: [
803
- {
804
- type: "dynamic-tool",
805
- toolName: primitiveId,
806
- toolCallId: runId,
807
- state: "input-available",
808
- input: chunk.payload.args
809
- }
810
- ],
811
- metadata: {
812
- ...metadata,
813
- selectionReason: chunk.payload?.args?.selectionReason || "",
814
- from: "WORKFLOW",
815
- mode: "network",
816
- agentInput
817
- }
818
- };
819
- return [...newConversation, newMessage];
820
- }
821
- if (chunk.type.startsWith("workflow-execution-event-")) {
822
- const lastMessage = newConversation[newConversation.length - 1];
823
- if (!lastMessage || lastMessage.role !== "assistant") return newConversation;
824
- const parts = [...lastMessage.parts];
825
- const toolPartIndex = parts.findIndex((part) => part.type === "dynamic-tool");
826
- if (toolPartIndex === -1) return newConversation;
827
- const toolPart = parts[toolPartIndex];
828
- if (toolPart.type !== "dynamic-tool") return newConversation;
829
- const existingWorkflowState = toolPart.output || {};
830
- const updatedWorkflowState = mapWorkflowStreamChunkToWatchResult(existingWorkflowState, chunk.payload);
831
- parts[toolPartIndex] = {
832
- ...toolPart,
833
- output: updatedWorkflowState
834
- };
835
- return [
836
- ...newConversation.slice(0, -1),
837
- {
838
- ...lastMessage,
839
- parts
840
- }
841
- ];
842
- }
843
- return newConversation;
844
- };
845
- handleToolConversation = (chunk, newConversation, metadata) => {
846
- if (chunk.type === "tool-execution-start") {
847
- const { args: argsData } = chunk.payload;
848
- const lastMessage = newConversation[newConversation.length - 1];
849
- const nestedArgs = argsData.args || {};
850
- if (!lastMessage || lastMessage.role !== "assistant") {
851
- const newMessage = {
852
- id: `tool-start-${chunk.runId}-${Date.now()}`,
853
- role: "assistant",
854
- parts: [
855
- {
856
- type: "dynamic-tool",
857
- toolName: argsData.toolName || "unknown",
858
- toolCallId: argsData.toolCallId || "unknown",
859
- state: "input-available",
860
- input: nestedArgs
861
- }
862
- ],
863
- metadata: {
864
- ...metadata,
865
- selectionReason: metadata?.mode === "network" ? metadata.selectionReason || argsData.selectionReason : "",
866
- mode: "network",
867
- agentInput: nestedArgs
868
- }
869
- };
870
- return [...newConversation, newMessage];
871
- }
872
- const parts = [...lastMessage.parts];
873
- parts.push({
874
- type: "dynamic-tool",
875
- toolName: argsData.toolName || "unknown",
876
- toolCallId: argsData.toolCallId || "unknown",
877
- state: "input-available",
878
- input: nestedArgs
879
- });
880
- return [
881
- ...newConversation.slice(0, -1),
882
- {
883
- ...lastMessage,
884
- parts
885
- }
886
- ];
887
- }
888
- if (chunk.type === "tool-execution-end") {
889
- const lastMessage = newConversation[newConversation.length - 1];
890
- if (!lastMessage || lastMessage.role !== "assistant") return newConversation;
891
- const parts = [...lastMessage.parts];
892
- const toolPartIndex = parts.findIndex(
893
- (part) => part.type === "dynamic-tool" && "toolCallId" in part && part.toolCallId === chunk.payload.toolCallId
894
- );
895
- if (toolPartIndex !== -1) {
896
- const toolPart = parts[toolPartIndex];
897
- if (toolPart.type === "dynamic-tool") {
898
- const currentOutput = toolPart.output;
899
- parts[toolPartIndex] = {
900
- type: "dynamic-tool",
901
- toolName: toolPart.toolName,
902
- toolCallId: toolPart.toolCallId,
903
- state: "output-available",
904
- input: toolPart.input,
905
- output: currentOutput?.result || chunk.payload?.result || ""
906
- };
907
- }
908
- }
909
- return [
910
- ...newConversation.slice(0, -1),
911
- {
912
- ...lastMessage,
913
- parts
914
- }
915
- ];
916
- }
917
- return newConversation;
918
- };
64
+ function convertSignalDataToBase64String(content) {
65
+ if (typeof content === "string") return content;
66
+ const bytes = content instanceof ArrayBuffer ? new Uint8Array(content) : content;
67
+ return uint8ArrayToBase64(bytes);
919
68
  }
920
-
921
- const resolveInitialMessages = (messages) => {
922
- return messages.map((message) => {
923
- const networkPart = message.parts.find((part) => part.type === "text" && part.text.includes('"isNetwork":true'));
924
- if (networkPart && networkPart.type === "text") {
925
- try {
926
- const json = JSON.parse(networkPart.text);
927
- if (json.isNetwork === true) {
928
- const selectionReason = json.selectionReason || "";
929
- const primitiveType = json.primitiveType || "";
930
- const primitiveId = json.primitiveId || "";
931
- const finalResult = json.finalResult;
932
- const toolCalls = finalResult?.toolCalls || [];
933
- const childMessages = [];
934
- for (const toolCall of toolCalls) {
935
- if (toolCall.type === "tool-call" && toolCall.payload) {
936
- const toolCallId = toolCall.payload.toolCallId;
937
- let toolResult;
938
- for (const message2 of finalResult?.messages || []) {
939
- for (const part of message2.content || []) {
940
- if (typeof part === "object" && part.type === "tool-result" && part.toolCallId === toolCallId) {
941
- toolResult = part;
942
- break;
943
- }
944
- }
945
- }
946
- const isWorkflow = Boolean(toolResult?.result?.result?.steps);
947
- childMessages.push({
948
- type: "tool",
949
- toolCallId: toolCall.payload.toolCallId,
950
- toolName: toolCall.payload.toolName,
951
- args: toolCall.payload.args,
952
- toolOutput: isWorkflow ? toolResult?.result?.result : toolResult?.result
953
- });
954
- }
955
- }
956
- if (finalResult && finalResult.text) {
957
- childMessages.push({
958
- type: "text",
959
- content: finalResult.text
960
- });
961
- }
962
- const result = {
963
- childMessages,
964
- result: finalResult?.text || ""
965
- };
966
- console.log("json", json);
967
- const nextMessage = {
968
- role: "assistant",
969
- parts: [
970
- {
971
- type: "dynamic-tool",
972
- toolCallId: primitiveId,
973
- toolName: primitiveId,
974
- state: "output-available",
975
- input: json.input,
976
- output: result
977
- }
978
- ],
979
- id: message.id,
980
- metadata: {
981
- ...message.metadata,
982
- mode: "network",
983
- selectionReason,
984
- agentInput: json.input,
985
- from: primitiveType === "agent" ? "AGENT" : "WORKFLOW"
986
- }
987
- };
988
- return nextMessage;
989
- }
990
- } catch (error) {
991
- return message;
992
- }
993
- }
994
- return message;
995
- });
69
+ /** Canonical DB `file` part `data` for optimistic UI and memory-shaped storage. */
70
+ function encodeFilePartDataForStorage(data, mimeType) {
71
+ if (typeof data === "string") return data;
72
+ if (data instanceof URL) return data.toString();
73
+ const bytes = data instanceof ArrayBuffer ? new Uint8Array(data) : data;
74
+ const base64 = uint8ArrayToBase64(bytes);
75
+ if (base64.startsWith("data:")) return base64;
76
+ return `data:${mimeType};base64,${base64}`;
77
+ }
78
+ //#endregion
79
+ //#region src/lib/mastra-db/formatCompletionFeedback.ts
80
+ const formatBaseCompletionFeedback = (result, maxIterationReached, formatScorerHeading, incompleteMessage) => {
81
+ const lines = [];
82
+ lines.push("#### Completion Check Results");
83
+ lines.push("");
84
+ lines.push(`Overall: ${result.complete ? "✅ COMPLETE" : "❌ NOT COMPLETE"}`);
85
+ lines.push(`Duration: ${result.totalDuration}ms`);
86
+ if (result.timedOut) lines.push("⚠️ Scoring timed out");
87
+ lines.push("");
88
+ for (const scorer of result.scorers) {
89
+ lines.push(formatScorerHeading(scorer));
90
+ lines.push(`Score: ${scorer.score} ${scorer.passed ? "✅" : "❌"}`);
91
+ if (scorer.reason) lines.push(`Reason: ${scorer.reason}`);
92
+ lines.push("");
93
+ }
94
+ if (result.complete) lines.push("✅ The task is complete.");
95
+ else if (maxIterationReached) lines.push("⚠️ Max iterations reached.");
96
+ else lines.push(incompleteMessage);
97
+ return lines.join("\n");
996
98
  };
997
-
998
- const useChat = ({ agentId, initializeMessages }) => {
999
- const [messages, setMessages] = useState(
1000
- () => resolveInitialMessages(initializeMessages?.() || [])
1001
- );
1002
- const baseClient = useMastraClient();
1003
- const [isRunning, setIsRunning] = useState(false);
1004
- const generate = async ({
1005
- coreUserMessages,
1006
- runtimeContext,
1007
- threadId,
1008
- modelSettings,
1009
- signal,
1010
- onFinish
1011
- }) => {
1012
- const {
1013
- frequencyPenalty,
1014
- presencePenalty,
1015
- maxRetries,
1016
- maxTokens,
1017
- temperature,
1018
- topK,
1019
- topP,
1020
- instructions,
1021
- providerOptions,
1022
- maxSteps
1023
- } = modelSettings || {};
1024
- setIsRunning(true);
1025
- const clientWithAbort = new MastraClient({
1026
- ...baseClient.options,
1027
- abortSignal: signal
1028
- });
1029
- const agent = clientWithAbort.getAgent(agentId);
1030
- const response = await agent.generate({
1031
- messages: coreUserMessages,
1032
- runId: agentId,
1033
- maxSteps,
1034
- modelSettings: {
1035
- frequencyPenalty,
1036
- presencePenalty,
1037
- maxRetries,
1038
- maxOutputTokens: maxTokens,
1039
- temperature,
1040
- topK,
1041
- topP
1042
- },
1043
- instructions,
1044
- runtimeContext,
1045
- ...threadId ? { threadId, resourceId: agentId } : {},
1046
- providerOptions
1047
- });
1048
- setIsRunning(false);
1049
- if (response && "uiMessages" in response.response && response.response.uiMessages) {
1050
- onFinish?.(response.response.uiMessages);
1051
- const mastraUIMessages = (response.response.uiMessages || []).map((message) => ({
1052
- ...message,
1053
- metadata: {
1054
- mode: "generate"
1055
- }
1056
- }));
1057
- setMessages((prev) => [...prev, ...mastraUIMessages]);
1058
- }
1059
- };
1060
- const stream = async ({ coreUserMessages, runtimeContext, threadId, onChunk, modelSettings, signal }) => {
1061
- const {
1062
- frequencyPenalty,
1063
- presencePenalty,
1064
- maxRetries,
1065
- maxTokens,
1066
- temperature,
1067
- topK,
1068
- topP,
1069
- instructions,
1070
- providerOptions,
1071
- maxSteps
1072
- } = modelSettings || {};
1073
- setIsRunning(true);
1074
- const clientWithAbort = new MastraClient({
1075
- ...baseClient.options,
1076
- abortSignal: signal
1077
- });
1078
- const agent = clientWithAbort.getAgent(agentId);
1079
- const response = await agent.stream({
1080
- messages: coreUserMessages,
1081
- runId: agentId,
1082
- maxSteps,
1083
- modelSettings: {
1084
- frequencyPenalty,
1085
- presencePenalty,
1086
- maxRetries,
1087
- maxOutputTokens: maxTokens,
1088
- temperature,
1089
- topK,
1090
- topP
1091
- },
1092
- instructions,
1093
- runtimeContext,
1094
- ...threadId ? { threadId, resourceId: agentId } : {},
1095
- providerOptions
1096
- });
1097
- if (!response.body) {
1098
- setIsRunning(false);
1099
- throw new Error("[Stream] No response body");
1100
- }
1101
- await response.processDataStream({
1102
- onChunk: async (chunk) => {
1103
- setMessages((prev) => toUIMessage({ chunk, conversation: prev, metadata: { mode: "stream" } }));
1104
- onChunk?.(chunk);
1105
- }
1106
- });
1107
- setIsRunning(false);
1108
- };
1109
- const network = async ({
1110
- coreUserMessages,
1111
- runtimeContext,
1112
- threadId,
1113
- onNetworkChunk,
1114
- modelSettings,
1115
- signal
1116
- }) => {
1117
- const { frequencyPenalty, presencePenalty, maxRetries, maxTokens, temperature, topK, topP, maxSteps } = modelSettings || {};
1118
- setIsRunning(true);
1119
- const clientWithAbort = new MastraClient({
1120
- ...baseClient.options,
1121
- abortSignal: signal
1122
- });
1123
- const agent = clientWithAbort.getAgent(agentId);
1124
- const response = await agent.network({
1125
- messages: coreUserMessages,
1126
- maxSteps,
1127
- modelSettings: {
1128
- frequencyPenalty,
1129
- presencePenalty,
1130
- maxRetries,
1131
- maxOutputTokens: maxTokens,
1132
- temperature,
1133
- topK,
1134
- topP
1135
- },
1136
- runId: agentId,
1137
- runtimeContext,
1138
- ...threadId ? { thread: threadId, resourceId: agentId } : {}
1139
- });
1140
- const transformer = new AISdkNetworkTransformer();
1141
- await response.processDataStream({
1142
- onChunk: async (chunk) => {
1143
- setMessages((prev) => transformer.transform({ chunk, conversation: prev, metadata: { mode: "network" } }));
1144
- onNetworkChunk?.(chunk);
1145
- }
1146
- });
1147
- setIsRunning(false);
1148
- };
1149
- const sendMessage = async ({ mode = "stream", ...args }) => {
1150
- const nextMessage = { role: "user", content: [{ type: "text", text: args.message }] };
1151
- const messages2 = args.coreUserMessages ? [nextMessage, ...args.coreUserMessages] : [nextMessage];
1152
- setMessages((s) => [...s, { role: "user", parts: [{ type: "text", text: args.message }] }]);
1153
- if (mode === "generate") {
1154
- await generate({ ...args, coreUserMessages: messages2 });
1155
- } else if (mode === "stream") {
1156
- await stream({ ...args, coreUserMessages: messages2 });
1157
- } else if (mode === "network") {
1158
- await network({ ...args, coreUserMessages: messages2 });
1159
- }
1160
- };
1161
- return {
1162
- setMessages,
1163
- sendMessage,
1164
- isRunning,
1165
- messages,
1166
- cancelRun: () => setIsRunning(false)
1167
- };
99
+ const formatCompletionFeedback = (result, maxIterationReached) => {
100
+ return formatBaseCompletionFeedback(result, maxIterationReached, (scorer) => `###### ${scorer.scorerName} (${scorer.scorerId})`, "🔄 Will continue working on the task.");
1168
101
  };
1169
-
1170
- const EntityContext = createContext({
1171
- expanded: false,
1172
- setExpanded: () => {
1173
- },
1174
- variant: "initial",
1175
- disabled: false
102
+ const formatStreamCompletionFeedback = (result, maxIterationReached) => {
103
+ return formatBaseCompletionFeedback(result, maxIterationReached, (scorer) => `**${scorer.scorerName}** (${scorer.scorerId})`, "🔄 The task is not yet complete. Please continue working based on the feedback above.");
104
+ };
105
+ //#endregion
106
+ //#region src/lib/mastra-db/types.ts
107
+ /**
108
+ * Canonical metadata block stored under `MastraDBMessage.content.metadata`.
109
+ *
110
+ * Every UX hint the React accumulator needs to surface lives here. Mode-specific
111
+ * fields are all optional so a single record can carry the union without forcing
112
+ * narrowing on the consumer side.
113
+ */
114
+ /**
115
+ * Metadata key carrying a client-generated correlation id. The optimistic
116
+ * pending user bubble and the outgoing `sendMessage` request both stamp this
117
+ * id; the server echoes it back on the `data-user-message` data part so the
118
+ * accumulator can reconcile the pending bubble deterministically (decoupled
119
+ * from the server-assigned signal id). This is transient client state and is
120
+ * stripped once the echo is reconciled and on reload.
121
+ */
122
+ const CLIENT_MESSAGE_ID_KEY = "clientMessageId";
123
+ //#endregion
124
+ //#region src/lib/mastra-db/accumulator.ts
125
+ const cloneMetadata = (metadata) => metadata ? { ...metadata } : {};
126
+ const withParts = (message, parts) => ({
127
+ ...message,
128
+ content: {
129
+ ...message.content,
130
+ parts
131
+ }
1176
132
  });
1177
- const EntityProvider = EntityContext.Provider;
1178
- const useEntity = () => useContext(EntityContext);
1179
-
1180
- const IconSizes = {
1181
- sm: "mastra:[&>svg]:size-3",
1182
- md: "mastra:[&>svg]:size-4",
1183
- lg: "mastra:[&>svg]:size-5"
133
+ const withMetadata = (message, metadata) => ({
134
+ ...message,
135
+ content: {
136
+ ...message.content,
137
+ metadata
138
+ }
139
+ });
140
+ const clearPendingStatus = (message) => {
141
+ const { status: _status, [CLIENT_MESSAGE_ID_KEY]: _clientMessageId, ...rest } = message.content.metadata ?? {};
142
+ return withMetadata(message, rest);
1184
143
  };
1185
- const Icon = ({ children, className, size = "md", ...props }) => {
1186
- return /* @__PURE__ */ jsx("div", { className: className || IconSizes[size], ...props, children });
144
+ const clearPendingStatusKeepClientId = (message) => {
145
+ const { status: _status, ...rest } = message.content.metadata ?? {};
146
+ return withMetadata(message, rest);
1187
147
  };
1188
-
1189
- const Entity = ({
1190
- className,
1191
- variant = "initial",
1192
- initialExpanded = false,
1193
- disabled = false,
1194
- ...props
1195
- }) => {
1196
- const [expanded, setExpanded] = useState(initialExpanded);
1197
- return /* @__PURE__ */ jsx(EntityProvider, { value: { expanded, setExpanded, variant, disabled }, children: /* @__PURE__ */ jsx("div", { className, ...props }) });
148
+ const replaceLast = (conversation, message) => [...conversation.slice(0, -1), message];
149
+ const replaceAt = (conversation, index, message) => [
150
+ ...conversation.slice(0, index),
151
+ message,
152
+ ...conversation.slice(index + 1)
153
+ ];
154
+ const newAssistantMessage = (id, parts, metadata) => ({
155
+ id,
156
+ role: "assistant",
157
+ createdAt: /* @__PURE__ */ new Date(),
158
+ content: {
159
+ format: 2,
160
+ parts,
161
+ metadata: cloneMetadata(metadata)
162
+ }
163
+ });
164
+ const appendAssistantMessage = (conversation, id, parts, metadata) => [...conversation, newAssistantMessage(id, parts, metadata)];
165
+ const isToolPart = (part) => part.type === "tool-invocation";
166
+ const partTextId = (part) => part.type === "text" ? part.textId : void 0;
167
+ const partState = (part) => part.state;
168
+ /**
169
+ * Set any streaming text/reasoning parts on the trailing assistant message to
170
+ * `state: 'done'`. Mirrors the previous `finishStreamingAssistantMessage` from
171
+ * the AI-SDK accumulator.
172
+ */
173
+ const finishStreamingAssistantMessage = (conversation) => {
174
+ const lastMessage = conversation[conversation.length - 1];
175
+ if (!lastMessage || lastMessage.role !== "assistant") return conversation;
176
+ if (lastMessage.content.parts.length === 0) return conversation.slice(0, -1);
177
+ const nextParts = lastMessage.content.parts.map((part) => {
178
+ if ((part.type === "text" || part.type === "reasoning") && partState(part) === "streaming") return {
179
+ ...part,
180
+ state: "done"
181
+ };
182
+ return part;
183
+ });
184
+ return replaceLast(conversation, withParts(lastMessage, nextParts));
1198
185
  };
1199
- const EntityTriggerClass = twMerge(
1200
- "mastra:aria-disabled:cursor-not-allowed mastra:aria-disabled:bg-surface5 mastra:aria-disabled:text-text3",
1201
- "mastra:aria-expanded:rounded-b-none mastra:aria-expanded:border-b-0",
1202
- "mastra:bg-surface3 mastra:text-text6 mastra:hover:bg-surface4 mastra:active:bg-surface5",
1203
- "mastra:rounded-lg mastra:py-2 mastra:px-4 mastra:border mastra:border-border1",
1204
- "mastra:cursor-pointer mastra:inline-flex mastra:items-center mastra:gap-1 mastra:font-mono"
1205
- );
1206
- const EntityTriggerVariantClasses = {
1207
- agent: "mastra:[&_svg.mastra-icon]:text-accent1",
1208
- workflow: "mastra:[&_svg.mastra-icon]:text-accent3",
1209
- tool: "mastra:[&_svg.mastra-icon]:text-accent6",
1210
- memory: "mastra:[&_svg.mastra-icon]:text-accent2",
1211
- initial: "mastra:[&_svg.mastra-icon]:text-text3"
186
+ /**
187
+ * Locate the assistant message + tool part owning `toolCallId`. Prefers the
188
+ * most recent assistant message and walks back up to 10 messages, matching the
189
+ * historical accumulator's lookup behavior.
190
+ */
191
+ const locateToolPart = (messages, toolCallId, allowMetadataOnlyMatch) => {
192
+ const findIndex = (parts) => parts.findIndex((part) => isToolPart(part) && part.toolInvocation.toolCallId === toolCallId);
193
+ const lastMessage = messages[messages.length - 1];
194
+ if (lastMessage && lastMessage.role === "assistant") {
195
+ const idx = findIndex(lastMessage.content.parts);
196
+ if (idx !== -1) return {
197
+ messageIndex: messages.length - 1,
198
+ toolPartIndex: idx
199
+ };
200
+ }
201
+ let count = 0;
202
+ const maxMessagesBack = 10;
203
+ for (let i = messages.length - 1; i >= 0; i--) {
204
+ if (count > maxMessagesBack) break;
205
+ const message = messages[i];
206
+ if (message.role !== "assistant") continue;
207
+ const idx = findIndex(message.content.parts);
208
+ if (idx !== -1) return {
209
+ messageIndex: i,
210
+ toolPartIndex: idx
211
+ };
212
+ count++;
213
+ }
214
+ if (!allowMetadataOnlyMatch) return null;
215
+ for (let i = messages.length - 1; i >= 0; i--) if (messages[i].role === "assistant") return {
216
+ messageIndex: i,
217
+ toolPartIndex: -1
218
+ };
219
+ return null;
1212
220
  };
1213
- const EntityTrigger = ({ className, children, ...props }) => {
1214
- const { expanded, setExpanded, variant, disabled } = useEntity();
1215
- const handleClick = (e) => {
1216
- if (disabled) return;
1217
- setExpanded(!expanded);
1218
- props?.onClick?.(e);
1219
- };
1220
- return /* @__PURE__ */ jsx(
1221
- "button",
1222
- {
1223
- className: className || twMerge(EntityTriggerClass, !disabled && EntityTriggerVariantClasses[variant]),
1224
- ...props,
1225
- onClick: handleClick,
1226
- "aria-expanded": expanded,
1227
- "aria-disabled": disabled,
1228
- children
1229
- }
1230
- );
221
+ /**
222
+ * Merge per-toolCallId background-task bookkeeping onto message metadata.
223
+ */
224
+ const mergeBgTaskMetadata = (existing, mode, args, otherMetadata) => {
225
+ const base = cloneMetadata(existing);
226
+ const existingBgTasks = base.backgroundTasks ?? {};
227
+ const nextBgTasks = { ...existingBgTasks };
228
+ if (args.perTaskEntry) {
229
+ const { toolCallId, startedAt, completedAt, taskId, suspendedAt } = args.perTaskEntry;
230
+ nextBgTasks[toolCallId] = {
231
+ ...existingBgTasks[toolCallId] ?? { taskId },
232
+ taskId,
233
+ ...startedAt !== void 0 ? { startedAt } : {},
234
+ ...completedAt !== void 0 ? { completedAt } : {},
235
+ ...suspendedAt !== void 0 ? { suspendedAt } : {}
236
+ };
237
+ }
238
+ const merged = {
239
+ ...base,
240
+ ...otherMetadata ?? {},
241
+ mode,
242
+ backgroundTasks: nextBgTasks
243
+ };
244
+ if (args.resetRunningCount) merged.runningBackgroundTasksCount = void 0;
245
+ return merged;
1231
246
  };
1232
- const EntityContentClass = twMerge(
1233
- "mastra:space-y-4",
1234
- "mastra:rounded-lg mastra:rounded-tl-none mastra:p-4 mastra:border mastra:border-border1 mastra:-mt-[0.5px]",
1235
- "mastra:bg-surface3 mastra:text-text6"
1236
- );
1237
- const EntityContent = ({ className, ...props }) => {
1238
- const { expanded } = useEntity();
1239
- if (!expanded) return null;
1240
- return /* @__PURE__ */ jsx("div", { className: className || EntityContentClass, ...props });
247
+ /**
248
+ * Workflow chunk accumulation. Mirrors
249
+ * `mapWorkflowStreamChunkToWatchResult` from the previous accumulator.
250
+ */
251
+ const mapWorkflowStreamChunkToWatchResult = (prev, chunk) => {
252
+ if (chunk.type === "workflow-start") return {
253
+ input: prev?.input,
254
+ status: "running",
255
+ steps: prev?.steps || {}
256
+ };
257
+ if (chunk.type === "workflow-canceled") return {
258
+ ...prev,
259
+ status: "canceled"
260
+ };
261
+ if (chunk.type === "workflow-finish") {
262
+ const finalStatus = chunk.payload.workflowStatus;
263
+ const prevSteps = prev?.steps ?? {};
264
+ const lastStep = Object.values(prevSteps).pop();
265
+ return {
266
+ ...prev,
267
+ status: chunk.payload.workflowStatus,
268
+ ...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 } : {}
269
+ };
270
+ }
271
+ const { stepCallId: _stepCallId, stepName: _stepName, ...newPayload } = chunk.payload ?? {};
272
+ const newSteps = {
273
+ ...prev?.steps,
274
+ [chunk.payload.id]: {
275
+ ...prev?.steps?.[chunk.payload.id],
276
+ ...newPayload
277
+ }
278
+ };
279
+ if (chunk.type === "workflow-step-start") return {
280
+ ...prev,
281
+ steps: newSteps
282
+ };
283
+ if (chunk.type === "workflow-step-suspended") {
284
+ const suspendedStepIds = Object.entries(newSteps).flatMap(([stepId, stepResult]) => {
285
+ if (stepResult?.status === "suspended") {
286
+ const nestedPath = stepResult?.suspendPayload?.__workflow_meta?.path;
287
+ return nestedPath ? [[stepId, ...nestedPath]] : [[stepId]];
288
+ }
289
+ return [];
290
+ });
291
+ return {
292
+ ...prev,
293
+ status: "suspended",
294
+ steps: newSteps,
295
+ suspendPayload: chunk.payload.suspendPayload,
296
+ suspended: suspendedStepIds
297
+ };
298
+ }
299
+ if (chunk.type === "workflow-step-waiting") return {
300
+ ...prev,
301
+ status: "waiting",
302
+ steps: newSteps
303
+ };
304
+ if (chunk.type === "workflow-step-progress") return {
305
+ ...prev,
306
+ steps: {
307
+ ...prev?.steps,
308
+ [chunk.payload.id]: {
309
+ ...prev?.steps?.[chunk.payload.id],
310
+ foreachProgress: {
311
+ completedCount: chunk.payload.completedCount,
312
+ totalCount: chunk.payload.totalCount,
313
+ currentIndex: chunk.payload.currentIndex,
314
+ iterationStatus: chunk.payload.iterationStatus,
315
+ iterationOutput: chunk.payload.iterationOutput
316
+ }
317
+ }
318
+ }
319
+ };
320
+ if (chunk.type === "workflow-step-result") return {
321
+ ...prev,
322
+ steps: newSteps
323
+ };
324
+ return prev;
1241
325
  };
1242
- const EntityCaret = ({ className, ...props }) => {
1243
- const { expanded } = useEntity();
1244
- return /* @__PURE__ */ jsx(Icon, { children: /* @__PURE__ */ jsx(
1245
- ChevronDownIcon,
1246
- {
1247
- className: twMerge(
1248
- `mastra:text-text3 mastra:transition-transform mastra:duration-200 mastra:ease-in-out`,
1249
- expanded ? "mastra:rotate-0" : "mastra:-rotate-90",
1250
- className
1251
- ),
1252
- ...props
1253
- }
1254
- ) });
326
+ const signalContentsToUserMessages = (contents, metadata) => {
327
+ const makeUserMessage = (parts) => ({
328
+ id: `signal-${Date.now()}`,
329
+ role: "user",
330
+ createdAt: /* @__PURE__ */ new Date(),
331
+ content: {
332
+ format: 2,
333
+ parts,
334
+ metadata: cloneMetadata(metadata)
335
+ }
336
+ });
337
+ const toMessagePart = (part) => {
338
+ if (!part || typeof part !== "object") return [];
339
+ const typedPart = part;
340
+ if (typedPart.type === "text" && typeof typedPart.text === "string") return [{
341
+ type: "text",
342
+ text: typedPart.text
343
+ }];
344
+ if (typedPart.type === "image") {
345
+ const image = typedPart.image;
346
+ const mimeType = typeof typedPart.mediaType === "string" ? typedPart.mediaType : typeof typedPart.mimeType === "string" ? typedPart.mimeType : "image/*";
347
+ if (typeof image === "string" || image instanceof URL || image instanceof ArrayBuffer || image instanceof Uint8Array) return [{
348
+ type: "file",
349
+ mimeType,
350
+ data: encodeFilePartDataForStorage(image, mimeType)
351
+ }];
352
+ return [{
353
+ type: "file",
354
+ mimeType,
355
+ data: ""
356
+ }];
357
+ }
358
+ if (typedPart.type === "file") {
359
+ const data = typedPart.data;
360
+ const mimeType = typeof typedPart.mediaType === "string" ? typedPart.mediaType : typeof typedPart.mimeType === "string" ? typedPart.mimeType : "application/octet-stream";
361
+ if (typeof data === "string" || data instanceof URL || data instanceof ArrayBuffer || data instanceof Uint8Array) return [{
362
+ type: "file",
363
+ mimeType,
364
+ data: encodeFilePartDataForStorage(data, mimeType),
365
+ ...typeof typedPart.filename === "string" ? { filename: typedPart.filename } : {}
366
+ }];
367
+ return [{
368
+ type: "file",
369
+ mimeType,
370
+ data: typeof typedPart.url === "string" ? typedPart.url : "",
371
+ ...typeof typedPart.filename === "string" ? { filename: typedPart.filename } : {}
372
+ }];
373
+ }
374
+ return [];
375
+ };
376
+ if (typeof contents === "string") return [makeUserMessage([{
377
+ type: "text",
378
+ text: contents
379
+ }])];
380
+ if (Array.isArray(contents)) {
381
+ const parts = contents.flatMap(toMessagePart);
382
+ return parts.length ? [makeUserMessage(parts)] : contents.flatMap((content) => signalContentsToUserMessages(content, metadata));
383
+ }
384
+ if (!contents || typeof contents !== "object") return [];
385
+ const message = contents;
386
+ if (message.role && message.role !== "user") return [];
387
+ const content = message.content;
388
+ if (typeof content === "string") return [makeUserMessage([{
389
+ type: "text",
390
+ text: content
391
+ }])];
392
+ if (!Array.isArray(content)) return [];
393
+ const parts = content.flatMap(toMessagePart);
394
+ return parts.length ? [makeUserMessage(parts)] : [];
1255
395
  };
1256
-
1257
- const ToolApprovalClass = twMerge(
1258
- "mastra:rounded-lg mastra:border mastra:border-border1 mastra:max-w-1/2 mastra:mt-2",
1259
- "mastra:bg-surface3 mastra:text-text6"
1260
- );
1261
- const ToolApproval = ({ className, ...props }) => {
1262
- return /* @__PURE__ */ jsx("div", { className: className || ToolApprovalClass, ...props });
396
+ const reconcilePendingUserEcho = (message, echoedContents, metadata, options) => {
397
+ const echoedParts = signalContentsToUserMessages(echoedContents, metadata)[0]?.content.parts;
398
+ let reconciled = typeof options.signalId === "string" ? {
399
+ ...message,
400
+ id: options.signalId
401
+ } : message;
402
+ if (echoedParts?.length) reconciled = {
403
+ ...reconciled,
404
+ content: {
405
+ ...reconciled.content,
406
+ parts: echoedParts
407
+ }
408
+ };
409
+ return options.keepClientMessageId ? clearPendingStatusKeepClientId(reconciled) : clearPendingStatus(reconciled);
1263
410
  };
1264
- const ToolApprovalTitleClass = twMerge("mastra:text-text6 mastra:inline-flex mastra:items-center mastra:gap-1");
1265
- const ToolApprovalTitle = ({ className, ...props }) => {
1266
- return /* @__PURE__ */ jsx("div", { className: className || ToolApprovalTitleClass, ...props });
411
+ const makeToolInvocationPart = (invocation) => ({
412
+ type: "tool-invocation",
413
+ toolInvocation: invocation
414
+ });
415
+ /**
416
+ * Narrow the chunk to the template-literal passthrough variants from
417
+ * `NetworkChunkType`. Encoded as a type guard so the surrounding `switch`
418
+ * statement can stay exhaustive over the remaining string-literal cases.
419
+ */
420
+ const isTemplateLiteralPassthrough = (chunk) => chunk.type.startsWith("agent-execution-event-") || chunk.type.startsWith("workflow-execution-event-");
421
+ /**
422
+ * Narrow the chunk to the `data-${string}` family. Used so the trailing
423
+ * exhaustiveness check can prove the switch covers every remaining variant.
424
+ */
425
+ const isDataChunk$2 = (chunk) => chunk.type.startsWith("data-");
426
+ /**
427
+ * Reduce a single stream chunk into the running `MastraDBMessage[]`
428
+ * conversation. The accumulator owns the entire chunk→DB mapping for the
429
+ * React SDK; consumers downstream (e.g. `toAISdkV5Messages` in the playground)
430
+ * translate the result into AI SDK / assistant-ui shapes as needed.
431
+ */
432
+ const accumulateChunk = ({ chunk, conversation, metadata }) => {
433
+ const result = [...conversation];
434
+ if (isTemplateLiteralPassthrough(chunk)) return result;
435
+ if (isDataChunk$2(chunk)) {
436
+ if (chunk.type === "data-user-message" && "data" in chunk && (chunk.data?.type === "user-message" || chunk.data?.type === "user")) {
437
+ const signalId = chunk.data.id;
438
+ const echoedClientMessageId = chunk.data?.metadata?.[CLIENT_MESSAGE_ID_KEY];
439
+ if (typeof echoedClientMessageId === "string" && result.some((message) => message.content.metadata?.status === "pending" && message.content.metadata["clientMessageId"] === echoedClientMessageId)) return finishStreamingAssistantMessage(result.map((message) => message.content.metadata?.status === "pending" && message.content.metadata["clientMessageId"] === echoedClientMessageId ? reconcilePendingUserEcho(message, chunk.data.contents, metadata, {
440
+ signalId: typeof signalId === "string" ? signalId : void 0,
441
+ keepClientMessageId: true
442
+ }) : message));
443
+ if (typeof signalId === "string" && result.some((message) => message.id === signalId)) return finishStreamingAssistantMessage(result.map((message) => message.id === signalId && message.content.metadata?.status === "pending" ? reconcilePendingUserEcho(message, chunk.data.contents, metadata, { keepClientMessageId: false }) : message));
444
+ const userMessages = signalContentsToUserMessages(chunk.data.contents, metadata);
445
+ if (!userMessages.length) return result;
446
+ const conversationWithFinishedAssistant = finishStreamingAssistantMessage(result);
447
+ const messageIdPrefix = typeof signalId === "string" ? signalId : `signal-${chunk.runId}-${Date.now()}`;
448
+ return [...conversationWithFinishedAssistant, ...userMessages.map((message, index) => ({
449
+ ...message,
450
+ id: index === 0 ? messageIdPrefix : `${messageIdPrefix}-${index}`
451
+ }))];
452
+ }
453
+ const dataPart = {
454
+ type: chunk.type,
455
+ data: "data" in chunk ? chunk.data : void 0,
456
+ ..."id" in chunk && typeof chunk.id === "string" ? { id: chunk.id } : {}
457
+ };
458
+ const lastMessage = result[result.length - 1];
459
+ if (!lastMessage || lastMessage.role !== "assistant") return appendAssistantMessage(result, `data-${chunk.runId}-${Date.now()}`, [dataPart], metadata);
460
+ return replaceLast(result, withParts(lastMessage, [...lastMessage.content.parts, dataPart]));
461
+ }
462
+ switch (chunk.type) {
463
+ case "tripwire": {
464
+ const newMessage = newAssistantMessage(`tripwire-${chunk.runId + Date.now()}`, [{
465
+ type: "text",
466
+ text: chunk.payload.reason
467
+ }], {
468
+ ...metadata,
469
+ status: "tripwire",
470
+ tripwire: {
471
+ reason: chunk.payload.reason,
472
+ retry: chunk.payload.retry,
473
+ metadata: chunk.payload.metadata,
474
+ processorId: chunk.payload.processorId
475
+ }
476
+ });
477
+ return [...result, newMessage];
478
+ }
479
+ case "start": {
480
+ const messageId = typeof chunk.payload.messageId === "string" ? chunk.payload.messageId : void 0;
481
+ if (messageId && result.some((message) => message.id === messageId)) return result;
482
+ return [...result, newAssistantMessage(messageId ?? `start-${chunk.runId + Date.now()}`, [], metadata)];
483
+ }
484
+ case "text-start": {
485
+ const lastMessage = result[result.length - 1];
486
+ const textId = chunk.payload.id || `text-${Date.now()}`;
487
+ const tailPart = lastMessage?.content.parts[lastMessage.content.parts.length - 1];
488
+ if (chunk.payload.id && lastMessage?.role === "assistant" && tailPart?.type === "text" && partTextId(tailPart) === textId) return result;
489
+ const newTextPart = {
490
+ type: "text",
491
+ text: "",
492
+ state: "streaming",
493
+ textId,
494
+ providerMetadata: chunk.payload.providerMetadata
495
+ };
496
+ if (!lastMessage || lastMessage.role !== "assistant") return appendAssistantMessage(result, `start-${chunk.runId}-${Date.now()}`, [newTextPart], metadata);
497
+ if (lastMessage.content.metadata?.completionResult) return appendAssistantMessage(result, `start-${chunk.runId}-${Date.now()}`, [newTextPart], metadata);
498
+ return replaceLast(result, withParts(lastMessage, [...lastMessage.content.parts, newTextPart]));
499
+ }
500
+ case "background-task-progress": {
501
+ const lastMessage = result[result.length - 1];
502
+ if (!lastMessage || lastMessage.role !== "assistant") return result;
503
+ return replaceLast(result, withMetadata(lastMessage, {
504
+ mode: metadata.mode,
505
+ ...lastMessage.content.metadata,
506
+ runningBackgroundTasksCount: chunk.payload.runningCount
507
+ }));
508
+ }
509
+ case "text-delta": {
510
+ const lastMessage = result[result.length - 1];
511
+ const textId = chunk.payload.id;
512
+ if (!lastMessage || lastMessage.role !== "assistant") {
513
+ const newTextPart = {
514
+ type: "text",
515
+ text: chunk.payload.text,
516
+ state: "streaming",
517
+ textId,
518
+ providerMetadata: chunk.payload.providerMetadata
519
+ };
520
+ return appendAssistantMessage(result, `text-${chunk.runId}-${Date.now()}`, [newTextPart], metadata);
521
+ }
522
+ const parts = [...lastMessage.content.parts];
523
+ let textPartIndex = textId ? parts.findLastIndex((part) => part.type === "text" && partTextId(part) === textId) : -1;
524
+ if (textPartIndex === -1) textPartIndex = parts.findLastIndex((part) => part.type === "text" && partState(part) === "streaming");
525
+ if (textPartIndex !== -1 && parts.some((part, index) => index > textPartIndex && part.type !== "text")) textPartIndex = -1;
526
+ if (textPartIndex === -1) {
527
+ const newTextPart = {
528
+ type: "text",
529
+ text: chunk.payload.text,
530
+ state: "streaming",
531
+ textId,
532
+ providerMetadata: chunk.payload.providerMetadata
533
+ };
534
+ parts.push(newTextPart);
535
+ } else {
536
+ const textPart = parts[textPartIndex];
537
+ parts[textPartIndex] = {
538
+ ...textPart,
539
+ text: textPart.text + chunk.payload.text,
540
+ state: "streaming"
541
+ };
542
+ }
543
+ return replaceLast(result, withParts(lastMessage, parts));
544
+ }
545
+ case "text-end": return result;
546
+ case "reasoning-start": {
547
+ const lastMessage = result[result.length - 1];
548
+ const newReasoningPart = {
549
+ type: "reasoning",
550
+ reasoning: "",
551
+ state: "streaming",
552
+ providerMetadata: chunk.payload.providerMetadata
553
+ };
554
+ if (!lastMessage || lastMessage.role !== "assistant") return appendAssistantMessage(result, `reasoning-${chunk.runId + Date.now()}`, [newReasoningPart], metadata);
555
+ return replaceLast(result, withParts(lastMessage, [...lastMessage.content.parts, newReasoningPart]));
556
+ }
557
+ case "reasoning-delta": {
558
+ const lastMessage = result[result.length - 1];
559
+ if (!lastMessage || lastMessage.role !== "assistant") {
560
+ const newReasoningPart = {
561
+ type: "reasoning",
562
+ reasoning: chunk.payload.text,
563
+ state: "streaming",
564
+ providerMetadata: chunk.payload.providerMetadata
565
+ };
566
+ return appendAssistantMessage(result, `reasoning-${chunk.runId + Date.now()}`, [newReasoningPart], metadata);
567
+ }
568
+ const parts = [...lastMessage.content.parts];
569
+ const lastIndex = parts.length - 1;
570
+ const lastPart = parts[lastIndex];
571
+ if (lastPart?.type === "reasoning") {
572
+ const reasoningPart = lastPart;
573
+ parts[lastIndex] = {
574
+ ...reasoningPart,
575
+ reasoning: reasoningPart.reasoning + chunk.payload.text,
576
+ state: "streaming"
577
+ };
578
+ } else {
579
+ const newReasoningPart = {
580
+ type: "reasoning",
581
+ reasoning: chunk.payload.text,
582
+ state: "streaming",
583
+ providerMetadata: chunk.payload.providerMetadata
584
+ };
585
+ parts.push(newReasoningPart);
586
+ }
587
+ return replaceLast(result, withParts(lastMessage, parts));
588
+ }
589
+ case "reasoning-end": {
590
+ const lastMessage = result[result.length - 1];
591
+ if (!lastMessage || lastMessage.role !== "assistant") return result;
592
+ const parts = [...lastMessage.content.parts];
593
+ const reasoningIndex = parts.findLastIndex((part) => part.type === "reasoning" && partState(part) === "streaming");
594
+ if (reasoningIndex === -1) return result;
595
+ const reasoningPart = parts[reasoningIndex];
596
+ const existingMeta = reasoningPart.providerMetadata;
597
+ const endMeta = chunk.payload.providerMetadata;
598
+ parts[reasoningIndex] = {
599
+ ...reasoningPart,
600
+ state: "done",
601
+ ...existingMeta || endMeta ? { providerMetadata: {
602
+ ...existingMeta ?? {},
603
+ ...endMeta ?? {}
604
+ } } : {}
605
+ };
606
+ return replaceLast(result, withParts(lastMessage, parts));
607
+ }
608
+ case "reasoning-signature": {
609
+ const lastMessage = result[result.length - 1];
610
+ if (!lastMessage || lastMessage.role !== "assistant") return result;
611
+ const parts = [...lastMessage.content.parts];
612
+ const reasoningIndex = parts.findLastIndex((part) => part.type === "reasoning");
613
+ if (reasoningIndex === -1) return result;
614
+ const reasoningPart = parts[reasoningIndex];
615
+ const existingMeta = reasoningPart.providerMetadata;
616
+ const sigMeta = chunk.payload.providerMetadata;
617
+ parts[reasoningIndex] = {
618
+ ...reasoningPart,
619
+ ...existingMeta || sigMeta ? { providerMetadata: {
620
+ ...existingMeta ?? {},
621
+ ...sigMeta ?? {}
622
+ } } : {}
623
+ };
624
+ return replaceLast(result, withParts(lastMessage, parts));
625
+ }
626
+ case "redacted-reasoning": {
627
+ const lastMessage = result[result.length - 1];
628
+ const redactedData = chunk.payload.data;
629
+ const redactedPart = {
630
+ type: "reasoning",
631
+ reasoning: typeof redactedData === "string" ? redactedData : "",
632
+ state: "done",
633
+ redacted: true,
634
+ providerMetadata: chunk.payload.providerMetadata
635
+ };
636
+ if (!lastMessage || lastMessage.role !== "assistant") return appendAssistantMessage(result, `redacted-reasoning-${chunk.runId + Date.now()}`, [redactedPart], metadata);
637
+ return replaceLast(result, withParts(lastMessage, [...lastMessage.content.parts, redactedPart]));
638
+ }
639
+ case "tool-call": {
640
+ const invocation = {
641
+ state: "call",
642
+ toolCallId: chunk.payload.toolCallId,
643
+ toolName: chunk.payload.toolName,
644
+ args: chunk.payload.args
645
+ };
646
+ const newPart = {
647
+ ...makeToolInvocationPart(invocation),
648
+ providerMetadata: chunk.payload.providerMetadata
649
+ };
650
+ const existing = locateToolPart(result, chunk.payload.toolCallId, false);
651
+ if (existing && existing.toolPartIndex >= 0) {
652
+ const { messageIndex, toolPartIndex } = existing;
653
+ const targetMessage = result[messageIndex];
654
+ if (targetMessage && targetMessage.role === "assistant") {
655
+ const parts = [...targetMessage.content.parts];
656
+ const prev = parts[toolPartIndex];
657
+ if (isToolPart(prev)) {
658
+ const { argsText: _argsText, ...rest } = prev;
659
+ parts[toolPartIndex] = {
660
+ ...rest,
661
+ toolInvocation: {
662
+ ...prev.toolInvocation,
663
+ state: "call",
664
+ toolName: chunk.payload.toolName,
665
+ toolCallId: chunk.payload.toolCallId,
666
+ args: chunk.payload.args
667
+ },
668
+ providerMetadata: chunk.payload.providerMetadata ?? prev.providerMetadata
669
+ };
670
+ return replaceAt(result, messageIndex, withParts(targetMessage, parts));
671
+ }
672
+ }
673
+ }
674
+ const lastMessage = result[result.length - 1];
675
+ if (!lastMessage || lastMessage.role !== "assistant") return appendAssistantMessage(result, `tool-call-${chunk.runId + Date.now()}`, [newPart], metadata);
676
+ return replaceLast(result, withParts(lastMessage, [...lastMessage.content.parts, newPart]));
677
+ }
678
+ case "tool-call-input-streaming-start": {
679
+ const lastMessage = result[result.length - 1];
680
+ const invocation = {
681
+ state: "partial-call",
682
+ toolCallId: chunk.payload.toolCallId,
683
+ toolName: chunk.payload.toolName,
684
+ args: {}
685
+ };
686
+ const newPart = {
687
+ ...makeToolInvocationPart(invocation),
688
+ argsText: ""
689
+ };
690
+ if (!lastMessage || lastMessage.role !== "assistant") return appendAssistantMessage(result, `tool-call-streaming-${chunk.runId + Date.now()}`, [newPart], metadata);
691
+ return replaceLast(result, withParts(lastMessage, [...lastMessage.content.parts, newPart]));
692
+ }
693
+ case "tool-call-delta": {
694
+ const location = locateToolPart(result, chunk.payload.toolCallId, false);
695
+ if (!location || location.toolPartIndex < 0) return result;
696
+ const { messageIndex, toolPartIndex } = location;
697
+ const targetMessage = result[messageIndex];
698
+ if (!targetMessage || targetMessage.role !== "assistant") return result;
699
+ const parts = [...targetMessage.content.parts];
700
+ const toolPart = parts[toolPartIndex];
701
+ if (!isToolPart(toolPart)) return result;
702
+ const nextArgsText = (toolPart.argsText ?? "") + (chunk.payload.argsTextDelta ?? "");
703
+ parts[toolPartIndex] = {
704
+ ...toolPart,
705
+ argsText: nextArgsText,
706
+ toolInvocation: {
707
+ ...toolPart.toolInvocation,
708
+ state: "partial-call"
709
+ }
710
+ };
711
+ return replaceAt(result, messageIndex, withParts(targetMessage, parts));
712
+ }
713
+ case "tool-call-input-streaming-end": {
714
+ const location = locateToolPart(result, chunk.payload.toolCallId, false);
715
+ if (!location || location.toolPartIndex < 0) return result;
716
+ const { messageIndex, toolPartIndex } = location;
717
+ const targetMessage = result[messageIndex];
718
+ if (!targetMessage || targetMessage.role !== "assistant") return result;
719
+ const parts = [...targetMessage.content.parts];
720
+ const toolPart = parts[toolPartIndex];
721
+ if (!isToolPart(toolPart)) return result;
722
+ let parsedArgs = {};
723
+ const argsText = toolPart.argsText;
724
+ if (typeof argsText === "string" && argsText.length > 0) try {
725
+ const maybe = JSON.parse(argsText);
726
+ if (maybe && typeof maybe === "object" && !Array.isArray(maybe)) parsedArgs = maybe;
727
+ } catch {
728
+ parsedArgs = {};
729
+ }
730
+ parts[toolPartIndex] = {
731
+ ...toolPart,
732
+ toolInvocation: {
733
+ ...toolPart.toolInvocation,
734
+ state: "call",
735
+ args: parsedArgs
736
+ }
737
+ };
738
+ return replaceAt(result, messageIndex, withParts(targetMessage, parts));
739
+ }
740
+ case "tool-output-denied": {
741
+ const location = locateToolPart(result, chunk.payload.toolCallId, false);
742
+ if (!location || location.toolPartIndex < 0) return result;
743
+ const { messageIndex, toolPartIndex } = location;
744
+ const targetMessage = result[messageIndex];
745
+ if (!targetMessage || targetMessage.role !== "assistant") return result;
746
+ const parts = [...targetMessage.content.parts];
747
+ const toolPart = parts[toolPartIndex];
748
+ if (!isToolPart(toolPart)) return result;
749
+ parts[toolPartIndex] = {
750
+ ...toolPart,
751
+ toolInvocation: {
752
+ ...toolPart.toolInvocation,
753
+ state: "output-denied",
754
+ toolName: chunk.payload.toolName,
755
+ args: chunk.payload.args ?? toolPart.toolInvocation.args,
756
+ approval: chunk.payload.approval
757
+ }
758
+ };
759
+ return replaceAt(result, messageIndex, withParts(targetMessage, parts));
760
+ }
761
+ case "tool-error":
762
+ case "tool-result":
763
+ case "background-task-completed":
764
+ case "background-task-failed": {
765
+ const isBgTaskEvent = chunk.type === "background-task-completed" || chunk.type === "background-task-failed";
766
+ const location = locateToolPart(result, chunk.payload.toolCallId, isBgTaskEvent);
767
+ if (!location) return result;
768
+ const { messageIndex, toolPartIndex } = location;
769
+ const targetMessage = result[messageIndex];
770
+ if (!targetMessage || targetMessage.role !== "assistant") return result;
771
+ const parts = [...targetMessage.content.parts];
772
+ const toolPart = toolPartIndex >= 0 ? parts[toolPartIndex] : void 0;
773
+ let payloadResult;
774
+ let payloadError;
775
+ let payloadIsError = false;
776
+ let payloadProviderMetadata;
777
+ let payloadCompletedAt;
778
+ let payloadTaskId;
779
+ switch (chunk.type) {
780
+ case "tool-result":
781
+ payloadResult = chunk.payload.result;
782
+ payloadIsError = Boolean(chunk.payload.isError);
783
+ payloadProviderMetadata = chunk.payload.providerMetadata;
784
+ break;
785
+ case "tool-error":
786
+ payloadError = chunk.payload.error;
787
+ payloadProviderMetadata = chunk.payload.providerMetadata;
788
+ break;
789
+ case "background-task-completed":
790
+ payloadResult = chunk.payload.result;
791
+ payloadCompletedAt = chunk.payload.completedAt;
792
+ payloadTaskId = chunk.payload.taskId;
793
+ break;
794
+ case "background-task-failed":
795
+ payloadError = chunk.payload.error;
796
+ payloadCompletedAt = chunk.payload.completedAt;
797
+ payloadTaskId = chunk.payload.taskId;
798
+ break;
799
+ }
800
+ if (toolPart && isToolPart(toolPart)) {
801
+ const { toolName, toolCallId, args } = toolPart.toolInvocation;
802
+ const providerMeta = payloadProviderMetadata ?? toolPart.providerMetadata;
803
+ if (chunk.type === "tool-error" || chunk.type === "background-task-failed" || payloadIsError) {
804
+ const error = chunk.type === "tool-error" || chunk.type === "background-task-failed" ? payloadError : payloadResult;
805
+ const errorText = typeof error === "string" ? error : error instanceof Error ? error.message : error?.message ?? String(error);
806
+ parts[toolPartIndex] = {
807
+ ...toolPart,
808
+ providerMetadata: providerMeta,
809
+ toolInvocation: {
810
+ state: "output-error",
811
+ toolCallId,
812
+ toolName,
813
+ args,
814
+ errorText
815
+ }
816
+ };
817
+ } else {
818
+ const resultObj = payloadResult;
819
+ const existingResult = toolPart.toolInvocation.state === "partial-call" || toolPart.toolInvocation.state === "result" ? toolPart.toolInvocation.result : void 0;
820
+ const existingLooksLikeWorkflow = Boolean(existingResult && typeof existingResult === "object" && "steps" in existingResult);
821
+ const isWorkflow = Boolean(resultObj?.result?.steps) || toolName?.startsWith("workflow-") || existingLooksLikeWorkflow;
822
+ const isAgent = chunk.from === "AGENT";
823
+ let output;
824
+ if (isWorkflow) {
825
+ const accumulated = existingLooksLikeWorkflow && existingResult && typeof existingResult === "object" ? existingResult : void 0;
826
+ const payloadWorkflow = resultObj?.result && typeof resultObj.result === "object" ? resultObj.result : void 0;
827
+ if (accumulated || payloadWorkflow) output = {
828
+ ...accumulated ?? {},
829
+ ...payloadWorkflow ?? {},
830
+ steps: payloadWorkflow?.steps ?? accumulated?.steps ?? [],
831
+ status: payloadWorkflow?.status ?? accumulated?.status ?? "success",
832
+ output: payloadResult
833
+ };
834
+ else output = payloadResult;
835
+ } else if (isAgent) {
836
+ const existingOutput = toolPart.toolInvocation.state === "result" ? toolPart.toolInvocation.result : void 0;
837
+ const existingChild = existingOutput?.childMessages;
838
+ output = existingOutput ? {
839
+ ...payloadResult,
840
+ childMessages: existingChild?.length ? existingChild : resultObj?.childMessages
841
+ } : payloadResult;
842
+ } else output = payloadResult;
843
+ parts[toolPartIndex] = {
844
+ ...toolPart,
845
+ providerMetadata: providerMeta,
846
+ toolInvocation: {
847
+ state: "result",
848
+ toolCallId,
849
+ toolName,
850
+ args,
851
+ result: output
852
+ }
853
+ };
854
+ }
855
+ }
856
+ const nextMetadata = mergeBgTaskMetadata(targetMessage.content.metadata, metadata.mode, {
857
+ resetRunningCount: isBgTaskEvent,
858
+ perTaskEntry: isBgTaskEvent && payloadTaskId ? {
859
+ toolCallId: chunk.payload.toolCallId,
860
+ completedAt: payloadCompletedAt,
861
+ taskId: payloadTaskId
862
+ } : void 0
863
+ });
864
+ const nextMessage = {
865
+ ...targetMessage,
866
+ content: {
867
+ ...targetMessage.content,
868
+ parts,
869
+ metadata: nextMetadata
870
+ }
871
+ };
872
+ return replaceAt(result, messageIndex, nextMessage);
873
+ }
874
+ case "background-task-running": {
875
+ const location = locateToolPart(result, chunk.payload.toolCallId, true);
876
+ if (!location) return result;
877
+ const { messageIndex } = location;
878
+ const targetMessage = result[messageIndex];
879
+ if (!targetMessage || targetMessage.role !== "assistant") return result;
880
+ const nextMetadata = mergeBgTaskMetadata(targetMessage.content.metadata, metadata.mode, { perTaskEntry: {
881
+ toolCallId: chunk.payload.toolCallId,
882
+ startedAt: chunk.payload.startedAt,
883
+ taskId: chunk.payload.taskId
884
+ } });
885
+ return replaceAt(result, messageIndex, withMetadata(targetMessage, nextMetadata));
886
+ }
887
+ case "tool-output":
888
+ case "background-task-output": {
889
+ const isBgTaskOutput = chunk.type === "background-task-output";
890
+ const location = locateToolPart(result, chunk.payload.toolCallId, isBgTaskOutput);
891
+ if (!location || location.toolPartIndex < 0) return result;
892
+ const { messageIndex, toolPartIndex } = location;
893
+ const targetMessage = result[messageIndex];
894
+ if (!targetMessage || targetMessage.role !== "assistant") return result;
895
+ const parts = [...targetMessage.content.parts];
896
+ const toolPart = parts[toolPartIndex];
897
+ if (!isToolPart(toolPart)) return result;
898
+ const { toolName, toolCallId, args } = toolPart.toolInvocation;
899
+ const payloadOutput = chunk.type === "background-task-output" ? chunk.payload.payload.payload.output : chunk.payload.output;
900
+ if (payloadOutput?.type?.startsWith("workflow-")) {
901
+ const existingWorkflowState = toolPart.toolInvocation.result || {};
902
+ const updated = mapWorkflowStreamChunkToWatchResult(existingWorkflowState, payloadOutput);
903
+ parts[toolPartIndex] = {
904
+ ...toolPart,
905
+ toolInvocation: {
906
+ state: "partial-call",
907
+ toolCallId,
908
+ toolName,
909
+ args,
910
+ result: updated
911
+ }
912
+ };
913
+ } else if (payloadOutput?.from === "AGENT" || payloadOutput?.from === "USER" && payloadOutput?.payload?.output?.type?.startsWith("workflow-")) return accumulateAgentChunk(payloadOutput, result, metadata, toolCallId, toolName);
914
+ else {
915
+ const currentResult = toolPart.toolInvocation.result;
916
+ const existing = Array.isArray(currentResult) ? currentResult : [];
917
+ parts[toolPartIndex] = {
918
+ ...toolPart,
919
+ toolInvocation: {
920
+ state: "partial-call",
921
+ toolCallId,
922
+ toolName,
923
+ args,
924
+ result: [...existing, payloadOutput]
925
+ }
926
+ };
927
+ }
928
+ return replaceAt(result, messageIndex, withParts(targetMessage, parts));
929
+ }
930
+ case "is-task-complete": {
931
+ if (chunk.payload.suppressFeedback) return result;
932
+ const feedback = formatStreamCompletionFeedback({
933
+ complete: chunk.payload.passed,
934
+ scorers: chunk.payload.results,
935
+ totalDuration: chunk.payload.duration,
936
+ timedOut: chunk.payload.timedOut,
937
+ completionReason: chunk.payload.reason
938
+ }, chunk.payload.maxIterationReached);
939
+ const newMessage = newAssistantMessage(`is-task-complete-${chunk.runId + Date.now()}`, [{
940
+ type: "text",
941
+ text: feedback
942
+ }], {
943
+ ...metadata,
944
+ completionResult: { passed: chunk.payload.passed }
945
+ });
946
+ return [...result, newMessage];
947
+ }
948
+ case "source": {
949
+ const lastMessage = result[result.length - 1];
950
+ if (!lastMessage || lastMessage.role !== "assistant") return result;
951
+ const parts = [...lastMessage.content.parts];
952
+ if (chunk.payload.sourceType === "url") {
953
+ const sourceUrlPart = {
954
+ type: "source-url",
955
+ sourceId: chunk.payload.id,
956
+ url: chunk.payload.url || "",
957
+ title: chunk.payload.title,
958
+ providerMetadata: chunk.payload.providerMetadata
959
+ };
960
+ parts.push(sourceUrlPart);
961
+ } else if (chunk.payload.sourceType === "document") parts.push({
962
+ type: "source-document",
963
+ sourceId: chunk.payload.id,
964
+ mediaType: chunk.payload.mimeType || "application/octet-stream",
965
+ title: chunk.payload.title,
966
+ filename: chunk.payload.filename,
967
+ providerMetadata: chunk.payload.providerMetadata
968
+ });
969
+ return replaceLast(result, withParts(lastMessage, parts));
970
+ }
971
+ case "file": {
972
+ const lastMessage = result[result.length - 1];
973
+ if (!lastMessage || lastMessage.role !== "assistant") return result;
974
+ const parts = [...lastMessage.content.parts];
975
+ let data;
976
+ if (typeof chunk.payload.data === "string") data = chunk.payload.base64 ? `data:${chunk.payload.mimeType};base64,${chunk.payload.data}` : `data:${chunk.payload.mimeType},${encodeURIComponent(chunk.payload.data)}`;
977
+ else {
978
+ const base64 = uint8ArrayToBase64(chunk.payload.data);
979
+ data = `data:${chunk.payload.mimeType};base64,${base64}`;
980
+ }
981
+ parts.push({
982
+ type: "file",
983
+ mimeType: chunk.payload.mimeType,
984
+ data,
985
+ providerMetadata: chunk.payload.providerMetadata
986
+ });
987
+ return replaceLast(result, withParts(lastMessage, parts));
988
+ }
989
+ case "tool-call-approval": {
990
+ const lastMessage = result[result.length - 1];
991
+ if (!lastMessage || lastMessage.role !== "assistant") return result;
992
+ const existingMeta = lastMessage.content.metadata ?? {};
993
+ const lastRequireApproval = existingMeta.mode === "stream" ? existingMeta.requireApprovalMetadata ?? {} : {};
994
+ return replaceLast(result, {
995
+ ...lastMessage,
996
+ content: {
997
+ ...lastMessage.content,
998
+ metadata: {
999
+ ...existingMeta,
1000
+ mode: "stream",
1001
+ requireApprovalMetadata: {
1002
+ ...lastRequireApproval,
1003
+ [chunk.payload.toolName]: {
1004
+ toolCallId: chunk.payload.toolCallId,
1005
+ toolName: chunk.payload.toolName,
1006
+ args: chunk.payload.args
1007
+ }
1008
+ }
1009
+ }
1010
+ }
1011
+ });
1012
+ }
1013
+ case "tool-call-suspended":
1014
+ case "background-task-suspended": {
1015
+ const isBgTaskEvent = chunk.type === "background-task-suspended";
1016
+ let suspToolCallId;
1017
+ let suspToolName;
1018
+ let suspArgs;
1019
+ let suspPayload;
1020
+ let suspSuspendedAt;
1021
+ let suspTaskId;
1022
+ if (chunk.type === "background-task-suspended") {
1023
+ suspToolCallId = chunk.payload.toolCallId;
1024
+ suspToolName = chunk.payload.toolName;
1025
+ suspArgs = chunk.payload.args;
1026
+ suspPayload = chunk.payload.suspendPayload;
1027
+ suspSuspendedAt = chunk.payload.suspendedAt;
1028
+ suspTaskId = chunk.payload.taskId;
1029
+ } else {
1030
+ suspToolCallId = chunk.payload.toolCallId;
1031
+ suspToolName = chunk.payload.toolName;
1032
+ suspArgs = chunk.payload.args;
1033
+ suspPayload = chunk.payload.suspendPayload;
1034
+ }
1035
+ const location = isBgTaskEvent ? locateToolPart(result, suspToolCallId, true) : { messageIndex: result.length - 1 };
1036
+ if (!location) return result;
1037
+ const { messageIndex } = location;
1038
+ const targetMessage = result[messageIndex];
1039
+ if (!targetMessage || targetMessage.role !== "assistant") return result;
1040
+ const existingMeta = targetMessage.content.metadata ?? {};
1041
+ const lastSuspendedTools = existingMeta.mode === "stream" ? existingMeta.suspendedTools ?? {} : {};
1042
+ const nextMetadata = mergeBgTaskMetadata(existingMeta, "stream", {
1043
+ resetRunningCount: isBgTaskEvent,
1044
+ perTaskEntry: isBgTaskEvent && suspTaskId ? {
1045
+ toolCallId: suspToolCallId,
1046
+ suspendedAt: suspSuspendedAt,
1047
+ taskId: suspTaskId
1048
+ } : void 0
1049
+ }, { suspendedTools: {
1050
+ ...lastSuspendedTools,
1051
+ [suspToolName]: {
1052
+ toolCallId: suspToolCallId,
1053
+ toolName: suspToolName,
1054
+ args: suspArgs,
1055
+ suspendPayload: suspPayload,
1056
+ runId: chunk.runId
1057
+ }
1058
+ } });
1059
+ return replaceAt(result, messageIndex, withMetadata(targetMessage, nextMetadata));
1060
+ }
1061
+ case "finish":
1062
+ case "abort": return finishStreamingAssistantMessage(result);
1063
+ case "error": {
1064
+ const newMessage = newAssistantMessage(`error-${chunk.runId + Date.now()}`, [{
1065
+ type: "text",
1066
+ text: typeof chunk.payload.error === "string" ? chunk.payload.error : JSON.stringify(chunk.payload.error)
1067
+ }], {
1068
+ ...metadata,
1069
+ status: "error"
1070
+ });
1071
+ return [...result, newMessage];
1072
+ }
1073
+ case "step-start": {
1074
+ const stepMessageId = typeof chunk.payload?.messageId === "string" ? chunk.payload.messageId : void 0;
1075
+ if (!stepMessageId) return result;
1076
+ const lastMessage = result[result.length - 1];
1077
+ if (!lastMessage || lastMessage.role !== "assistant") return result;
1078
+ if (result.some((message) => message.id === stepMessageId)) return result;
1079
+ if (!lastMessage.content.parts.some((part) => !String(part.type).startsWith("data-"))) return replaceLast(result, {
1080
+ ...lastMessage,
1081
+ id: stepMessageId
1082
+ });
1083
+ return appendAssistantMessage(finishStreamingAssistantMessage(result), stepMessageId, [], metadata);
1084
+ }
1085
+ case "step-finish":
1086
+ case "step-output":
1087
+ case "raw":
1088
+ case "watch":
1089
+ case "response-metadata": return result;
1090
+ case "goal": return result;
1091
+ case "object":
1092
+ case "object-result": return result;
1093
+ case "background-task-started":
1094
+ case "background-task-cancelled":
1095
+ case "background-task-resumed": return result;
1096
+ case "workflow-start":
1097
+ case "workflow-finish":
1098
+ case "workflow-canceled":
1099
+ case "workflow-paused":
1100
+ case "workflow-step-start":
1101
+ case "workflow-step-finish":
1102
+ case "workflow-step-suspended":
1103
+ case "workflow-step-waiting":
1104
+ case "workflow-step-output":
1105
+ case "workflow-step-progress":
1106
+ case "workflow-step-result": return result;
1107
+ case "agent-execution-start":
1108
+ case "agent-execution-approval":
1109
+ case "agent-execution-suspended":
1110
+ case "agent-execution-end":
1111
+ case "agent-execution-abort":
1112
+ case "tool-execution-start":
1113
+ case "tool-execution-end":
1114
+ case "tool-execution-approval":
1115
+ case "tool-execution-suspended":
1116
+ case "tool-execution-abort":
1117
+ case "routing-agent-start":
1118
+ case "routing-agent-text-delta":
1119
+ case "routing-agent-text-start":
1120
+ case "routing-agent-end":
1121
+ case "routing-agent-abort":
1122
+ case "workflow-execution-start":
1123
+ case "workflow-execution-end":
1124
+ case "workflow-execution-suspended":
1125
+ case "workflow-execution-abort":
1126
+ case "network-execution-event-step-finish":
1127
+ case "network-execution-event-finish":
1128
+ case "network-validation-start":
1129
+ case "network-validation-end":
1130
+ case "network-object":
1131
+ case "network-object-result":
1132
+ case "tool-output-denied": return result;
1133
+ default: return assertExhaustive(chunk, result);
1134
+ }
1267
1135
  };
1268
- const ToolApprovalHeaderClass = twMerge(
1269
- "mastra:flex mastra:justify-between mastra:items-center mastra:gap-2",
1270
- "mastra:border-b mastra:border-border1 mastra:px-4 mastra:py-2"
1271
- );
1272
- const ToolApprovalHeader = ({ className, ...props }) => {
1273
- return /* @__PURE__ */ jsx("div", { className: className || ToolApprovalHeaderClass, ...props });
1136
+ /**
1137
+ * Compile-time exhaustiveness helper. At runtime, returns the conversation
1138
+ * unchanged so unexpected chunk variants never throw inside the React stream
1139
+ * pump; TypeScript will fail to compile if `ChunkType` ever grows a new branch
1140
+ * that isn't enumerated above.
1141
+ */
1142
+ const assertExhaustive = (_chunk, fallback) => fallback;
1143
+ const accumulateAgentChunk = (chunk, conversation, _metadata, parentToolCallId, parentToolName) => {
1144
+ const lastMessage = conversation[conversation.length - 1];
1145
+ if (!lastMessage || lastMessage.role !== "assistant") return conversation;
1146
+ const parts = [...lastMessage.content.parts];
1147
+ const findToolPartIndex = () => parts.findIndex((part) => isToolPart(part) && (parentToolCallId && part.toolInvocation.toolCallId === parentToolCallId || parentToolName && part.toolInvocation.toolName === parentToolName));
1148
+ if (chunk.type === "text-delta") {
1149
+ const agentChunk = chunk.payload;
1150
+ const toolPartIndex = findToolPartIndex();
1151
+ if (toolPartIndex === -1) return conversation;
1152
+ const toolPart = parts[toolPartIndex];
1153
+ const existingResult = toolPart.toolInvocation.result || {};
1154
+ const childMessages = existingResult.childMessages || [];
1155
+ const lastChildMessage = childMessages[childMessages.length - 1];
1156
+ const textMessage = {
1157
+ type: "text",
1158
+ content: (lastChildMessage?.content || "") + agentChunk.text
1159
+ };
1160
+ const nextChildren = lastChildMessage?.type === "text" ? [...childMessages.slice(0, -1), textMessage] : [...childMessages, textMessage];
1161
+ parts[toolPartIndex] = {
1162
+ ...toolPart,
1163
+ toolInvocation: {
1164
+ ...toolPart.toolInvocation,
1165
+ result: {
1166
+ ...existingResult,
1167
+ childMessages: nextChildren
1168
+ }
1169
+ }
1170
+ };
1171
+ } else if (chunk.type === "tool-call") {
1172
+ const agentChunk = chunk.payload;
1173
+ const toolPartIndex = findToolPartIndex();
1174
+ if (toolPartIndex === -1) return conversation;
1175
+ const toolPart = parts[toolPartIndex];
1176
+ const existingResult = toolPart.toolInvocation.result || {};
1177
+ const childMessages = existingResult.childMessages || [];
1178
+ parts[toolPartIndex] = {
1179
+ ...toolPart,
1180
+ toolInvocation: {
1181
+ ...toolPart.toolInvocation,
1182
+ result: {
1183
+ ...existingResult,
1184
+ childMessages: [...childMessages, {
1185
+ type: "tool",
1186
+ toolCallId: agentChunk.toolCallId,
1187
+ toolName: agentChunk.toolName,
1188
+ args: agentChunk.args
1189
+ }]
1190
+ }
1191
+ }
1192
+ };
1193
+ } else if (chunk.type === "tool-output") {
1194
+ const agentChunk = chunk.payload;
1195
+ const toolPartIndex = findToolPartIndex();
1196
+ if (toolPartIndex === -1) return conversation;
1197
+ const toolPart = parts[toolPartIndex];
1198
+ if (agentChunk?.output?.type?.startsWith("workflow-")) {
1199
+ const existingResult = toolPart.toolInvocation.result || {};
1200
+ const childMessages = existingResult.childMessages || [];
1201
+ const lastIndex = childMessages.length - 1;
1202
+ const currentMessage = childMessages[lastIndex];
1203
+ const actualExistingWorkflowState = currentMessage?.toolOutput || {};
1204
+ const updated = mapWorkflowStreamChunkToWatchResult(actualExistingWorkflowState, agentChunk.output);
1205
+ if (lastIndex >= 0 && childMessages[lastIndex]?.type === "tool") parts[toolPartIndex] = {
1206
+ ...toolPart,
1207
+ toolInvocation: {
1208
+ ...toolPart.toolInvocation,
1209
+ result: {
1210
+ ...existingResult,
1211
+ childMessages: [...childMessages.slice(0, -1), {
1212
+ ...currentMessage,
1213
+ toolOutput: {
1214
+ ...updated,
1215
+ runId: agentChunk.output.runId
1216
+ }
1217
+ }]
1218
+ }
1219
+ }
1220
+ };
1221
+ }
1222
+ } else if (chunk.type === "tool-result") {
1223
+ const agentChunk = chunk.payload;
1224
+ const toolPartIndex = findToolPartIndex();
1225
+ if (toolPartIndex === -1) return conversation;
1226
+ const toolPart = parts[toolPartIndex];
1227
+ const existingResult = toolPart.toolInvocation.result || {};
1228
+ const childMessages = existingResult.childMessages || [];
1229
+ const lastIndex = childMessages.length - 1;
1230
+ const isWorkflow = agentChunk?.toolName?.startsWith("workflow-");
1231
+ if (lastIndex >= 0 && childMessages[lastIndex]?.type === "tool") parts[toolPartIndex] = {
1232
+ ...toolPart,
1233
+ toolInvocation: {
1234
+ ...toolPart.toolInvocation,
1235
+ result: {
1236
+ ...existingResult,
1237
+ childMessages: [...childMessages.slice(0, -1), {
1238
+ ...childMessages[lastIndex],
1239
+ toolOutput: isWorkflow ? {
1240
+ ...agentChunk.result?.result,
1241
+ runId: agentChunk.result?.runId
1242
+ } : agentChunk.result
1243
+ }]
1244
+ }
1245
+ }
1246
+ };
1247
+ }
1248
+ return replaceLast(conversation, withParts(lastMessage, parts));
1274
1249
  };
1275
- const ToolApprovalContentClass = twMerge("mastra:text-text6 mastra:p-4");
1276
- const ToolApprovalContent = ({ className, ...props }) => {
1277
- return /* @__PURE__ */ jsx("div", { className: className || ToolApprovalContentClass, ...props });
1250
+ const networkMode = (metadata) => ({
1251
+ ...metadata,
1252
+ mode: "network"
1253
+ });
1254
+ const findPartIndex = (parts, predicate) => parts.findIndex(predicate);
1255
+ const isDynamicToolPart = (part) => part.type === "dynamic-tool";
1256
+ const lastAssistant = (conversation) => {
1257
+ const last = conversation[conversation.length - 1];
1258
+ return last && last.role === "assistant" ? last : void 0;
1278
1259
  };
1279
- const ToolApprovalActionsClass = twMerge("mastra:flex mastra:gap-2 mastra:items-center");
1280
- const ToolApprovalActions = ({ className, ...props }) => {
1281
- return /* @__PURE__ */ jsx("div", { className: className || ToolApprovalActionsClass, ...props });
1260
+ /**
1261
+ * Try to parse the buffered routing-agent text as a JSON object. Returns the
1262
+ * parsed object on success, or `null` while the buffer is still incomplete or
1263
+ * not JSON at all.
1264
+ */
1265
+ const tryParseRoutingDecision = (buffered) => {
1266
+ const trimmed = buffered.trim();
1267
+ if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) return null;
1268
+ try {
1269
+ const parsed = JSON.parse(trimmed);
1270
+ if (parsed && typeof parsed === "object") return parsed;
1271
+ return null;
1272
+ } catch {
1273
+ return null;
1274
+ }
1282
1275
  };
1283
-
1284
- const EntryClass = "mastra:space-y-2";
1285
- const Entry = ({ className, ...props }) => {
1286
- return /* @__PURE__ */ jsx("div", { className: className || EntryClass, ...props });
1276
+ /**
1277
+ * Routing-agent text deltas describe the network's routing decision (often a
1278
+ * JSON object such as `{ "isNetwork": true, "agentId": "...", ... }`). The raw
1279
+ * payload is never useful in the rendered thread, so we buffer it in
1280
+ * message-level metadata, promote it to `routingDecision` when it parses as
1281
+ * JSON, and fall back to `routingDecisionText` for non-JSON routing models.
1282
+ * No visible text part is produced.
1283
+ */
1284
+ const handleRoutingAgentDelta = (chunk, conversation, metadata) => {
1285
+ const delta = chunk.payload?.text ?? "";
1286
+ if (!delta) return conversation;
1287
+ const lastMessage = lastAssistant(conversation);
1288
+ const mergeRoutingMetadata = (existing) => {
1289
+ const buffered = (existing.routingDecisionBuffer ?? "") + delta;
1290
+ const next = {
1291
+ ...cloneMetadata(existing),
1292
+ mode: "network"
1293
+ };
1294
+ const parsed = tryParseRoutingDecision(buffered);
1295
+ if (parsed) {
1296
+ next.routingDecision = parsed;
1297
+ delete next.routingDecisionBuffer;
1298
+ delete next.routingDecisionText;
1299
+ } else {
1300
+ next.routingDecisionBuffer = buffered;
1301
+ next.routingDecisionText = buffered;
1302
+ }
1303
+ return next;
1304
+ };
1305
+ if (!lastMessage) {
1306
+ const seed = mergeRoutingMetadata({});
1307
+ return appendAssistantMessage(conversation, `routing-agent-${chunk.payload?.runId ?? "unknown"}-${Date.now()}`, [], {
1308
+ ...networkMode(metadata),
1309
+ ...seed
1310
+ });
1311
+ }
1312
+ return replaceLast(conversation, withMetadata(lastMessage, mergeRoutingMetadata(lastMessage.content.metadata ?? {})));
1287
1313
  };
1288
- const EntryTitleClass = "mastra:font-mono mastra:text-sm mastra:text-text3";
1289
- const EntryTitle = ({ className, as: Root = "h3", ...props }) => {
1290
- return /* @__PURE__ */ jsx(Root, { className: className || EntryTitleClass, ...props });
1314
+ const handleAgentNetworkChunk = (chunk, conversation, metadata) => {
1315
+ if (chunk.type === "agent-execution-start") {
1316
+ const primitiveId = chunk.payload?.args?.primitiveId;
1317
+ const runId = chunk.payload.runId;
1318
+ if (!primitiveId || !runId) return conversation;
1319
+ const toolPart = {
1320
+ type: "dynamic-tool",
1321
+ toolName: primitiveId,
1322
+ toolCallId: runId,
1323
+ state: "input-available",
1324
+ input: chunk.payload.args
1325
+ };
1326
+ return appendAssistantMessage(conversation, `agent-execution-start-${runId}-${Date.now()}`, [toolPart], {
1327
+ ...networkMode(metadata),
1328
+ selectionReason: chunk.payload?.args?.selectionReason || "",
1329
+ agentInput: chunk.payload?.args?.task,
1330
+ from: "AGENT"
1331
+ });
1332
+ }
1333
+ if (chunk.type === "agent-execution-end") {
1334
+ const lastMessage = lastAssistant(conversation);
1335
+ if (!lastMessage) return conversation;
1336
+ const parts = [...lastMessage.content.parts];
1337
+ const toolPartIndex = findPartIndex(parts, (part) => isDynamicToolPart(part));
1338
+ if (toolPartIndex !== -1) {
1339
+ const toolPart = parts[toolPartIndex];
1340
+ const currentOutput = toolPart.output;
1341
+ parts[toolPartIndex] = {
1342
+ type: "dynamic-tool",
1343
+ toolName: toolPart.toolName,
1344
+ toolCallId: toolPart.toolCallId,
1345
+ state: "output-available",
1346
+ input: toolPart.input,
1347
+ output: {
1348
+ ...currentOutput,
1349
+ result: currentOutput?.result || chunk.payload?.result || ""
1350
+ }
1351
+ };
1352
+ }
1353
+ return replaceLast(conversation, withParts(lastMessage, parts));
1354
+ }
1355
+ if (chunk.type.startsWith("agent-execution-event-")) {
1356
+ const lastMessage = lastAssistant(conversation);
1357
+ if (!lastMessage) return conversation;
1358
+ const agentChunk = chunk.payload;
1359
+ const parts = [...lastMessage.content.parts];
1360
+ const toolPartIndex = findPartIndex(parts, (part) => isDynamicToolPart(part));
1361
+ if (toolPartIndex === -1) return conversation;
1362
+ const toolPart = parts[toolPartIndex];
1363
+ if (agentChunk.type === "text-delta") {
1364
+ const childMessages = toolPart?.output?.childMessages || [];
1365
+ const lastChildMessage = childMessages[childMessages.length - 1];
1366
+ const textMessage = {
1367
+ type: "text",
1368
+ content: (lastChildMessage?.content || "") + agentChunk.payload.text
1369
+ };
1370
+ const nextMessages = lastChildMessage?.type === "text" ? [...childMessages.slice(0, -1), textMessage] : [...childMessages, textMessage];
1371
+ parts[toolPartIndex] = {
1372
+ ...toolPart,
1373
+ output: { childMessages: nextMessages }
1374
+ };
1375
+ } else if (agentChunk.type === "tool-call") {
1376
+ const childMessages = toolPart?.output?.childMessages || [];
1377
+ parts[toolPartIndex] = {
1378
+ ...toolPart,
1379
+ output: {
1380
+ ...toolPart?.output,
1381
+ childMessages: [...childMessages, {
1382
+ type: "tool",
1383
+ toolCallId: agentChunk.payload.toolCallId,
1384
+ toolName: agentChunk.payload.toolName,
1385
+ args: agentChunk.payload.args
1386
+ }]
1387
+ }
1388
+ };
1389
+ } else if (agentChunk.type === "tool-output") {
1390
+ if (agentChunk.payload?.output?.type?.startsWith("workflow-")) {
1391
+ const childMessages = toolPart?.output?.childMessages || [];
1392
+ const lastToolIndex = childMessages.length - 1;
1393
+ const currentMessage = childMessages[lastToolIndex];
1394
+ const actualExistingWorkflowState = currentMessage?.toolOutput || {};
1395
+ const updatedWorkflowState = mapWorkflowStreamChunkToWatchResult(actualExistingWorkflowState, agentChunk.payload.output);
1396
+ if (lastToolIndex >= 0 && childMessages[lastToolIndex]?.type === "tool") parts[toolPartIndex] = {
1397
+ ...toolPart,
1398
+ output: {
1399
+ ...toolPart?.output,
1400
+ childMessages: [...childMessages.slice(0, -1), {
1401
+ ...currentMessage,
1402
+ toolOutput: updatedWorkflowState
1403
+ }]
1404
+ }
1405
+ };
1406
+ }
1407
+ } else if (agentChunk.type === "tool-result") {
1408
+ const childMessages = toolPart?.output?.childMessages || [];
1409
+ const lastToolIndex = childMessages.length - 1;
1410
+ const isWorkflow = Boolean(agentChunk.payload?.result?.result?.steps);
1411
+ if (lastToolIndex >= 0 && childMessages[lastToolIndex]?.type === "tool") parts[toolPartIndex] = {
1412
+ ...toolPart,
1413
+ output: {
1414
+ ...toolPart?.output,
1415
+ childMessages: [...childMessages.slice(0, -1), {
1416
+ ...childMessages[lastToolIndex],
1417
+ toolOutput: isWorkflow ? agentChunk.payload.result.result : agentChunk.payload.result
1418
+ }]
1419
+ }
1420
+ };
1421
+ }
1422
+ return replaceLast(conversation, withParts(lastMessage, parts));
1423
+ }
1424
+ return conversation;
1291
1425
  };
1292
-
1293
- async function highlight(code, lang) {
1294
- const out = await codeToHast(code, {
1295
- lang,
1296
- theme: "dracula-soft"
1297
- });
1298
- return toJsxRuntime(out, {
1299
- Fragment,
1300
- jsx,
1301
- jsxs
1302
- });
1303
- }
1304
-
1305
- const Tooltip = ({ children }) => {
1306
- return /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsx(Root, { children }) });
1426
+ const handleWorkflowNetworkChunk = (chunk, conversation, metadata) => {
1427
+ if (chunk.type === "workflow-execution-start") {
1428
+ const primitiveId = chunk.payload?.args?.primitiveId;
1429
+ const runId = chunk.payload.runId;
1430
+ if (!primitiveId || !runId) return conversation;
1431
+ let agentInput;
1432
+ try {
1433
+ agentInput = JSON.parse(chunk.payload?.args?.prompt);
1434
+ } catch {
1435
+ agentInput = chunk.payload?.args?.prompt;
1436
+ }
1437
+ const toolPart = {
1438
+ type: "dynamic-tool",
1439
+ toolName: primitiveId,
1440
+ toolCallId: runId,
1441
+ state: "input-available",
1442
+ input: chunk.payload.args
1443
+ };
1444
+ return appendAssistantMessage(conversation, `workflow-start-${runId}-${Date.now()}`, [toolPart], {
1445
+ ...networkMode(metadata),
1446
+ selectionReason: chunk.payload?.args?.selectionReason || "",
1447
+ from: "WORKFLOW",
1448
+ agentInput
1449
+ });
1450
+ }
1451
+ if (chunk.type === "workflow-execution-suspended") {
1452
+ const lastMessage = lastAssistant(conversation);
1453
+ if (!lastMessage) return conversation;
1454
+ const existing = lastMessage.content.metadata?.suspendedTools ?? {};
1455
+ return replaceLast(conversation, withMetadata(lastMessage, {
1456
+ ...cloneMetadata(lastMessage.content.metadata),
1457
+ mode: "network",
1458
+ suspendedTools: {
1459
+ ...existing,
1460
+ [chunk.payload.toolName]: {
1461
+ toolCallId: chunk.payload.toolCallId,
1462
+ toolName: chunk.payload.toolName,
1463
+ args: chunk.payload.args,
1464
+ suspendPayload: chunk.payload.suspendPayload,
1465
+ runId: chunk.payload.runId
1466
+ }
1467
+ }
1468
+ }));
1469
+ }
1470
+ if (chunk.type.startsWith("workflow-execution-event-")) {
1471
+ const lastMessage = lastAssistant(conversation);
1472
+ if (!lastMessage) return conversation;
1473
+ const parts = [...lastMessage.content.parts];
1474
+ const toolPartIndex = findPartIndex(parts, (part) => isDynamicToolPart(part));
1475
+ if (toolPartIndex === -1) return conversation;
1476
+ const toolPart = parts[toolPartIndex];
1477
+ const existingWorkflowState = toolPart.output || {};
1478
+ const updatedWorkflowState = mapWorkflowStreamChunkToWatchResult(existingWorkflowState, chunk.payload);
1479
+ parts[toolPartIndex] = {
1480
+ ...toolPart,
1481
+ output: updatedWorkflowState
1482
+ };
1483
+ return replaceLast(conversation, withParts(lastMessage, parts));
1484
+ }
1485
+ return conversation;
1307
1486
  };
1308
- const TooltipContentClass = "mastra:bg-surface4 mastra:text-text6 mastra mastra:rounded-lg mastra:py-1 mastra:px-2 mastra:text-xs mastra:border mastra:border-border1 mastra-tooltip-enter";
1309
- const TooltipContent = ({ children, className, ...props }) => {
1310
- return /* @__PURE__ */ jsx(TooltipPortal, { children: /* @__PURE__ */ jsx(TooltipContent$1, { className: className || TooltipContentClass, ...props, children }) });
1487
+ const handleToolNetworkChunk = (chunk, conversation, metadata) => {
1488
+ if (chunk.type === "tool-execution-start") {
1489
+ const argsData = chunk.payload.args;
1490
+ const nestedArgs = argsData.args || {};
1491
+ const lastMessage = lastAssistant(conversation);
1492
+ const toolPart = {
1493
+ type: "dynamic-tool",
1494
+ toolName: argsData.toolName || "unknown",
1495
+ toolCallId: argsData.toolCallId || "unknown",
1496
+ state: "input-available",
1497
+ input: nestedArgs
1498
+ };
1499
+ if (!lastMessage) return appendAssistantMessage(conversation, `tool-start-${chunk.payload.runId}-${Date.now()}`, [toolPart], {
1500
+ ...networkMode(metadata),
1501
+ selectionReason: metadata.mode === "network" ? metadata.selectionReason || argsData.selectionReason : "",
1502
+ agentInput: nestedArgs
1503
+ });
1504
+ const parts = [...lastMessage.content.parts, toolPart];
1505
+ return replaceLast(conversation, withParts(lastMessage, parts));
1506
+ }
1507
+ if (chunk.type === "tool-execution-approval") {
1508
+ const lastMessage = lastAssistant(conversation);
1509
+ if (!lastMessage) return conversation;
1510
+ const existing = lastMessage.content.metadata?.requireApprovalMetadata ?? {};
1511
+ return replaceLast(conversation, withMetadata(lastMessage, {
1512
+ ...cloneMetadata(lastMessage.content.metadata),
1513
+ mode: "network",
1514
+ requireApprovalMetadata: {
1515
+ ...existing,
1516
+ [chunk.payload.toolName]: {
1517
+ toolCallId: chunk.payload.toolCallId,
1518
+ toolName: chunk.payload.toolName,
1519
+ args: chunk.payload.args,
1520
+ runId: chunk.payload.runId
1521
+ }
1522
+ }
1523
+ }));
1524
+ }
1525
+ if (chunk.type === "tool-execution-suspended") {
1526
+ const lastMessage = lastAssistant(conversation);
1527
+ if (!lastMessage) return conversation;
1528
+ const existing = lastMessage.content.metadata?.suspendedTools ?? {};
1529
+ return replaceLast(conversation, withMetadata(lastMessage, {
1530
+ ...cloneMetadata(lastMessage.content.metadata),
1531
+ mode: "network",
1532
+ suspendedTools: {
1533
+ ...existing,
1534
+ [chunk.payload.toolName]: {
1535
+ toolCallId: chunk.payload.toolCallId,
1536
+ toolName: chunk.payload.toolName,
1537
+ args: chunk.payload.args,
1538
+ suspendPayload: chunk.payload.suspendPayload,
1539
+ runId: chunk.payload.runId
1540
+ }
1541
+ }
1542
+ }));
1543
+ }
1544
+ if (chunk.type === "tool-execution-end") {
1545
+ const lastMessage = lastAssistant(conversation);
1546
+ if (!lastMessage) return conversation;
1547
+ const parts = [...lastMessage.content.parts];
1548
+ const toolPartIndex = findPartIndex(parts, (part) => isDynamicToolPart(part) && part.toolCallId === chunk.payload.toolCallId);
1549
+ if (toolPartIndex !== -1) {
1550
+ const toolPart = parts[toolPartIndex];
1551
+ const currentOutput = toolPart.output;
1552
+ parts[toolPartIndex] = {
1553
+ type: "dynamic-tool",
1554
+ toolName: toolPart.toolName,
1555
+ toolCallId: toolPart.toolCallId,
1556
+ state: "output-available",
1557
+ input: toolPart.input,
1558
+ output: currentOutput?.result || chunk.payload?.result || ""
1559
+ };
1560
+ }
1561
+ return replaceLast(conversation, withParts(lastMessage, parts));
1562
+ }
1563
+ return conversation;
1311
1564
  };
1312
- const TooltipTrigger = (props) => {
1313
- return /* @__PURE__ */ jsx(TooltipTrigger$1, { ...props, asChild: true });
1565
+ /**
1566
+ * Reduce a single network-mode chunk into the running `MastraDBMessage[]`
1567
+ * conversation. Ports the historical `AISdkNetworkTransformer` so the playground
1568
+ * keeps rendering network badges (routing text, agent/workflow/tool execution,
1569
+ * suspensions, approvals, completion feedback) until the next major.
1570
+ */
1571
+ const accumulateNetworkChunk = ({ chunk, conversation, metadata }) => {
1572
+ const newConversation = [...conversation];
1573
+ if (chunk.type === "routing-agent-text-delta") return handleRoutingAgentDelta(chunk, newConversation, metadata);
1574
+ if (chunk.type.startsWith("agent-execution-")) return handleAgentNetworkChunk(chunk, newConversation, metadata);
1575
+ if (chunk.type.startsWith("workflow-execution-")) return handleWorkflowNetworkChunk(chunk, newConversation, metadata);
1576
+ if (chunk.type.startsWith("tool-execution-")) return handleToolNetworkChunk(chunk, newConversation, metadata);
1577
+ if (chunk.type === "network-validation-end") {
1578
+ if (chunk.payload.suppressFeedback) return newConversation;
1579
+ const textPart = {
1580
+ type: "text",
1581
+ text: formatCompletionFeedback({
1582
+ complete: chunk.payload.passed,
1583
+ scorers: chunk.payload.results,
1584
+ totalDuration: chunk.payload.duration,
1585
+ timedOut: chunk.payload.timedOut,
1586
+ completionReason: chunk.payload.reason
1587
+ }, chunk.payload.maxIterationReached)
1588
+ };
1589
+ return appendAssistantMessage(newConversation, `network-validation-end-${chunk.payload.runId}-${Date.now()}`, [textPart], {
1590
+ ...networkMode(metadata),
1591
+ completionResult: { passed: chunk.payload.passed }
1592
+ });
1593
+ }
1594
+ if (chunk.type === "network-execution-event-step-finish") {
1595
+ const lastMessage = lastAssistant(newConversation);
1596
+ if (!lastMessage) return newConversation;
1597
+ const agentChunk = chunk.payload;
1598
+ const parts = [...lastMessage.content.parts];
1599
+ const textPartIndex = findPartIndex(parts, (part) => part.type === "text");
1600
+ if (textPartIndex === -1) {
1601
+ parts.push({
1602
+ type: "text",
1603
+ text: agentChunk.result,
1604
+ state: "done"
1605
+ });
1606
+ return replaceLast(newConversation, withParts(lastMessage, parts));
1607
+ }
1608
+ const textPart = parts[textPartIndex];
1609
+ if (textPart.type === "text") {
1610
+ parts[textPartIndex] = {
1611
+ ...textPart,
1612
+ state: "done"
1613
+ };
1614
+ return replaceLast(newConversation, withParts(lastMessage, parts));
1615
+ }
1616
+ return newConversation;
1617
+ }
1618
+ return newConversation;
1314
1619
  };
1315
-
1316
- const IconButtonClass = "mastra:text-text3 mastra:hover:text-text6 mastra:active:text-text6 mastra:hover:bg-surface4 mastra:active:bg-surface5 mastra:rounded-md mastra:cursor-pointer";
1317
- const IconButton = ({ children, tooltip, size = "md", className, ...props }) => {
1318
- return /* @__PURE__ */ jsxs(Tooltip, { children: [
1319
- /* @__PURE__ */ jsx(TooltipTrigger, { children: /* @__PURE__ */ jsx(
1320
- "button",
1321
- {
1322
- ...props,
1323
- className: className || twMerge(IconButtonClass, size === "md" && "mastra:p-0.5", size === "lg" && "mastra:p-1"),
1324
- children: /* @__PURE__ */ jsx(Icon, { size, children })
1325
- }
1326
- ) }),
1327
- /* @__PURE__ */ jsx(TooltipContent, { children: tooltip })
1328
- ] });
1620
+ //#endregion
1621
+ //#region src/lib/mastra-db/fromCoreUserMessage.ts
1622
+ /**
1623
+ * Convert a CoreUserMessage into a canonical `MastraDBMessage` (`format: 2`).
1624
+ *
1625
+ * Image and file inputs are emitted as the canonical V4 `file` part shape
1626
+ * (`{ type: 'file', mimeType, data }`) — the exact shape memory resolves on
1627
+ * reload (see `AIV4Adapter`), so optimistic-send and reload render identically.
1628
+ *
1629
+ * Handles all CoreUserMessage content types:
1630
+ * - String content → single text part
1631
+ * - Array content with text/image/file parts → corresponding `MastraMessagePart`s
1632
+ */
1633
+ const coreUserMessageToParts = (coreUserMessage) => typeof coreUserMessage.content === "string" ? [{
1634
+ type: "text",
1635
+ text: coreUserMessage.content
1636
+ }] : coreUserMessage.content.map((part) => {
1637
+ switch (part.type) {
1638
+ case "text": return {
1639
+ type: "text",
1640
+ text: part.text
1641
+ };
1642
+ case "image": {
1643
+ const mimeType = part.mimeType ?? "image/*";
1644
+ return {
1645
+ type: "file",
1646
+ mimeType,
1647
+ data: encodeFilePartDataForStorage(part.image, mimeType)
1648
+ };
1649
+ }
1650
+ case "file": return {
1651
+ type: "file",
1652
+ mimeType: part.mimeType,
1653
+ data: encodeFilePartDataForStorage(part.data, part.mimeType),
1654
+ ...part.filename !== void 0 ? { filename: part.filename } : {}
1655
+ };
1656
+ default: throw new Error(`Unhandled content part type: ${part.type}`);
1657
+ }
1658
+ });
1659
+ const newUserMessage = (parts) => ({
1660
+ id: `user-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`,
1661
+ role: "user",
1662
+ createdAt: /* @__PURE__ */ new Date(),
1663
+ content: {
1664
+ format: 2,
1665
+ parts
1666
+ }
1667
+ });
1668
+ const fromCoreUserMessageToMastraDBMessage = (coreUserMessage) => newUserMessage(coreUserMessageToParts(coreUserMessage));
1669
+ /**
1670
+ * Merge multiple `CoreUserMessage`s into a single canonical `MastraDBMessage`.
1671
+ *
1672
+ * A user turn that carries attachments arrives as several `CoreUserMessage`s
1673
+ * (one for the text, one per attachment). Memory/reload persists and resolves
1674
+ * that whole turn as a single multi-part user message, so the optimistic
1675
+ * streaming display must do the same — flatten every message's parts into one
1676
+ * `parts` array — to render identically (one bubble, not one per message).
1677
+ */
1678
+ const fromCoreUserMessagesToMastraDBMessage = (coreUserMessages) => newUserMessage(coreUserMessages.flatMap(coreUserMessageToParts));
1679
+ //#endregion
1680
+ //#region src/agent/extract-tasks.ts
1681
+ const TASK_TOOL_NAMES = /* @__PURE__ */ new Set([
1682
+ "task_write",
1683
+ "task_update",
1684
+ "task_complete",
1685
+ "task_check"
1686
+ ]);
1687
+ const TASK_SIGNAL_ID = "tasks";
1688
+ const TASK_TAG_NAMES = /* @__PURE__ */ new Set(["current-task-list", "task-list-update"]);
1689
+ const TASK_STATUSES = /* @__PURE__ */ new Set([
1690
+ "pending",
1691
+ "in_progress",
1692
+ "completed"
1693
+ ]);
1694
+ const isDataChunk$1 = (chunk) => typeof chunk.type === "string" && chunk.type.startsWith("data-");
1695
+ const isRecord$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1696
+ const isTaskStatus = (value) => typeof value === "string" && TASK_STATUSES.has(value);
1697
+ const isTaskItemArray = (value) => Array.isArray(value) && value.every((item) => isRecord$1(item) && typeof item.id === "string" && typeof item.content === "string" && isTaskStatus(item.status) && typeof item.activeForm === "string");
1698
+ const isTaskSignal = (value) => {
1699
+ if (!isRecord$1(value)) return false;
1700
+ return value.id === TASK_SIGNAL_ID || typeof value.tagName === "string" && TASK_TAG_NAMES.has(value.tagName);
1329
1701
  };
1330
-
1331
- const CodeBlockClass = "mastra:rounded-lg mastra:[&>pre]:p-4 mastra:overflow-hidden mastra:[&>pre]:!bg-surface4 mastra:[&>pre>code]:leading-5 mastra:relative";
1332
- const CodeBlock = ({ code, language, className, cta }) => {
1333
- const [nodes, setNodes] = useState(null);
1334
- useLayoutEffect(() => {
1335
- void highlight(code, language).then(setNodes);
1336
- }, [language]);
1337
- return /* @__PURE__ */ jsxs("div", { className: className || CodeBlockClass, children: [
1338
- nodes ?? null,
1339
- cta
1340
- ] });
1702
+ const extractTasksFromSignalData = (data) => {
1703
+ if (!isTaskSignal(data) || !isRecord$1(data)) return void 0;
1704
+ const metadata = data.metadata;
1705
+ if (!isRecord$1(metadata)) return void 0;
1706
+ const value = metadata.value;
1707
+ if (!isRecord$1(value)) return void 0;
1708
+ return isTaskItemArray(value.tasks) ? value.tasks : void 0;
1341
1709
  };
1342
- const CodeCopyButton = ({ code }) => {
1343
- const [isCopied, setIsCopied] = useState(false);
1344
- const handleCopy = () => {
1345
- navigator.clipboard.writeText(code);
1346
- setIsCopied(true);
1347
- setTimeout(() => setIsCopied(false), 2e3);
1348
- };
1349
- return /* @__PURE__ */ jsx("div", { className: "mastra:absolute mastra:top-2 mastra:right-2", children: /* @__PURE__ */ jsx(IconButton, { tooltip: "Copy", onClick: handleCopy, children: isCopied ? /* @__PURE__ */ jsx(CheckIcon, {}) : /* @__PURE__ */ jsx(CopyIcon, {}) }) });
1710
+ const parseTasksFromResult = (raw) => {
1711
+ if (isRecord$1(raw) && isTaskItemArray(raw.tasks)) return raw.tasks;
1712
+ if (typeof raw !== "string") return void 0;
1713
+ try {
1714
+ const parsed = JSON.parse(raw);
1715
+ return isRecord$1(parsed) && isTaskItemArray(parsed.tasks) ? parsed.tasks : void 0;
1716
+ } catch {
1717
+ return;
1718
+ }
1350
1719
  };
1351
-
1352
- const AgentIcon = ({ className, ...props }) => /* @__PURE__ */ jsxs(
1353
- "svg",
1354
- {
1355
- width: "17",
1356
- height: "16",
1357
- viewBox: "0 0 17 16",
1358
- fill: "none",
1359
- xmlns: "http://www.w3.org/2000/svg",
1360
- ...props,
1361
- className: twMerge("mastra-icon", className),
1362
- children: [
1363
- /* @__PURE__ */ jsx(
1364
- "path",
1365
- {
1366
- fillRule: "evenodd",
1367
- clipRule: "evenodd",
1368
- d: "M8.5 15C10.3565 15 12.137 14.2625 13.4497 12.9497C14.7625 11.637 15.5 9.85652 15.5 8C15.5 6.14348 14.7625 4.36301 13.4497 3.05025C12.137 1.7375 10.3565 1 8.5 1C6.64348 1 4.86301 1.7375 3.55025 3.05025C2.2375 4.36301 1.5 6.14348 1.5 8C1.5 9.85652 2.2375 11.637 3.55025 12.9497C4.86301 14.2625 6.64348 15 8.5 15ZM5.621 10.879L4.611 11.889C3.84179 11.1198 3.31794 10.1398 3.1057 9.07291C2.89346 8.00601 3.00236 6.90013 3.41864 5.89512C3.83491 4.89012 4.53986 4.03112 5.44434 3.42676C6.34881 2.8224 7.41219 2.49983 8.5 2.49983C9.58781 2.49983 10.6512 2.8224 11.5557 3.42676C12.4601 4.03112 13.1651 4.89012 13.5814 5.89512C13.9976 6.90013 14.1065 8.00601 13.8943 9.07291C13.6821 10.1398 13.1582 11.1198 12.389 11.889L11.379 10.879C11.1004 10.6003 10.7696 10.3792 10.4055 10.2284C10.0414 10.0776 9.6511 9.99995 9.257 10H7.743C7.3489 9.99995 6.95865 10.0776 6.59455 10.2284C6.23045 10.3792 5.89963 10.6003 5.621 10.879Z",
1369
- fill: "currentColor"
1370
- }
1371
- ),
1372
- /* @__PURE__ */ jsx(
1373
- "path",
1374
- {
1375
- d: "M8.5 4C7.96957 4 7.46086 4.21071 7.08579 4.58579C6.71071 4.96086 6.5 5.46957 6.5 6V6.5C6.5 7.03043 6.71071 7.53914 7.08579 7.91421C7.46086 8.28929 7.96957 8.5 8.5 8.5C9.03043 8.5 9.53914 8.28929 9.91421 7.91421C10.2893 7.53914 10.5 7.03043 10.5 6.5V6C10.5 5.46957 10.2893 4.96086 9.91421 4.58579C9.53914 4.21071 9.03043 4 8.5 4Z",
1376
- fill: "currentColor"
1377
- }
1378
- )
1379
- ]
1380
- }
1381
- );
1382
-
1383
- const ToolsIcon = ({ className, ...props }) => /* @__PURE__ */ jsx(
1384
- "svg",
1385
- {
1386
- width: "17",
1387
- height: "16",
1388
- viewBox: "0 0 17 16",
1389
- fill: "none",
1390
- xmlns: "http://www.w3.org/2000/svg",
1391
- ...props,
1392
- className: twMerge("mastra-icon", className),
1393
- children: /* @__PURE__ */ jsx(
1394
- "path",
1395
- {
1396
- fillRule: "evenodd",
1397
- clipRule: "evenodd",
1398
- d: "M7.5605 1.42351C8.0791 0.904904 8.92215 0.906157 9.4395 1.42351L10.6922 2.67617C11.2108 3.19477 11.2095 4.03782 10.6922 4.55517L9.4395 5.80783C8.9209 6.32643 8.07785 6.32518 7.5605 5.80783L6.30784 4.55517C5.78923 4.03656 5.79049 3.19352 6.30784 2.67617L7.5605 1.42351ZM3.17618 5.80783C3.69478 5.28923 4.53782 5.29048 5.05517 5.80783L6.30784 7.0605C6.82644 7.5791 6.82519 8.42214 6.30784 8.93949L5.05517 10.1922C4.53657 10.7108 3.69353 10.7095 3.17618 10.1922L1.92351 8.93949C1.40491 8.42089 1.40616 7.57785 1.92351 7.0605L3.17618 5.80783ZM11.9448 5.80783C12.4634 5.28923 13.3065 5.29048 13.8238 5.80783L15.0765 7.0605C15.5951 7.5791 15.5938 8.42214 15.0765 8.93949L13.8238 10.1922C13.3052 10.7108 12.4622 10.7095 11.9448 10.1922L10.6922 8.93949C10.1736 8.42089 10.1748 7.57785 10.6922 7.0605L11.9448 5.80783ZM7.5605 10.1922C8.0791 9.67355 8.92215 9.67481 9.4395 10.1922L10.6922 11.4448C11.2108 11.9634 11.2095 12.8065 10.6922 13.3238L9.4395 14.5765C8.9209 15.0951 8.07785 15.0938 7.5605 14.5765L6.30784 13.3238C5.78923 12.8052 5.79049 11.9622 6.30784 11.4448L7.5605 10.1922Z",
1399
- fill: "currentColor"
1400
- }
1401
- )
1402
- }
1403
- );
1404
-
1405
- const WorkflowIcon = ({ className, ...props }) => /* @__PURE__ */ jsx(
1406
- "svg",
1407
- {
1408
- width: "17",
1409
- height: "16",
1410
- viewBox: "0 0 17 16",
1411
- fill: "none",
1412
- xmlns: "http://www.w3.org/2000/svg",
1413
- ...props,
1414
- className: twMerge("mastra-icon", className),
1415
- children: /* @__PURE__ */ jsx(
1416
- "path",
1417
- {
1418
- fillRule: "evenodd",
1419
- clipRule: "evenodd",
1420
- d: "M6.24388 2.4018C6.24388 2.0394 6.53767 1.74561 6.90008 1.74561H10.0991C10.4614 1.74561 10.7553 2.0394 10.7553 2.4018V4.57546C10.7553 4.93787 10.4614 5.23166 10.0991 5.23166H9.31982V7.35469L10.0033 9.22664C9.90442 9.20146 9.80035 9.1761 9.6915 9.14986L9.62652 9.13422C9.30473 9.05687 8.92256 8.96501 8.61993 8.84491C8.5819 8.82981 8.54147 8.81292 8.49957 8.79391C8.45767 8.81292 8.41724 8.82981 8.3792 8.84491C8.07657 8.96501 7.6944 9.05687 7.37261 9.13422L7.30763 9.14986C7.19879 9.1761 7.09471 9.20146 6.99577 9.22664L7.67932 7.35469V5.23166H6.90008C6.53767 5.23166 6.24388 4.93787 6.24388 4.57546V2.4018ZM6.99577 9.22664C6.99577 9.22664 6.99578 9.22664 6.99577 9.22664L6.43283 10.7683H6.81806C7.18047 10.7683 7.47426 11.0622 7.47426 11.4245V13.5982C7.47426 13.9606 7.18047 14.2544 6.81806 14.2544H3.61909C3.25668 14.2544 2.96289 13.9606 2.96289 13.5982V11.4245C2.96289 11.0622 3.25668 10.7683 3.61909 10.7683H4.26617C4.2921 10.4663 4.32783 10.1494 4.37744 9.85171C4.43762 9.49063 4.52982 9.08135 4.68998 8.76102C4.93975 8.2615 5.44743 8.01751 5.7771 7.88788C6.14684 7.74249 6.57537 7.63889 6.92317 7.55505C7.24707 7.47696 7.49576 7.41679 7.67932 7.35469L6.99577 9.22664ZM6.43283 10.7683L6.99577 9.22664C6.75846 9.28705 6.55067 9.34646 6.37745 9.41458C6.22784 9.47341 6.1623 9.51712 6.14023 9.53254C6.09752 9.63631 6.04409 9.83055 5.99562 10.1214C5.96201 10.3231 5.93498 10.5439 5.91341 10.7683H6.43283ZM10.0033 9.22664L9.31982 7.35469C9.50338 7.41679 9.75206 7.47696 10.076 7.55505C10.4238 7.63889 10.8523 7.74249 11.2221 7.88788C11.5517 8.01751 12.0594 8.2615 12.3091 8.76102C12.4693 9.08135 12.5615 9.49063 12.6217 9.85171C12.6713 10.1494 12.707 10.4663 12.733 10.7683H13.38C13.7424 10.7683 14.0362 11.0622 14.0362 11.4245V13.5982C14.0362 13.9606 13.7424 14.2544 13.38 14.2544H10.1811C9.81867 14.2544 9.52488 13.9606 9.52488 13.5982V11.4245C9.52488 11.0622 9.81867 10.7683 10.1811 10.7683H10.5663L10.0033 9.22664ZM10.0033 9.22664L10.5663 10.7683H11.0857C11.0642 10.5439 11.0372 10.3231 11.0035 10.1214C10.9551 9.83055 10.9016 9.63631 10.8589 9.53254C10.8369 9.51712 10.7713 9.47341 10.6217 9.41458C10.4485 9.34646 10.2407 9.28705 10.0033 9.22664Z",
1421
- fill: "currentColor"
1422
- }
1423
- )
1424
- }
1425
- );
1426
-
1427
- const MessageClass = "mastra:flex mastra:flex-col mastra:w-full mastra:py-4 mastra:gap-2 mastra:group";
1428
- const Message = ({ position, className, children, ...props }) => {
1429
- return /* @__PURE__ */ jsx(
1430
- "div",
1431
- {
1432
- className: className || twMerge(
1433
- MessageClass,
1434
- position === "left" ? "" : "mastra:items-end mastra:[&_.mastra-message-content]:bg-surface4 mastra:[&_.mastra-message-content]:px-4"
1435
- ),
1436
- ...props,
1437
- children
1438
- }
1439
- );
1720
+ const extractTasksFromSignalChunk = (chunk) => {
1721
+ if (!isDataChunk$1(chunk)) return void 0;
1722
+ return extractTasksFromSignalData(chunk.data);
1723
+ };
1724
+ const extractTasksFromToolResultChunk = (chunk) => {
1725
+ if (chunk.type !== "tool-result") return void 0;
1726
+ const payload = chunk.payload;
1727
+ if (!isRecord$1(payload) || typeof payload.toolName !== "string" || !TASK_TOOL_NAMES.has(payload.toolName)) return;
1728
+ return parseTasksFromResult(payload.result);
1440
1729
  };
1441
- const MessageContentClass = "mastra:max-w-4/5 mastra:py-2 mastra:text-text6 mastra:rounded-lg mastra-message-content mastra:text-md";
1442
- const MessageContent = ({ children, className, isStreaming, ...props }) => {
1443
- return /* @__PURE__ */ jsxs("div", { className: className || MessageContentClass, ...props, children: [
1444
- children,
1445
- isStreaming && /* @__PURE__ */ jsx(MessageStreaming, {})
1446
- ] });
1730
+ const extractTasksFromToolInvocationPart = (part) => {
1731
+ if (!isRecord$1(part) || part.type !== "tool-invocation" || !isRecord$1(part.toolInvocation)) return void 0;
1732
+ const toolInvocation = part.toolInvocation;
1733
+ if (typeof toolInvocation.toolName !== "string" || !TASK_TOOL_NAMES.has(toolInvocation.toolName)) return void 0;
1734
+ return parseTasksFromResult(toolInvocation.result);
1447
1735
  };
1448
- const MessageActionsClass = "mastra:gap-2 mastra:flex mastra:opacity-0 mastra:group-hover:opacity-100 mastra:group-focus-within:opacity-100 mastra:items-center";
1449
- const MessageActions = ({ children, className, ...props }) => {
1450
- return /* @__PURE__ */ jsx("div", { className: className || MessageActionsClass, ...props, children });
1736
+ const extractTasksFromDataPart = (part) => {
1737
+ if (!isRecord$1(part) || typeof part.type !== "string" || !part.type.startsWith("data-")) return void 0;
1738
+ return extractTasksFromSignalData(part.data);
1451
1739
  };
1452
- const MessageUsagesClass = "mastra:flex mastra:gap-2 mastra:items-center";
1453
- const MessageUsages = ({ children, className, ...props }) => {
1454
- return /* @__PURE__ */ jsx("div", { className: className || MessageUsagesClass, ...props, children });
1740
+ const extractTasksFromSignalMessage = (message) => {
1741
+ if (message.role !== "signal") return void 0;
1742
+ const metadata = message.content?.metadata;
1743
+ if (!isRecord$1(metadata)) return void 0;
1744
+ return extractTasksFromSignalData(metadata.signal);
1455
1745
  };
1456
- const MessageUsageClass = "mastra:flex mastra:gap-2 mastra:items-center mastra:font-mono mastra:text-xs mastra:bg-surface3 mastra:rounded-lg mastra:px-2 mastra:py-1";
1457
- const MessageUsage = ({ children, className, ...props }) => {
1458
- return /* @__PURE__ */ jsx("dl", { className: className || MessageUsageClass, ...props, children });
1746
+ const extractLatestTasksFromMessages = (messages) => {
1747
+ let latest;
1748
+ for (const message of messages) {
1749
+ for (const part of message.content?.parts ?? []) {
1750
+ const toolTasks = extractTasksFromToolInvocationPart(part);
1751
+ if (toolTasks !== void 0) latest = toolTasks;
1752
+ }
1753
+ const signalTasks = extractTasksFromSignalMessage(message);
1754
+ if (signalTasks !== void 0) latest = signalTasks;
1755
+ for (const part of message.content?.parts ?? []) {
1756
+ const dataTasks = extractTasksFromDataPart(part);
1757
+ if (dataTasks !== void 0) latest = dataTasks;
1758
+ }
1759
+ }
1760
+ return latest ?? [];
1459
1761
  };
1460
- const MessageUsageEntryClass = "mastra:text-text3 mastra:text-xs mastra:flex mastra:gap-1 mastra:items-center";
1461
- const MessageUsageEntry = ({ children, className, ...props }) => {
1462
- return /* @__PURE__ */ jsx("dt", { className: className || MessageUsageEntryClass, ...props, children });
1762
+ //#endregion
1763
+ //#region src/agent/extractRunIdFromMessages.ts
1764
+ const isRecord = (value) => value !== null && typeof value === "object";
1765
+ const runIdMetadataKeys = [
1766
+ "pendingToolApprovals",
1767
+ "requireApprovalMetadata",
1768
+ "suspendedTools"
1769
+ ];
1770
+ const isRunIdMetadataSource = (value) => isRecord(value) && Object.values(value).every((entry) => isRecord(entry));
1771
+ const getRunIdMetadataSources = (metadata) => {
1772
+ if (!isRecord(metadata)) return [];
1773
+ const sources = [];
1774
+ for (const key of runIdMetadataKeys) {
1775
+ const source = metadata[key];
1776
+ if (isRunIdMetadataSource(source)) sources.push(source);
1777
+ }
1778
+ return sources;
1463
1779
  };
1464
- const MessageUsageValueClass = "mastra:text-text6 mastra:text-xs";
1465
- const MessageUsageValue = ({ children, className, ...props }) => {
1466
- return /* @__PURE__ */ jsx("dd", { className: className || MessageUsageValueClass, ...props, children });
1780
+ /**
1781
+ * Scan initial DB-shape messages for any pending approvals, suspended tools, or
1782
+ * `requireApprovalMetadata` entries and return the first non-empty `runId`.
1783
+ *
1784
+ * Metadata is read off `message.content.metadata`, the canonical location for
1785
+ * MastraDBMessage UX hints.
1786
+ */
1787
+ const extractRunIdFromMessages = (messages) => {
1788
+ for (const message of messages) for (const source of getRunIdMetadataSources(message.content?.metadata)) for (const entry of Object.values(source)) if (isRecord(entry) && typeof entry.runId === "string" && entry.runId.length > 0) return entry.runId;
1789
+ };
1790
+ //#endregion
1791
+ //#region src/agent/hooks.ts
1792
+ const extractPendingToolApprovalIdsFromMessages = (messages) => {
1793
+ const pendingToolApprovalIds = /* @__PURE__ */ new Set();
1794
+ for (const message of messages) {
1795
+ const metadata = message.content?.metadata;
1796
+ if (!metadata) continue;
1797
+ const metadataSources = [
1798
+ metadata.pendingToolApprovals,
1799
+ metadata.requireApprovalMetadata,
1800
+ metadata.suspendedTools
1801
+ ];
1802
+ for (const source of metadataSources) {
1803
+ if (!source || typeof source !== "object") continue;
1804
+ for (const suspensionData of Object.values(source)) {
1805
+ const toolCallId = suspensionData?.toolCallId;
1806
+ if (typeof toolCallId === "string" && toolCallId.length > 0) pendingToolApprovalIds.add(toolCallId);
1807
+ }
1808
+ }
1809
+ }
1810
+ return pendingToolApprovalIds;
1811
+ };
1812
+ const toolCallHasOutput = (parts, toolCallId) => parts.some((part) => {
1813
+ if (part.type !== "tool-invocation") return false;
1814
+ const invocation = part.toolInvocation;
1815
+ if (invocation.toolCallId !== toolCallId) return false;
1816
+ return invocation.state === "result" || invocation.result != null;
1817
+ });
1818
+ /**
1819
+ * Normalize persisted initial messages back into the stream-friendly shape the
1820
+ * UI renders from. Mirrors `main`'s `resolveInitialMessages`:
1821
+ *
1822
+ * - Converts persisted `pendingToolApprovals` (DB shape) into
1823
+ * `requireApprovalMetadata` (stream shape) so reloaded threads still render
1824
+ * approve/decline buttons, filtering out approvals whose tool already
1825
+ * produced output, and marks the message `mode: 'stream'`.
1826
+ * - Drops assistant completion messages flagged `suppressFeedback`, which are
1827
+ * persisted by the supervisor agent but must stay hidden on reload.
1828
+ */
1829
+ const resolveInitialMessages = (messages) => messages.filter((message) => {
1830
+ const metadata = message.content?.metadata;
1831
+ if (metadata?.completionResult?.suppressFeedback || metadata?.isTaskCompleteResult?.suppressFeedback) return false;
1832
+ return true;
1833
+ }).map((message) => {
1834
+ const metadata = message.content?.metadata;
1835
+ const normalizedMessage = metadata && (metadata.status === "pending" || "clientMessageId" in metadata) ? (() => {
1836
+ const { [CLIENT_MESSAGE_ID_KEY]: _omitClientMessageId, ...rest } = metadata;
1837
+ const { status: _omitStatus, ...restWithoutStatus } = rest;
1838
+ return {
1839
+ ...message,
1840
+ content: {
1841
+ ...message.content,
1842
+ metadata: metadata.status === "pending" ? restWithoutStatus : rest
1843
+ }
1844
+ };
1845
+ })() : message;
1846
+ const normalizedMetadata = normalizedMessage.content?.metadata;
1847
+ const pendingToolApprovals = normalizedMetadata?.pendingToolApprovals;
1848
+ if (!pendingToolApprovals || typeof pendingToolApprovals !== "object") return normalizedMessage;
1849
+ const stillPending = Object.fromEntries(Object.entries(pendingToolApprovals).filter(([, approval]) => approval && typeof approval === "object" && typeof approval.toolCallId === "string" && !toolCallHasOutput(normalizedMessage.content.parts, approval.toolCallId)));
1850
+ const { pendingToolApprovals: _omit, ...restMetadata } = normalizedMetadata;
1851
+ const hasStillPending = Object.keys(stillPending).length > 0;
1852
+ return {
1853
+ ...normalizedMessage,
1854
+ content: {
1855
+ ...normalizedMessage.content,
1856
+ metadata: {
1857
+ ...restMetadata,
1858
+ mode: "stream",
1859
+ ...hasStillPending ? {
1860
+ pendingToolApprovals: stillPending,
1861
+ requireApprovalMetadata: stillPending
1862
+ } : {}
1863
+ }
1864
+ }
1865
+ };
1866
+ });
1867
+ const isObject = (value) => typeof value === "object" && value !== null;
1868
+ const getErrorName = (error) => isObject(error) && typeof error.name === "string" ? error.name : void 0;
1869
+ const isAbortError = (error) => getErrorName(error) === "AbortError";
1870
+ const isThreadSignalUnsupportedError = (error) => {
1871
+ if (!isObject(error)) return false;
1872
+ const status = error.status;
1873
+ if (status === 404 || status === 405 || status === 501) return true;
1874
+ return status === 400 && typeof error.message === "string" && error.message.includes("No active agent run found for signal target");
1875
+ };
1876
+ const isDataChunk = (chunk) => typeof chunk.type === "string" && chunk.type.startsWith("data-");
1877
+ /**
1878
+ * Convert AI-SDK v5 UIMessages returned by the server (generate mode) into
1879
+ * `MastraDBMessage[]`, stamping the supplied metadata onto each message's
1880
+ * `content.metadata`. Private helper — `useChat` never exposes the AI-SDK
1881
+ * shape to consumers.
1882
+ */
1883
+ const dbFromServerUiMessages = (uiMessages, metadata) => uiMessages.map((uiMsg) => {
1884
+ const dbMsg = AIV5Adapter.fromUIMessage(uiMsg);
1885
+ return {
1886
+ ...dbMsg,
1887
+ content: {
1888
+ ...dbMsg.content,
1889
+ metadata: {
1890
+ ...dbMsg.content.metadata ?? {},
1891
+ ...metadata
1892
+ }
1893
+ }
1894
+ };
1895
+ });
1896
+ const useChat = ({ agentId, resourceId, threadId, initialMessages, requestContext: propsRequestContext, clientTools: hookClientTools, onSignalSent, onSignalEcho, onThreadSignalsUnsupported, enableThreadSignals = false }) => {
1897
+ const threadSignalsDisabled = enableThreadSignals === false;
1898
+ const _currentRunId = useRef(void 0);
1899
+ const _onChunk = useRef(void 0);
1900
+ const _networkRunId = useRef(void 0);
1901
+ const _onNetworkChunk = useRef(void 0);
1902
+ const _activeContinuation = useRef({ requestContext: propsRequestContext });
1903
+ const _streamAbortRef = useRef(null);
1904
+ const _threadSubscriptionAbortRef = useRef(null);
1905
+ const _threadSubscriptionRef = useRef(null);
1906
+ const _threadSubscriptionKeyRef = useRef(void 0);
1907
+ const _threadSubscriptionPromiseRef = useRef(null);
1908
+ const _threadSignalsUnsupportedRef = useRef(false);
1909
+ const [messages, setMessages] = useState([]);
1910
+ const [tasks, setTasks] = useState([]);
1911
+ const [toolCallApprovals, setToolCallApprovals] = useState({});
1912
+ const [networkToolCallApprovals, setNetworkToolCallApprovals] = useState({});
1913
+ const pendingToolApprovalIdsRef = useRef(/* @__PURE__ */ new Set());
1914
+ const [isAwaitingToolApproval, setIsAwaitingToolApproval] = useState(false);
1915
+ const baseClient = useMastraClient();
1916
+ const [isRunning, setIsRunning] = useState(false);
1917
+ useEffect(() => {
1918
+ const formattedMessages = resolveInitialMessages(initialMessages ?? []);
1919
+ setMessages(formattedMessages);
1920
+ setTasks(extractLatestTasksFromMessages(formattedMessages));
1921
+ pendingToolApprovalIdsRef.current = extractPendingToolApprovalIdsFromMessages(formattedMessages);
1922
+ setIsAwaitingToolApproval(pendingToolApprovalIdsRef.current.size > 0);
1923
+ _currentRunId.current = extractRunIdFromMessages(formattedMessages);
1924
+ }, [initialMessages]);
1925
+ useEffect(() => {
1926
+ _activeContinuation.current = {
1927
+ ..._activeContinuation.current,
1928
+ requestContext: propsRequestContext
1929
+ };
1930
+ }, [propsRequestContext]);
1931
+ const normalizeSignalFileData = (data) => {
1932
+ if (data instanceof URL) return data.toString();
1933
+ return convertSignalDataToBase64String(data);
1934
+ };
1935
+ const getSignalContents = (coreUserMessages) => {
1936
+ const parts = coreUserMessages.reduce((allParts, message) => {
1937
+ if (typeof message.content === "string") {
1938
+ allParts.push({
1939
+ type: "text",
1940
+ text: message.content
1941
+ });
1942
+ return allParts;
1943
+ }
1944
+ for (const part of message.content) if (part.type === "text") allParts.push({
1945
+ type: "text",
1946
+ text: part.text
1947
+ });
1948
+ else if (part.type === "file") allParts.push({
1949
+ type: "file",
1950
+ data: normalizeSignalFileData(part.data),
1951
+ mediaType: part.mimeType,
1952
+ ...part.filename ? { filename: part.filename } : {}
1953
+ });
1954
+ else if (part.type === "image") allParts.push({
1955
+ type: "file",
1956
+ data: normalizeSignalFileData(part.image),
1957
+ mediaType: part.mimeType ?? "image/png"
1958
+ });
1959
+ return allParts;
1960
+ }, []);
1961
+ return parts.length === 1 && parts[0]?.type === "text" ? parts[0].text : parts;
1962
+ };
1963
+ const markThreadSignalsUnsupported = useCallback(() => {
1964
+ _threadSignalsUnsupportedRef.current = true;
1965
+ onThreadSignalsUnsupported?.();
1966
+ }, [onThreadSignalsUnsupported]);
1967
+ const getSignalPreview = (coreUserMessages) => {
1968
+ return coreUserMessages.flatMap((message) => {
1969
+ if (typeof message.content === "string") return [message.content];
1970
+ return message.content.map((part) => {
1971
+ if (part.type === "text") return part.text;
1972
+ if (part.type === "image") return "Image";
1973
+ return part.filename ? `File: ${part.filename}` : "File";
1974
+ });
1975
+ }).join(" ").replace(/\s+/g, " ").trim() || "Attachment";
1976
+ };
1977
+ const closeThreadSubscription = useCallback(() => {
1978
+ const subscription = _threadSubscriptionRef.current;
1979
+ if (subscription?.unsubscribe) subscription.unsubscribe();
1980
+ else _threadSubscriptionAbortRef.current?.abort();
1981
+ _threadSubscriptionRef.current = null;
1982
+ _threadSubscriptionAbortRef.current = null;
1983
+ _threadSubscriptionKeyRef.current = void 0;
1984
+ _threadSubscriptionPromiseRef.current = null;
1985
+ }, []);
1986
+ const processStreamChunk = useCallback(async (chunk, onChunk) => {
1987
+ setMessages((prev) => accumulateChunk({
1988
+ chunk,
1989
+ conversation: prev,
1990
+ metadata: { mode: "stream" }
1991
+ }));
1992
+ const signalTasks = extractTasksFromSignalChunk(chunk);
1993
+ if (signalTasks !== void 0) setTasks(signalTasks);
1994
+ const toolTasks = extractTasksFromToolResultChunk(chunk);
1995
+ if (toolTasks !== void 0) setTasks(toolTasks);
1996
+ if (chunk.type === "data-user-message" && isDataChunk(chunk) && (chunk.data?.type === "user-message" || chunk.data?.type === "user") && typeof chunk.data?.id === "string") onSignalEcho?.(chunk.data.id);
1997
+ if (chunk.type === "start") {
1998
+ setIsRunning(true);
1999
+ if ("runId" in chunk && typeof chunk.runId === "string") _currentRunId.current = chunk.runId;
2000
+ }
2001
+ if (chunk.type === "tool-call-approval" || chunk.type === "tool-call-suspended") {
2002
+ const toolCallId = chunk.payload?.toolCallId;
2003
+ if (typeof toolCallId === "string") {
2004
+ pendingToolApprovalIdsRef.current.add(toolCallId);
2005
+ setIsAwaitingToolApproval(true);
2006
+ }
2007
+ setIsRunning(false);
2008
+ }
2009
+ if (chunk.type === "finish" || chunk.type === "abort" || chunk.type === "error") {
2010
+ pendingToolApprovalIdsRef.current.clear();
2011
+ setIsAwaitingToolApproval(false);
2012
+ setIsRunning(false);
2013
+ }
2014
+ (onChunk ?? _onChunk.current)?.(chunk);
2015
+ }, [onSignalEcho]);
2016
+ const ensureThreadSubscription = useCallback(async ({ threadId, resourceId }) => {
2017
+ const subscriptionKey = `${agentId}:${resourceId ?? ""}:${threadId}`;
2018
+ if (_threadSubscriptionKeyRef.current === subscriptionKey && _threadSubscriptionPromiseRef.current) {
2019
+ await _threadSubscriptionPromiseRef.current;
2020
+ return;
2021
+ }
2022
+ closeThreadSubscription();
2023
+ const subscriptionAbort = new AbortController();
2024
+ _threadSubscriptionAbortRef.current = subscriptionAbort;
2025
+ _threadSubscriptionKeyRef.current = subscriptionKey;
2026
+ const releaseSubscriptionRefs = () => {
2027
+ if (_threadSubscriptionAbortRef.current !== subscriptionAbort) return;
2028
+ _threadSubscriptionRef.current = null;
2029
+ _threadSubscriptionAbortRef.current = null;
2030
+ _threadSubscriptionKeyRef.current = void 0;
2031
+ _threadSubscriptionPromiseRef.current = null;
2032
+ };
2033
+ const subscriptionAgent = new MastraClient({
2034
+ ...baseClient.options,
2035
+ abortSignal: subscriptionAbort.signal
2036
+ }).getAgent(agentId);
2037
+ _threadSubscriptionPromiseRef.current = subscriptionAgent.subscribeToThread({
2038
+ resourceId,
2039
+ threadId
2040
+ }).then((response) => {
2041
+ const subscription = response;
2042
+ if (_threadSubscriptionAbortRef.current !== subscriptionAbort) {
2043
+ subscription.unsubscribe();
2044
+ return;
2045
+ }
2046
+ _threadSubscriptionRef.current = subscription;
2047
+ subscription.processDataStream({ onChunk: (chunk) => processStreamChunk(chunk) }).catch((error) => {
2048
+ if (!isAbortError(error)) {
2049
+ console.error("[useChat] Thread subscription failed", error);
2050
+ setIsRunning(false);
2051
+ }
2052
+ }).finally(() => {
2053
+ if (_threadSubscriptionRef.current === subscription) _threadSubscriptionRef.current = null;
2054
+ releaseSubscriptionRefs();
2055
+ });
2056
+ }).catch((error) => {
2057
+ releaseSubscriptionRefs();
2058
+ if (isThreadSignalUnsupportedError(error)) {
2059
+ markThreadSignalsUnsupported();
2060
+ return;
2061
+ }
2062
+ if (!isAbortError(error)) {
2063
+ console.error("[useChat] Thread subscription failed", error);
2064
+ setIsRunning(false);
2065
+ }
2066
+ throw error;
2067
+ });
2068
+ await _threadSubscriptionPromiseRef.current;
2069
+ }, [
2070
+ agentId,
2071
+ baseClient,
2072
+ closeThreadSubscription,
2073
+ markThreadSignalsUnsupported,
2074
+ processStreamChunk
2075
+ ]);
2076
+ useEffect(() => {
2077
+ _threadSignalsUnsupportedRef.current = false;
2078
+ return closeThreadSubscription;
2079
+ }, [
2080
+ agentId,
2081
+ resourceId,
2082
+ threadId,
2083
+ closeThreadSubscription
2084
+ ]);
2085
+ useEffect(() => {
2086
+ if (!threadId || threadSignalsDisabled) {
2087
+ closeThreadSubscription();
2088
+ return;
2089
+ }
2090
+ ensureThreadSubscription({
2091
+ threadId,
2092
+ resourceId: resourceId || agentId
2093
+ }).catch((error) => {
2094
+ if (!isAbortError(error)) console.error("[useChat] Thread subscription failed", error);
2095
+ });
2096
+ }, [
2097
+ agentId,
2098
+ closeThreadSubscription,
2099
+ ensureThreadSubscription,
2100
+ resourceId,
2101
+ threadId,
2102
+ threadSignalsDisabled
2103
+ ]);
2104
+ const generate = async ({ coreUserMessages, model, requestContext, threadId, modelSettings, signal, onFinish, tracingOptions, clientTools }) => {
2105
+ const { frequencyPenalty, presencePenalty, maxRetries, maxTokens, temperature, topK, topP, instructions, providerOptions, maxSteps, requireToolApproval } = modelSettings || {};
2106
+ const resolvedRequestContext = requestContext ?? propsRequestContext;
2107
+ const resolvedClientTools = clientTools ?? hookClientTools;
2108
+ _activeContinuation.current = {
2109
+ model,
2110
+ requestContext: resolvedRequestContext
2111
+ };
2112
+ setIsRunning(true);
2113
+ const agent = new MastraClient({
2114
+ ...baseClient.options,
2115
+ abortSignal: signal
2116
+ }).getAgent(agentId);
2117
+ const runId = v4();
2118
+ _currentRunId.current = runId;
2119
+ const response = await agent.generate(coreUserMessages, {
2120
+ model,
2121
+ runId,
2122
+ maxSteps,
2123
+ modelSettings: {
2124
+ frequencyPenalty,
2125
+ presencePenalty,
2126
+ maxRetries,
2127
+ maxOutputTokens: maxTokens,
2128
+ temperature,
2129
+ topK,
2130
+ topP
2131
+ },
2132
+ instructions,
2133
+ requestContext: resolvedRequestContext,
2134
+ ...threadId ? { memory: {
2135
+ thread: threadId,
2136
+ resource: resourceId || agentId
2137
+ } } : {},
2138
+ providerOptions,
2139
+ tracingOptions,
2140
+ requireToolApproval,
2141
+ clientTools: resolvedClientTools
2142
+ });
2143
+ if (response.finishReason === "suspended" && response.suspendPayload) {
2144
+ const { toolCallId, toolName, args } = response.suspendPayload;
2145
+ if (response.response?.uiMessages) {
2146
+ const dbMessages = dbFromServerUiMessages(response.response.uiMessages, {
2147
+ mode: "generate",
2148
+ requireApprovalMetadata: { [toolName]: {
2149
+ toolCallId,
2150
+ toolName,
2151
+ args
2152
+ } }
2153
+ });
2154
+ setMessages((prev) => [...prev, ...dbMessages]);
2155
+ }
2156
+ setIsRunning(false);
2157
+ return;
2158
+ }
2159
+ setIsRunning(false);
2160
+ if (response && "uiMessages" in response.response && response.response.uiMessages) {
2161
+ const dbMessages = dbFromServerUiMessages(response.response.uiMessages, { mode: "generate" });
2162
+ onFinish?.(dbMessages);
2163
+ setMessages((prev) => [...prev, ...dbMessages]);
2164
+ }
2165
+ };
2166
+ const stream = async ({ coreUserMessages, model, requestContext, threadId, onChunk, modelSettings, signal, tracingOptions, clientTools, signalId, clientMessageId }) => {
2167
+ const { frequencyPenalty, presencePenalty, maxRetries, maxTokens, temperature, topK, topP, instructions, providerOptions, maxSteps, requireToolApproval } = modelSettings || {};
2168
+ const resolvedRequestContext = requestContext ?? propsRequestContext;
2169
+ const resolvedClientTools = clientTools ?? hookClientTools;
2170
+ const signalContinuationOptions = {
2171
+ model,
2172
+ maxSteps,
2173
+ modelSettings: {
2174
+ frequencyPenalty,
2175
+ presencePenalty,
2176
+ maxRetries,
2177
+ maxOutputTokens: maxTokens,
2178
+ temperature,
2179
+ topK,
2180
+ topP
2181
+ },
2182
+ instructions,
2183
+ providerOptions,
2184
+ requireToolApproval,
2185
+ tracingOptions
2186
+ };
2187
+ _activeContinuation.current = {
2188
+ model,
2189
+ requestContext: resolvedRequestContext
2190
+ };
2191
+ setIsRunning(true);
2192
+ _streamAbortRef.current?.abort();
2193
+ const internalAbort = new AbortController();
2194
+ _streamAbortRef.current = internalAbort;
2195
+ if (signal) if (signal.aborted) internalAbort.abort();
2196
+ else signal.addEventListener("abort", () => internalAbort.abort(), { once: true });
2197
+ const agent = new MastraClient({
2198
+ ...baseClient.options,
2199
+ abortSignal: internalAbort.signal
2200
+ }).getAgent(agentId);
2201
+ const streamWithLegacyRoute = async () => {
2202
+ const runId = v4();
2203
+ const response = await agent.stream(coreUserMessages, {
2204
+ model,
2205
+ runId,
2206
+ maxSteps,
2207
+ untilIdle: true,
2208
+ modelSettings: {
2209
+ frequencyPenalty,
2210
+ presencePenalty,
2211
+ maxRetries,
2212
+ maxOutputTokens: maxTokens,
2213
+ temperature,
2214
+ topK,
2215
+ topP
2216
+ },
2217
+ instructions,
2218
+ requestContext: resolvedRequestContext,
2219
+ ...threadId ? { memory: {
2220
+ thread: threadId,
2221
+ resource: resourceId || agentId
2222
+ } } : {},
2223
+ providerOptions,
2224
+ requireToolApproval,
2225
+ tracingOptions,
2226
+ clientTools: resolvedClientTools
2227
+ });
2228
+ _onChunk.current = onChunk;
2229
+ _currentRunId.current = runId;
2230
+ await response.processDataStream({ onChunk: (chunk) => processStreamChunk(chunk, onChunk) });
2231
+ if (_streamAbortRef.current === internalAbort) _streamAbortRef.current = null;
2232
+ setIsRunning(false);
2233
+ };
2234
+ if (!threadId || _threadSignalsUnsupportedRef.current || threadSignalsDisabled) {
2235
+ await streamWithLegacyRoute();
2236
+ return;
2237
+ }
2238
+ _onChunk.current = onChunk;
2239
+ await ensureThreadSubscription({
2240
+ threadId,
2241
+ resourceId: resourceId || agentId
2242
+ });
2243
+ if (_threadSignalsUnsupportedRef.current) {
2244
+ await streamWithLegacyRoute();
2245
+ return;
2246
+ }
2247
+ const resolvedSignalId = signalId ?? v4();
2248
+ const messageContents = getSignalContents(coreUserMessages);
2249
+ const requestContextRecord = resolvedRequestContext;
2250
+ const streamOptions = {
2251
+ model,
2252
+ maxSteps,
2253
+ modelSettings: {
2254
+ frequencyPenalty,
2255
+ presencePenalty,
2256
+ maxRetries,
2257
+ maxOutputTokens: maxTokens,
2258
+ temperature,
2259
+ topK,
2260
+ topP
2261
+ },
2262
+ instructions,
2263
+ requestContext: requestContextRecord,
2264
+ providerOptions,
2265
+ requireToolApproval,
2266
+ tracingOptions
2267
+ };
2268
+ try {
2269
+ const result = await agent.sendMessage({
2270
+ message: clientMessageId ? {
2271
+ contents: messageContents,
2272
+ metadata: { [CLIENT_MESSAGE_ID_KEY]: clientMessageId }
2273
+ } : messageContents,
2274
+ resourceId: resourceId || agentId,
2275
+ threadId,
2276
+ ifIdle: { streamOptions: {
2277
+ ...signalContinuationOptions,
2278
+ requestContext: requestContextRecord,
2279
+ clientTools: resolvedClientTools
2280
+ } }
2281
+ });
2282
+ const echoedSignalId = result.signal && typeof result.signal === "object" && "id" in result.signal && typeof result.signal.id === "string" ? result.signal.id : resolvedSignalId;
2283
+ onSignalSent?.(echoedSignalId, getSignalPreview(coreUserMessages));
2284
+ if (pendingToolApprovalIdsRef.current.size > 0) setIsRunning(false);
2285
+ } catch (error) {
2286
+ if (isThreadSignalUnsupportedError(error)) {
2287
+ onSignalSent?.(resolvedSignalId, getSignalPreview(coreUserMessages));
2288
+ try {
2289
+ await agent.sendSignal({
2290
+ signal: {
2291
+ id: resolvedSignalId,
2292
+ type: "user-message",
2293
+ contents: messageContents
2294
+ },
2295
+ resourceId: resourceId || agentId,
2296
+ threadId,
2297
+ ifIdle: { streamOptions }
2298
+ });
2299
+ return;
2300
+ } catch (signalError) {
2301
+ onSignalEcho?.(resolvedSignalId);
2302
+ if (isThreadSignalUnsupportedError(signalError)) {
2303
+ markThreadSignalsUnsupported();
2304
+ setMessages((prev) => [...prev, fromCoreUserMessagesToMastraDBMessage(coreUserMessages)]);
2305
+ await streamWithLegacyRoute();
2306
+ return;
2307
+ }
2308
+ throw signalError;
2309
+ }
2310
+ }
2311
+ throw error;
2312
+ }
2313
+ if (_streamAbortRef.current === internalAbort) _streamAbortRef.current = null;
2314
+ };
2315
+ const network = async ({ coreUserMessages, model, requestContext, threadId, onNetworkChunk, modelSettings, signal, tracingOptions }) => {
2316
+ const { frequencyPenalty, presencePenalty, maxRetries, maxTokens, temperature, topK, topP, maxSteps } = modelSettings || {};
2317
+ const resolvedRequestContext = requestContext ?? propsRequestContext;
2318
+ _activeContinuation.current = {
2319
+ model,
2320
+ requestContext: resolvedRequestContext
2321
+ };
2322
+ setIsRunning(true);
2323
+ const agent = new MastraClient({
2324
+ ...baseClient.options,
2325
+ abortSignal: signal
2326
+ }).getAgent(agentId);
2327
+ const runId = v4();
2328
+ const response = await agent.network(coreUserMessages, {
2329
+ model,
2330
+ maxSteps,
2331
+ modelSettings: {
2332
+ frequencyPenalty,
2333
+ presencePenalty,
2334
+ maxRetries,
2335
+ maxOutputTokens: maxTokens,
2336
+ temperature,
2337
+ topK,
2338
+ topP
2339
+ },
2340
+ runId,
2341
+ requestContext: resolvedRequestContext,
2342
+ ...threadId ? { memory: {
2343
+ thread: threadId,
2344
+ resource: resourceId || agentId
2345
+ } } : {},
2346
+ tracingOptions
2347
+ });
2348
+ _onNetworkChunk.current = onNetworkChunk;
2349
+ _networkRunId.current = runId;
2350
+ await response.processDataStream({ onChunk: async (chunk) => {
2351
+ setMessages((prev) => accumulateNetworkChunk({
2352
+ chunk,
2353
+ conversation: prev,
2354
+ metadata: { mode: "network" }
2355
+ }));
2356
+ onNetworkChunk?.(chunk);
2357
+ } });
2358
+ setMessages((prev) => finishStreamingAssistantMessage(prev));
2359
+ setIsRunning(false);
2360
+ };
2361
+ const handleCancelRun = () => {
2362
+ _streamAbortRef.current?.abort();
2363
+ _streamAbortRef.current = null;
2364
+ const threadSubscription = _threadSubscriptionRef.current;
2365
+ Promise.resolve(threadSubscription?.abort?.()).catch((error) => {
2366
+ console.error("[useChat] Failed to abort thread subscription", error);
2367
+ });
2368
+ closeThreadSubscription();
2369
+ setMessages((prev) => finishStreamingAssistantMessage(prev));
2370
+ pendingToolApprovalIdsRef.current.clear();
2371
+ setIsAwaitingToolApproval(false);
2372
+ setIsRunning(false);
2373
+ _currentRunId.current = void 0;
2374
+ _onChunk.current = void 0;
2375
+ _networkRunId.current = void 0;
2376
+ _onNetworkChunk.current = void 0;
2377
+ _activeContinuation.current = {};
2378
+ };
2379
+ const approveToolCall = async (toolCallId, resumeData) => {
2380
+ const onChunk = _onChunk.current;
2381
+ const currentRunId = _currentRunId.current;
2382
+ const continuation = _activeContinuation.current;
2383
+ if (!currentRunId) return console.info("[approveToolCall] approveToolCall can only be called after a stream has started");
2384
+ setIsRunning(true);
2385
+ setToolCallApprovals((prev) => ({
2386
+ ...prev,
2387
+ [toolCallId]: { status: "approved" }
2388
+ }));
2389
+ const agent = baseClient.getAgent(agentId);
2390
+ if (_threadSubscriptionKeyRef.current && threadId) {
2391
+ try {
2392
+ await agent.sendToolApproval({
2393
+ resourceId: resourceId || agentId,
2394
+ threadId,
2395
+ toolCallId,
2396
+ approved: true,
2397
+ ...continuation.model !== void 0 ? { streamOptions: { model: continuation.model } } : {},
2398
+ ...resumeData !== void 0 ? { resumeData } : {},
2399
+ requestContext: continuation.requestContext
2400
+ });
2401
+ pendingToolApprovalIdsRef.current.delete(toolCallId);
2402
+ setIsAwaitingToolApproval(pendingToolApprovalIdsRef.current.size > 0);
2403
+ setIsRunning(false);
2404
+ } catch (error) {
2405
+ setToolCallApprovals((prev) => {
2406
+ const next = { ...prev };
2407
+ delete next[toolCallId];
2408
+ return next;
2409
+ });
2410
+ setIsRunning(false);
2411
+ throw error;
2412
+ }
2413
+ return;
2414
+ }
2415
+ await (await agent.approveToolCall({
2416
+ runId: currentRunId,
2417
+ toolCallId,
2418
+ ...continuation
2419
+ })).processDataStream({ onChunk: async (chunk) => {
2420
+ await processStreamChunk(chunk, onChunk);
2421
+ } });
2422
+ setIsRunning(false);
2423
+ };
2424
+ const declineToolCall = async (toolCallId) => {
2425
+ const onChunk = _onChunk.current;
2426
+ const currentRunId = _currentRunId.current;
2427
+ const continuation = _activeContinuation.current;
2428
+ if (!currentRunId) return console.info("[declineToolCall] declineToolCall can only be called after a stream has started");
2429
+ setIsRunning(true);
2430
+ setToolCallApprovals((prev) => ({
2431
+ ...prev,
2432
+ [toolCallId]: { status: "declined" }
2433
+ }));
2434
+ const agent = baseClient.getAgent(agentId);
2435
+ if (_threadSubscriptionKeyRef.current && threadId) {
2436
+ try {
2437
+ await agent.sendToolApproval({
2438
+ resourceId: resourceId || agentId,
2439
+ threadId,
2440
+ toolCallId,
2441
+ approved: false,
2442
+ ...continuation.model !== void 0 ? { streamOptions: { model: continuation.model } } : {},
2443
+ requestContext: continuation.requestContext
2444
+ });
2445
+ pendingToolApprovalIdsRef.current.delete(toolCallId);
2446
+ setIsAwaitingToolApproval(pendingToolApprovalIdsRef.current.size > 0);
2447
+ setIsRunning(false);
2448
+ } catch (error) {
2449
+ setToolCallApprovals((prev) => {
2450
+ const next = { ...prev };
2451
+ delete next[toolCallId];
2452
+ return next;
2453
+ });
2454
+ setIsRunning(false);
2455
+ throw error;
2456
+ }
2457
+ return;
2458
+ }
2459
+ await (await agent.declineToolCall({
2460
+ runId: currentRunId,
2461
+ toolCallId,
2462
+ ...continuation
2463
+ })).processDataStream({ onChunk: async (chunk) => {
2464
+ await processStreamChunk(chunk, onChunk);
2465
+ } });
2466
+ setIsRunning(false);
2467
+ };
2468
+ const approveToolCallGenerate = async (toolCallId) => {
2469
+ const currentRunId = _currentRunId.current;
2470
+ const continuation = _activeContinuation.current;
2471
+ if (!currentRunId) return console.info("[approveToolCallGenerate] approveToolCallGenerate can only be called after a generate has started");
2472
+ setIsRunning(true);
2473
+ setToolCallApprovals((prev) => ({
2474
+ ...prev,
2475
+ [toolCallId]: { status: "approved" }
2476
+ }));
2477
+ const response = await baseClient.getAgent(agentId).approveToolCallGenerate({
2478
+ runId: currentRunId,
2479
+ toolCallId,
2480
+ ...continuation
2481
+ });
2482
+ if (response && "uiMessages" in response.response && response.response.uiMessages) {
2483
+ const dbMessages = dbFromServerUiMessages(response.response.uiMessages, { mode: "generate" });
2484
+ setMessages((prev) => [...prev, ...dbMessages]);
2485
+ }
2486
+ setIsRunning(false);
2487
+ };
2488
+ const declineToolCallGenerate = async (toolCallId) => {
2489
+ const currentRunId = _currentRunId.current;
2490
+ const continuation = _activeContinuation.current;
2491
+ if (!currentRunId) return console.info("[declineToolCallGenerate] declineToolCallGenerate can only be called after a generate has started");
2492
+ setIsRunning(true);
2493
+ setToolCallApprovals((prev) => ({
2494
+ ...prev,
2495
+ [toolCallId]: { status: "declined" }
2496
+ }));
2497
+ const response = await baseClient.getAgent(agentId).declineToolCallGenerate({
2498
+ runId: currentRunId,
2499
+ toolCallId,
2500
+ ...continuation
2501
+ });
2502
+ if (response && "uiMessages" in response.response && response.response.uiMessages) {
2503
+ const dbMessages = dbFromServerUiMessages(response.response.uiMessages, { mode: "generate" });
2504
+ setMessages((prev) => [...prev, ...dbMessages]);
2505
+ }
2506
+ setIsRunning(false);
2507
+ };
2508
+ const approveNetworkToolCall = async (toolName, runId) => {
2509
+ const onNetworkChunk = _onNetworkChunk.current;
2510
+ const networkRunId = runId || _networkRunId.current;
2511
+ const continuation = _activeContinuation.current;
2512
+ if (!networkRunId) return console.info("[approveNetworkToolCall] approveNetworkToolCall can only be called after a network stream has started");
2513
+ setIsRunning(true);
2514
+ setNetworkToolCallApprovals((prev) => ({
2515
+ ...prev,
2516
+ [runId ? `${runId}-${toolName}` : toolName]: { status: "approved" }
2517
+ }));
2518
+ await (await baseClient.getAgent(agentId).approveNetworkToolCall({
2519
+ runId: networkRunId,
2520
+ ...continuation
2521
+ })).processDataStream({ onChunk: async (chunk) => {
2522
+ setMessages((prev) => accumulateNetworkChunk({
2523
+ chunk,
2524
+ conversation: prev,
2525
+ metadata: { mode: "network" }
2526
+ }));
2527
+ onNetworkChunk?.(chunk);
2528
+ } });
2529
+ setMessages((prev) => finishStreamingAssistantMessage(prev));
2530
+ setIsRunning(false);
2531
+ };
2532
+ const declineNetworkToolCall = async (toolName, runId) => {
2533
+ const onNetworkChunk = _onNetworkChunk.current;
2534
+ const networkRunId = runId || _networkRunId.current;
2535
+ const continuation = _activeContinuation.current;
2536
+ if (!networkRunId) return console.info("[declineNetworkToolCall] declineNetworkToolCall can only be called after a network stream has started");
2537
+ setIsRunning(true);
2538
+ setNetworkToolCallApprovals((prev) => ({
2539
+ ...prev,
2540
+ [runId ? `${runId}-${toolName}` : toolName]: { status: "declined" }
2541
+ }));
2542
+ await (await baseClient.getAgent(agentId).declineNetworkToolCall({
2543
+ runId: networkRunId,
2544
+ ...continuation
2545
+ })).processDataStream({ onChunk: async (chunk) => {
2546
+ setMessages((prev) => accumulateNetworkChunk({
2547
+ chunk,
2548
+ conversation: prev,
2549
+ metadata: { mode: "network" }
2550
+ }));
2551
+ onNetworkChunk?.(chunk);
2552
+ } });
2553
+ setMessages((prev) => finishStreamingAssistantMessage(prev));
2554
+ setIsRunning(false);
2555
+ };
2556
+ const sendMessage = async ({ mode = "stream", ...args }) => {
2557
+ const coreUserMessages = [{
2558
+ role: "user",
2559
+ content: [{
2560
+ type: "text",
2561
+ text: args.message
2562
+ }]
2563
+ }];
2564
+ if (args.coreUserMessages) coreUserMessages.push(...args.coreUserMessages);
2565
+ const dbUserMessage = fromCoreUserMessagesToMastraDBMessage(coreUserMessages);
2566
+ const clientSetId = mode === "stream" && args.threadId && !_threadSignalsUnsupportedRef.current && !threadSignalsDisabled ? `client-set-${v4()}` : void 0;
2567
+ const signalId = clientSetId;
2568
+ const clientMessageId = clientSetId;
2569
+ if (signalId) {
2570
+ const metadata = {
2571
+ ...dbUserMessage.content.metadata,
2572
+ mode: "stream",
2573
+ status: "pending",
2574
+ [CLIENT_MESSAGE_ID_KEY]: clientMessageId
2575
+ };
2576
+ const pendingMessage = {
2577
+ ...dbUserMessage,
2578
+ id: clientSetId,
2579
+ content: {
2580
+ ...dbUserMessage.content,
2581
+ metadata
2582
+ }
2583
+ };
2584
+ setMessages((s) => [...s, pendingMessage]);
2585
+ } else setMessages((s) => [...s, dbUserMessage]);
2586
+ try {
2587
+ if (mode === "generate") await generate({
2588
+ ...args,
2589
+ coreUserMessages
2590
+ });
2591
+ else if (mode === "stream") await stream({
2592
+ ...args,
2593
+ coreUserMessages,
2594
+ signalId,
2595
+ clientMessageId
2596
+ });
2597
+ else if (mode === "network") await network({
2598
+ ...args,
2599
+ coreUserMessages
2600
+ });
2601
+ } catch (error) {
2602
+ setIsRunning(false);
2603
+ throw error;
2604
+ }
2605
+ };
2606
+ return {
2607
+ setMessages,
2608
+ sendMessage,
2609
+ isRunning,
2610
+ isAwaitingToolApproval,
2611
+ messages,
2612
+ tasks,
2613
+ approveToolCall,
2614
+ declineToolCall,
2615
+ approveToolCallGenerate,
2616
+ declineToolCallGenerate,
2617
+ cancelRun: handleCancelRun,
2618
+ toolCallApprovals,
2619
+ approveNetworkToolCall,
2620
+ declineNetworkToolCall,
2621
+ networkToolCallApprovals
2622
+ };
2623
+ };
2624
+ //#endregion
2625
+ //#region src/lib/use-mutation.ts
2626
+ /**
2627
+ * Internal helper hook that provides mutation-like functionality without react-query.
2628
+ * Tracks pending, success, and error states for async operations.
2629
+ */
2630
+ function useMutation(mutationFn) {
2631
+ const [isPending, setIsPending] = useState(false);
2632
+ const [isSuccess, setIsSuccess] = useState(false);
2633
+ const [isError, setIsError] = useState(false);
2634
+ const [error, setError] = useState(null);
2635
+ const [data, setData] = useState(void 0);
2636
+ const mutationFnRef = useRef(mutationFn);
2637
+ mutationFnRef.current = mutationFn;
2638
+ const reset = useCallback(() => {
2639
+ setIsPending(false);
2640
+ setIsSuccess(false);
2641
+ setIsError(false);
2642
+ setError(null);
2643
+ setData(void 0);
2644
+ }, []);
2645
+ const mutateAsync = useCallback(async (variables) => {
2646
+ setIsPending(true);
2647
+ setIsSuccess(false);
2648
+ setIsError(false);
2649
+ setError(null);
2650
+ try {
2651
+ const result = await mutationFnRef.current(variables);
2652
+ setData(result);
2653
+ setIsSuccess(true);
2654
+ return result;
2655
+ } catch (err) {
2656
+ setError(err);
2657
+ setIsError(true);
2658
+ throw err;
2659
+ } finally {
2660
+ setIsPending(false);
2661
+ }
2662
+ }, []);
2663
+ return {
2664
+ mutate: useCallback((variables) => {
2665
+ mutateAsync(variables).catch(() => {});
2666
+ }, [mutateAsync]),
2667
+ mutateAsync,
2668
+ isPending,
2669
+ isSuccess,
2670
+ isError,
2671
+ error,
2672
+ data,
2673
+ reset
2674
+ };
2675
+ }
2676
+ //#endregion
2677
+ //#region src/workflows/use-stream-workflow.ts
2678
+ /**
2679
+ * Hook for streaming workflow execution with support for observing, resuming, and time-travel.
2680
+ *
2681
+ * @example
2682
+ * ```tsx
2683
+ * const {
2684
+ * streamWorkflow,
2685
+ * streamResult,
2686
+ * isStreaming,
2687
+ * observeWorkflowStream,
2688
+ * closeStreamsAndReset,
2689
+ * resumeWorkflowStream,
2690
+ * timeTravelWorkflowStream,
2691
+ * } = useStreamWorkflow({
2692
+ * debugMode: true,
2693
+ * tracingOptions: { enabled: true },
2694
+ * onError: (error, defaultMessage) => console.error(defaultMessage, error),
2695
+ * });
2696
+ *
2697
+ * // Start streaming a workflow
2698
+ * await streamWorkflow.mutateAsync({
2699
+ * workflowId: 'my-workflow',
2700
+ * runId: 'run-123',
2701
+ * inputData: { key: 'value' },
2702
+ * requestContext: {},
2703
+ * });
2704
+ * ```
2705
+ */
2706
+ function useStreamWorkflow({ debugMode, tracingOptions, onError }) {
2707
+ const client = useMastraClient();
2708
+ const [streamResult, setStreamResult] = useState({});
2709
+ const [isStreaming, setIsStreaming] = useState(false);
2710
+ const readerRef = useRef(null);
2711
+ const observerRef = useRef(null);
2712
+ const resumeStreamRef = useRef(null);
2713
+ const timeTravelStreamRef = useRef(null);
2714
+ const isMountedRef = useRef(true);
2715
+ useEffect(() => {
2716
+ isMountedRef.current = true;
2717
+ return () => {
2718
+ isMountedRef.current = false;
2719
+ if (readerRef.current) {
2720
+ try {
2721
+ readerRef.current.releaseLock();
2722
+ } catch {}
2723
+ readerRef.current = null;
2724
+ }
2725
+ if (observerRef.current) {
2726
+ try {
2727
+ observerRef.current.releaseLock();
2728
+ } catch {}
2729
+ observerRef.current = null;
2730
+ }
2731
+ if (resumeStreamRef.current) {
2732
+ try {
2733
+ resumeStreamRef.current.releaseLock();
2734
+ } catch {}
2735
+ resumeStreamRef.current = null;
2736
+ }
2737
+ if (timeTravelStreamRef.current) {
2738
+ try {
2739
+ timeTravelStreamRef.current.releaseLock();
2740
+ } catch {}
2741
+ timeTravelStreamRef.current = null;
2742
+ }
2743
+ };
2744
+ }, []);
2745
+ const handleStreamError = useCallback((err, defaultMessage, setStreamingState) => {
2746
+ if (err instanceof TypeError) return;
2747
+ const error = err instanceof Error ? err : new Error(defaultMessage);
2748
+ onError?.(error, defaultMessage);
2749
+ setStreamingState?.(false);
2750
+ }, [onError]);
2751
+ const handleWorkflowFinish = useCallback((value) => {
2752
+ if (value.type === "workflow-finish") {
2753
+ const streamStatus = value.payload?.workflowStatus;
2754
+ const metadata = value.payload?.metadata;
2755
+ setStreamResult((prev) => ({
2756
+ ...prev,
2757
+ status: streamStatus
2758
+ }));
2759
+ if (streamStatus === "failed") throw new Error(metadata?.errorMessage || "Workflow execution failed");
2760
+ }
2761
+ }, []);
2762
+ const streamWorkflow = useMutation(async ({ workflowId, runId, inputData, initialState, requestContext: playgroundRequestContext, perStep }) => {
2763
+ if (readerRef.current) readerRef.current.releaseLock();
2764
+ if (!isMountedRef.current) return;
2765
+ setIsStreaming(true);
2766
+ setStreamResult({ input: inputData });
2767
+ const stream = await (await client.getWorkflow(workflowId).createRun({ runId })).stream({
2768
+ inputData,
2769
+ initialState,
2770
+ requestContext: playgroundRequestContext,
2771
+ closeOnSuspend: true,
2772
+ tracingOptions,
2773
+ perStep: perStep ?? debugMode
2774
+ });
2775
+ if (!stream) return handleStreamError(/* @__PURE__ */ new Error("No stream returned"), "No stream returned", setIsStreaming);
2776
+ const reader = stream.getReader();
2777
+ readerRef.current = reader;
2778
+ try {
2779
+ while (true) {
2780
+ if (!isMountedRef.current) break;
2781
+ const { done, value } = await reader.read();
2782
+ if (done) break;
2783
+ if (isMountedRef.current) {
2784
+ setStreamResult((prev) => {
2785
+ return mapWorkflowStreamChunkToWatchResult(prev, value);
2786
+ });
2787
+ if (value.type === "workflow-step-start") setIsStreaming(true);
2788
+ if (value.type === "workflow-step-suspended") setIsStreaming(false);
2789
+ if (value.type === "workflow-finish") handleWorkflowFinish(value);
2790
+ }
2791
+ }
2792
+ } catch (err) {
2793
+ handleStreamError(err, "Error streaming workflow");
2794
+ } finally {
2795
+ if (isMountedRef.current) setIsStreaming(false);
2796
+ if (readerRef.current) {
2797
+ readerRef.current.releaseLock();
2798
+ readerRef.current = null;
2799
+ }
2800
+ }
2801
+ });
2802
+ const observeWorkflowStream = useMutation(async ({ workflowId, runId, storeRunResult }) => {
2803
+ if (observerRef.current) observerRef.current.releaseLock();
2804
+ if (!isMountedRef.current) return;
2805
+ setIsStreaming(true);
2806
+ setStreamResult(storeRunResult || {});
2807
+ if (storeRunResult?.status === "suspended") {
2808
+ setIsStreaming(false);
2809
+ return;
2810
+ }
2811
+ const stream = await (await client.getWorkflow(workflowId).createRun({ runId })).observeStream();
2812
+ if (!stream) return handleStreamError(/* @__PURE__ */ new Error("No stream returned"), "No stream returned", setIsStreaming);
2813
+ const reader = stream.getReader();
2814
+ observerRef.current = reader;
2815
+ try {
2816
+ while (true) {
2817
+ if (!isMountedRef.current) break;
2818
+ const { done, value } = await reader.read();
2819
+ if (done) break;
2820
+ if (isMountedRef.current) {
2821
+ setStreamResult((prev) => {
2822
+ return mapWorkflowStreamChunkToWatchResult(prev, value);
2823
+ });
2824
+ if (value.type === "workflow-step-start") setIsStreaming(true);
2825
+ if (value.type === "workflow-step-suspended") setIsStreaming(false);
2826
+ if (value.type === "workflow-finish") handleWorkflowFinish(value);
2827
+ }
2828
+ }
2829
+ } catch (err) {
2830
+ handleStreamError(err, "Error observing workflow");
2831
+ } finally {
2832
+ if (isMountedRef.current) setIsStreaming(false);
2833
+ if (observerRef.current) {
2834
+ observerRef.current.releaseLock();
2835
+ observerRef.current = null;
2836
+ }
2837
+ }
2838
+ });
2839
+ const resumeWorkflowStream = useMutation(async ({ workflowId, runId, step, resumeData, requestContext: playgroundRequestContext, perStep }) => {
2840
+ if (resumeStreamRef.current) resumeStreamRef.current.releaseLock();
2841
+ if (!isMountedRef.current) return;
2842
+ setIsStreaming(true);
2843
+ const stream = await (await client.getWorkflow(workflowId).createRun({ runId })).resumeStream({
2844
+ step,
2845
+ resumeData,
2846
+ requestContext: playgroundRequestContext,
2847
+ tracingOptions,
2848
+ perStep: perStep ?? debugMode
2849
+ });
2850
+ if (!stream) return handleStreamError(/* @__PURE__ */ new Error("No stream returned"), "No stream returned", setIsStreaming);
2851
+ const reader = stream.getReader();
2852
+ resumeStreamRef.current = reader;
2853
+ try {
2854
+ while (true) {
2855
+ if (!isMountedRef.current) break;
2856
+ const { done, value } = await reader.read();
2857
+ if (done) break;
2858
+ if (isMountedRef.current) {
2859
+ setStreamResult((prev) => {
2860
+ return mapWorkflowStreamChunkToWatchResult(prev, value);
2861
+ });
2862
+ if (value.type === "workflow-step-start") setIsStreaming(true);
2863
+ if (value.type === "workflow-step-suspended") setIsStreaming(false);
2864
+ if (value.type === "workflow-finish") handleWorkflowFinish(value);
2865
+ }
2866
+ }
2867
+ } catch (err) {
2868
+ handleStreamError(err, "Error resuming workflow stream");
2869
+ } finally {
2870
+ if (isMountedRef.current) setIsStreaming(false);
2871
+ if (resumeStreamRef.current) {
2872
+ resumeStreamRef.current.releaseLock();
2873
+ resumeStreamRef.current = null;
2874
+ }
2875
+ }
2876
+ });
2877
+ const timeTravelWorkflowStream = useMutation(async ({ workflowId, requestContext: playgroundRequestContext, runId, perStep, ...params }) => {
2878
+ if (timeTravelStreamRef.current) timeTravelStreamRef.current.releaseLock();
2879
+ if (!isMountedRef.current) return;
2880
+ setIsStreaming(true);
2881
+ const stream = await (await client.getWorkflow(workflowId).createRun({ runId })).timeTravelStream({
2882
+ ...params,
2883
+ perStep: perStep ?? debugMode,
2884
+ requestContext: playgroundRequestContext,
2885
+ tracingOptions
2886
+ });
2887
+ if (!stream) return handleStreamError(/* @__PURE__ */ new Error("No stream returned"), "No stream returned", setIsStreaming);
2888
+ const reader = stream.getReader();
2889
+ timeTravelStreamRef.current = reader;
2890
+ try {
2891
+ while (true) {
2892
+ if (!isMountedRef.current) break;
2893
+ const { done, value } = await reader.read();
2894
+ if (done) break;
2895
+ if (isMountedRef.current) {
2896
+ setStreamResult((prev) => {
2897
+ return mapWorkflowStreamChunkToWatchResult(prev, value);
2898
+ });
2899
+ if (value.type === "workflow-step-start") setIsStreaming(true);
2900
+ if (value.type === "workflow-step-suspended") setIsStreaming(false);
2901
+ if (value.type === "workflow-finish") handleWorkflowFinish(value);
2902
+ }
2903
+ }
2904
+ } catch (err) {
2905
+ handleStreamError(err, "Error time traveling workflow stream");
2906
+ } finally {
2907
+ if (isMountedRef.current) setIsStreaming(false);
2908
+ if (timeTravelStreamRef.current) {
2909
+ timeTravelStreamRef.current.releaseLock();
2910
+ timeTravelStreamRef.current = null;
2911
+ }
2912
+ }
2913
+ });
2914
+ return {
2915
+ streamWorkflow,
2916
+ streamResult,
2917
+ isStreaming,
2918
+ observeWorkflowStream,
2919
+ closeStreamsAndReset: useCallback(() => {
2920
+ setIsStreaming(false);
2921
+ setStreamResult({});
2922
+ if (readerRef.current) {
2923
+ try {
2924
+ readerRef.current.releaseLock();
2925
+ } catch {}
2926
+ readerRef.current = null;
2927
+ }
2928
+ if (observerRef.current) {
2929
+ try {
2930
+ observerRef.current.releaseLock();
2931
+ } catch {}
2932
+ observerRef.current = null;
2933
+ }
2934
+ if (resumeStreamRef.current) {
2935
+ try {
2936
+ resumeStreamRef.current.releaseLock();
2937
+ } catch {}
2938
+ resumeStreamRef.current = null;
2939
+ }
2940
+ if (timeTravelStreamRef.current) {
2941
+ try {
2942
+ timeTravelStreamRef.current.releaseLock();
2943
+ } catch {}
2944
+ timeTravelStreamRef.current = null;
2945
+ }
2946
+ }, []),
2947
+ resumeWorkflowStream,
2948
+ timeTravelWorkflowStream
2949
+ };
2950
+ }
2951
+ //#endregion
2952
+ //#region src/workflows/hooks.ts
2953
+ /**
2954
+ * Hook for creating workflow runs.
2955
+ * Returns a mutation for creating a new workflow run.
2956
+ *
2957
+ * @example
2958
+ * ```tsx
2959
+ * const createWorkflowRun = useCreateWorkflowRun();
2960
+ *
2961
+ * // Create a run
2962
+ * const { runId } = await createWorkflowRun.mutateAsync({
2963
+ * workflowId: 'my-workflow'
2964
+ * });
2965
+ * ```
2966
+ */
2967
+ function useCreateWorkflowRun() {
2968
+ const client = useMastraClient();
2969
+ return useMutation(async ({ workflowId, prevRunId }) => {
2970
+ try {
2971
+ const { runId: newRunId } = await client.getWorkflow(workflowId).createRun({ runId: prevRunId });
2972
+ return { runId: newRunId };
2973
+ } catch (error) {
2974
+ console.error("Error creating workflow run:", error);
2975
+ throw error;
2976
+ }
2977
+ });
2978
+ }
2979
+ /**
2980
+ * Hook for canceling workflow runs.
2981
+ * Returns a mutation for canceling a running workflow.
2982
+ *
2983
+ * @example
2984
+ * ```tsx
2985
+ * const cancelWorkflowRun = useCancelWorkflowRun();
2986
+ *
2987
+ * // Cancel a run
2988
+ * await cancelWorkflowRun.mutateAsync({
2989
+ * workflowId: 'my-workflow',
2990
+ * runId: 'run-123'
2991
+ * });
2992
+ * ```
2993
+ */
2994
+ function useCancelWorkflowRun() {
2995
+ const client = useMastraClient();
2996
+ return useMutation(async ({ workflowId, runId }) => {
2997
+ try {
2998
+ return (await client.getWorkflow(workflowId).createRun({ runId })).cancelRun();
2999
+ } catch (error) {
3000
+ console.error("Error canceling workflow run:", error);
3001
+ throw error;
3002
+ }
3003
+ });
3004
+ }
3005
+ //#endregion
3006
+ //#region src/workflows/WorkflowStepFactory/WorkflowStepFactory.tsx
3007
+ const renderUnknown = (step, UnknownStep) => UnknownStep?.(step) ?? null;
3008
+ const WorkflowStepFactoryComponent = ({ step, Step, MapStep, AgentStep, ToolStep, ForEachStep, ParallelStep, Conditional, LoopStep, SleepStep, SleepUntilStep, NestedWorkflowStep, UnknownStep }) => {
3009
+ switch (step.kind) {
3010
+ case "step": return /* @__PURE__ */ jsx(Fragment$1, { children: Step?.(step) ?? renderUnknown(step, UnknownStep) });
3011
+ case "map-step": return /* @__PURE__ */ jsx(Fragment$1, { children: MapStep?.(step) ?? renderUnknown(step, UnknownStep) });
3012
+ case "agent-step": return /* @__PURE__ */ jsx(Fragment$1, { children: AgentStep?.(step) ?? renderUnknown(step, UnknownStep) });
3013
+ case "tool-step": return /* @__PURE__ */ jsx(Fragment$1, { children: ToolStep?.(step) ?? renderUnknown(step, UnknownStep) });
3014
+ case "foreach-step": return /* @__PURE__ */ jsx(Fragment$1, { children: ForEachStep?.(step) ?? renderUnknown(step, UnknownStep) });
3015
+ case "parallel-step": return /* @__PURE__ */ jsx(Fragment$1, { children: ParallelStep?.(step) ?? renderUnknown(step, UnknownStep) });
3016
+ case "conditional": return /* @__PURE__ */ jsx(Fragment$1, { children: Conditional?.(step) ?? renderUnknown(step, UnknownStep) });
3017
+ case "loop-step": return /* @__PURE__ */ jsx(Fragment$1, { children: LoopStep?.(step) ?? renderUnknown(step, UnknownStep) });
3018
+ case "sleep-step": return /* @__PURE__ */ jsx(Fragment$1, { children: SleepStep?.(step) ?? renderUnknown(step, UnknownStep) });
3019
+ case "sleep-until-step": return /* @__PURE__ */ jsx(Fragment$1, { children: SleepUntilStep?.(step) ?? renderUnknown(step, UnknownStep) });
3020
+ case "nested-workflow-step": return /* @__PURE__ */ jsx(Fragment$1, { children: NestedWorkflowStep?.(step) ?? renderUnknown(step, UnknownStep) });
3021
+ default: return /* @__PURE__ */ jsx(Fragment$1, { children: renderUnknown(step, UnknownStep) });
3022
+ }
3023
+ };
3024
+ const WorkflowStepFactory = memo(WorkflowStepFactoryComponent);
3025
+ //#endregion
3026
+ //#region src/voice/record-mic-to-file.ts
3027
+ async function recordMicrophoneToFile(onFinish) {
3028
+ const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
3029
+ const mediaRecorder = new MediaRecorder(stream);
3030
+ let chunks = [];
3031
+ mediaRecorder.ondataavailable = (e) => {
3032
+ chunks.push(e.data);
3033
+ };
3034
+ mediaRecorder.onstop = () => {
3035
+ const blob = new Blob(chunks, { type: "audio/webm" });
3036
+ const file = new File([blob], `recording-${Date.now()}.webm`, {
3037
+ type: "audio/webm",
3038
+ lastModified: Date.now()
3039
+ });
3040
+ stream.getTracks().forEach((track) => track.stop());
3041
+ onFinish(file);
3042
+ };
3043
+ return mediaRecorder;
3044
+ }
3045
+ //#endregion
3046
+ //#region src/voice/play-stream-with-web-audio.ts
3047
+ async function playStreamWithWebAudio(stream, onEnded) {
3048
+ const audioContext = new window.AudioContext();
3049
+ const reader = stream.getReader();
3050
+ const chunks = [];
3051
+ try {
3052
+ while (true) {
3053
+ const { done, value } = await reader.read();
3054
+ if (done) break;
3055
+ chunks.push(value);
3056
+ }
3057
+ const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
3058
+ const combinedBuffer = new Uint8Array(totalLength);
3059
+ let offset = 0;
3060
+ for (const chunk of chunks) {
3061
+ combinedBuffer.set(chunk, offset);
3062
+ offset += chunk.length;
3063
+ }
3064
+ const audioBuffer = await audioContext.decodeAudioData(combinedBuffer.buffer);
3065
+ const source = audioContext.createBufferSource();
3066
+ source.buffer = audioBuffer;
3067
+ source.onended = onEnded ?? null;
3068
+ source.connect(audioContext.destination);
3069
+ source.start();
3070
+ return () => {
3071
+ source.onended = null;
3072
+ source.stop();
3073
+ audioContext.close();
3074
+ };
3075
+ } catch (error) {
3076
+ await reader.cancel().catch(() => void 0);
3077
+ await audioContext.close().catch(() => void 0);
3078
+ throw error;
3079
+ } finally {
3080
+ reader.releaseLock();
3081
+ }
3082
+ }
3083
+ //#endregion
3084
+ //#region src/voice/use-speech-recognition.ts
3085
+ const useSpeechRecognition = ({ language = "en-US", agentId, requestContext }) => {
3086
+ const client = useMastraClient();
3087
+ const [agent, setAgent] = useState(null);
3088
+ useEffect(() => {
3089
+ let cancelled = false;
3090
+ if (!agentId) {
3091
+ setAgent(null);
3092
+ return () => {
3093
+ cancelled = true;
3094
+ };
3095
+ }
3096
+ const agent = client.getAgent(agentId);
3097
+ const check = async () => {
3098
+ try {
3099
+ const speakers = await agent.voice.getSpeakers(requestContext);
3100
+ if (!cancelled) setAgent(speakers.length > 0 ? agent : null);
3101
+ } catch {
3102
+ if (!cancelled) setAgent(null);
3103
+ }
3104
+ };
3105
+ check();
3106
+ return () => {
3107
+ cancelled = true;
3108
+ };
3109
+ }, [
3110
+ agentId,
3111
+ client,
3112
+ requestContext
3113
+ ]);
3114
+ const browserSpeechRecognition = useBrowserSpeechRecognition({ language });
3115
+ const mastraSpeechRecognition = useMastraSpeechToText({
3116
+ agent,
3117
+ language
3118
+ });
3119
+ if (!agent) return browserSpeechRecognition;
3120
+ return mastraSpeechRecognition;
1467
3121
  };
1468
- const MessageListClass = "mastra:overflow-y-auto mastra:h-full mastra-list";
1469
- const MessageList = ({ children, className, ...props }) => {
1470
- const listRef = useRef(null);
1471
- useEffect(() => {
1472
- const scrollToBottom = () => {
1473
- if (!listRef.current) return;
1474
- listRef.current.scrollTo({ top: listRef.current.scrollHeight, behavior: "smooth" });
1475
- };
1476
- requestAnimationFrame(scrollToBottom);
1477
- });
1478
- return /* @__PURE__ */ jsx("div", { className: className || MessageListClass, ...props, ref: listRef, children });
3122
+ const useBrowserSpeechRecognition = ({ language = "en-US" }) => {
3123
+ const speechRecognitionRef = useRef(null);
3124
+ const [state, setState] = useState({
3125
+ isListening: false,
3126
+ transcript: "",
3127
+ error: null
3128
+ });
3129
+ const start = () => {
3130
+ if (!speechRecognitionRef.current) return;
3131
+ speechRecognitionRef.current.start();
3132
+ };
3133
+ const stop = () => {
3134
+ if (!speechRecognitionRef.current) return;
3135
+ speechRecognitionRef.current.stop();
3136
+ };
3137
+ useEffect(() => {
3138
+ if (!("webkitSpeechRecognition" in window) && !("SpeechRecognition" in window)) {
3139
+ setState((prev) => ({
3140
+ ...prev,
3141
+ error: "Speech Recognition not supported in this browser"
3142
+ }));
3143
+ return;
3144
+ }
3145
+ const recognition = new (window.SpeechRecognition || window.webkitSpeechRecognition)();
3146
+ speechRecognitionRef.current = recognition;
3147
+ recognition.continuous = true;
3148
+ recognition.lang = language;
3149
+ recognition.onstart = () => {
3150
+ setState((prev) => ({
3151
+ ...prev,
3152
+ isListening: true,
3153
+ error: null
3154
+ }));
3155
+ };
3156
+ recognition.onresult = (event) => {
3157
+ let finalTranscript = "";
3158
+ for (let i = event.resultIndex; i < event.results.length; i++) {
3159
+ const transcript = event.results[i][0].transcript;
3160
+ if (event.results[i].isFinal) finalTranscript += transcript + " ";
3161
+ }
3162
+ setState((prev) => ({
3163
+ ...prev,
3164
+ transcript: finalTranscript
3165
+ }));
3166
+ };
3167
+ recognition.onerror = (event) => {
3168
+ setState((prev) => ({
3169
+ ...prev,
3170
+ error: `Error: ${event.error}`
3171
+ }));
3172
+ };
3173
+ recognition.onend = () => setState((prev) => ({
3174
+ ...prev,
3175
+ isListening: false
3176
+ }));
3177
+ return () => {
3178
+ try {
3179
+ recognition.stop();
3180
+ } catch {}
3181
+ recognition.onstart = null;
3182
+ recognition.onresult = null;
3183
+ recognition.onerror = null;
3184
+ recognition.onend = null;
3185
+ speechRecognitionRef.current = null;
3186
+ };
3187
+ }, [language]);
3188
+ return {
3189
+ ...state,
3190
+ start,
3191
+ stop
3192
+ };
1479
3193
  };
1480
- const MessageStreamingClass = "mastra:inline-block mastra:w-[2px] mastra:h-[1em] mastra:bg-text5 mastra:ml-0.5 mastra:align-text-bottom mastra:animate-pulse";
1481
- const MessageStreaming = ({ className, ...props }) => {
1482
- return /* @__PURE__ */ jsx("span", { className: className || MessageStreamingClass, ...props });
3194
+ const useMastraSpeechToText = ({ agent, language }) => {
3195
+ const [state, setState] = useState({
3196
+ isListening: false,
3197
+ transcript: "",
3198
+ error: null
3199
+ });
3200
+ const recorderRef = useRef(null);
3201
+ const sessionRef = useRef(0);
3202
+ const startInFlightRef = useRef(false);
3203
+ useEffect(() => {
3204
+ return () => {
3205
+ sessionRef.current += 1;
3206
+ startInFlightRef.current = false;
3207
+ recorderRef.current?.stop();
3208
+ recorderRef.current = null;
3209
+ };
3210
+ }, [agent]);
3211
+ const handleFinish = (session) => (file) => {
3212
+ if (!agent || session !== sessionRef.current) return;
3213
+ recorderRef.current = null;
3214
+ setState((prev) => ({
3215
+ ...prev,
3216
+ isListening: false
3217
+ }));
3218
+ agent.voice.listen(file, { language }).then((res) => {
3219
+ if (session !== sessionRef.current) return;
3220
+ setState((prev) => ({
3221
+ ...prev,
3222
+ transcript: res.text,
3223
+ error: null
3224
+ }));
3225
+ }).catch((error) => {
3226
+ if (session !== sessionRef.current) return;
3227
+ const message = error instanceof Error ? error.message : "Failed to transcribe speech";
3228
+ setState((prev) => ({
3229
+ ...prev,
3230
+ error: message
3231
+ }));
3232
+ });
3233
+ };
3234
+ const start = () => {
3235
+ if (!agent || startInFlightRef.current || recorderRef.current) return;
3236
+ startInFlightRef.current = true;
3237
+ sessionRef.current += 1;
3238
+ const session = sessionRef.current;
3239
+ recordMicrophoneToFile(handleFinish(session)).then((recorder) => {
3240
+ startInFlightRef.current = false;
3241
+ if (session !== sessionRef.current) {
3242
+ try {
3243
+ recorder.stop();
3244
+ } catch {}
3245
+ return;
3246
+ }
3247
+ recorderRef.current = recorder;
3248
+ setState((prev) => ({
3249
+ ...prev,
3250
+ isListening: true,
3251
+ error: null
3252
+ }));
3253
+ recorder.start();
3254
+ }).catch((error) => {
3255
+ startInFlightRef.current = false;
3256
+ if (session !== sessionRef.current) return;
3257
+ const message = error instanceof Error ? error.message : "Failed to start speech recording";
3258
+ setState((prev) => ({
3259
+ ...prev,
3260
+ isListening: false,
3261
+ error: message
3262
+ }));
3263
+ });
3264
+ };
3265
+ const stop = () => {
3266
+ if (recorderRef.current) {
3267
+ recorderRef.current.stop();
3268
+ recorderRef.current = null;
3269
+ } else if (startInFlightRef.current) sessionRef.current += 1;
3270
+ startInFlightRef.current = false;
3271
+ setState((prev) => ({
3272
+ ...prev,
3273
+ isListening: false
3274
+ }));
3275
+ };
3276
+ return {
3277
+ ...state,
3278
+ start,
3279
+ stop
3280
+ };
1483
3281
  };
3282
+ //#endregion
3283
+ export { AgentIcon, CLIENT_MESSAGE_ID_KEY, CodeBlock, CodeBlockClass, CodeCopyButton, Entity, EntityCaret, EntityContent, EntityContentClass, EntityTrigger, EntityTriggerClass, EntityTriggerVariantClasses, Entry, EntryClass, EntryTitle, EntryTitleClass, Icon, IconButton, IconButtonClass, IconSizes, MastraReactProvider, Message, MessageActions, MessageActionsClass, MessageClass, MessageContent, MessageContentClass, MessageFactory, MessageList, MessageListClass, MessageStreaming, MessageStreamingClass, MessageUsage, MessageUsageClass, MessageUsageEntry, MessageUsageEntryClass, MessageUsageValue, MessageUsageValueClass, MessageUsages, MessageUsagesClass, ToolApproval, ToolApprovalActions, ToolApprovalActionsClass, ToolApprovalClass, ToolApprovalContent, ToolApprovalContentClass, ToolApprovalHeader, ToolApprovalHeaderClass, ToolApprovalTitle, ToolApprovalTitleClass, ToolsIcon, Tooltip, TooltipContent, TooltipContentClass, TooltipTrigger, WorkflowIcon, WorkflowStepFactory, accumulateChunk, accumulateNetworkChunk, finishStreamingAssistantMessage, fromCoreUserMessageToMastraDBMessage, fromCoreUserMessagesToMastraDBMessage, mapWorkflowStreamChunkToWatchResult, playStreamWithWebAudio, recordMicrophoneToFile, useCancelWorkflowRun, useChat, useCreateWorkflowRun, useEntity, useMastraClient, useSpeechRecognition, useStreamWorkflow };
1484
3284
 
1485
- export { AgentIcon, CodeBlock, CodeBlockClass, CodeCopyButton, Entity, EntityCaret, EntityContent, EntityContentClass, EntityTrigger, EntityTriggerClass, EntityTriggerVariantClasses, Entry, EntryClass, EntryTitle, EntryTitleClass, Icon, IconButton, IconButtonClass, IconSizes, MastraReactProvider, Message, MessageActions, MessageActionsClass, MessageClass, MessageContent, MessageContentClass, MessageList, MessageListClass, MessageStreaming, MessageStreamingClass, MessageUsage, MessageUsageClass, MessageUsageEntry, MessageUsageEntryClass, MessageUsageValue, MessageUsageValueClass, MessageUsages, MessageUsagesClass, ToolApproval, ToolApprovalActions, ToolApprovalActionsClass, ToolApprovalClass, ToolApprovalContent, ToolApprovalContentClass, ToolApprovalHeader, ToolApprovalHeaderClass, ToolApprovalTitle, ToolApprovalTitleClass, ToolsIcon, Tooltip, TooltipContent, TooltipContentClass, TooltipTrigger, WorkflowIcon, mapWorkflowStreamChunkToWatchResult, toAssistantUIMessage, toUIMessage, useChat, useEntity, useMastraClient };
1486
- //# sourceMappingURL=index.js.map
3285
+ //# sourceMappingURL=index.js.map