@dbx-tools/ui-mastra 0.6.218 → 0.6.219
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +114 -21
- package/index.ts +10 -9
- package/package.json +8 -8
- package/src/react/bubbles.tsx +4 -1
- package/src/react/chat-approvals.ts +9 -4
- package/src/react/chat-composer.tsx +80 -73
- package/src/react/chat-feedback.ts +2 -2
- package/src/react/chat-history.ts +79 -10
- package/src/react/chat-stream.ts +29 -59
- package/src/react/chat-thread-layout.tsx +17 -1
- package/src/react/chat-view.tsx +10 -0
- package/src/react/export-menu.tsx +8 -1
- package/src/react/index.ts +18 -2
- package/src/react/mastra-assistant.tsx +509 -0
- package/src/react/mastra-chat.tsx +120 -72
- package/src/react/thread-sidebar.tsx +33 -43
- package/src/react/thread-tabs.tsx +141 -117
- package/src/react/tool-pill.tsx +24 -1
- package/src/react/types.ts +32 -22
- package/src/support/mastra-client.ts +203 -171
- package/src/support/request-context.ts +30 -0
- package/src/support/thread-sessions.ts +16 -38
- package/src/support/tool-events.ts +41 -0
- package/src/react/chat-stream-reducer.ts +0 -243
- package/src/support/mastra-stream.ts +0 -59
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
cn,
|
|
10
10
|
} from "@dbx-tools/ui-appkit/react";
|
|
11
11
|
import { HistoryIcon, Loader2Icon, PlusIcon, XIcon } from "lucide-react";
|
|
12
|
-
import { useEffect, useRef, useState } from "react";
|
|
12
|
+
import { type ReactNode, useEffect, useRef, useState } from "react";
|
|
13
13
|
import { ThreadSidebar, type ThreadSidebarProps } from "./thread-sidebar.tsx";
|
|
14
14
|
import { threadTitle } from "../support/thread-labels.ts";
|
|
15
15
|
|
|
@@ -36,8 +36,90 @@ export interface ThreadTabsProps extends Omit<ThreadSidebarProps, "onHide" | "si
|
|
|
36
36
|
* affordance hidden when omitted.
|
|
37
37
|
*/
|
|
38
38
|
onCloseTab?: (threadId: string) => void;
|
|
39
|
+
/** Host actions appended after New and History. */
|
|
40
|
+
actions?: ReactNode;
|
|
41
|
+
/** Render New/History/actions in this row. Defaults to true. */
|
|
42
|
+
showActions?: boolean;
|
|
43
|
+
/** Replacement for the default plus icon. */
|
|
44
|
+
newIcon?: ReactNode;
|
|
45
|
+
/** Replacement for the default history icon. */
|
|
46
|
+
historyIcon?: ReactNode;
|
|
39
47
|
}
|
|
40
48
|
|
|
49
|
+
/** New/history controls reused by the tab strip and persistent assistant header. */
|
|
50
|
+
export interface ThreadActionsProps extends Omit<
|
|
51
|
+
ThreadSidebarProps,
|
|
52
|
+
"onHide" | "side" | "className"
|
|
53
|
+
> {
|
|
54
|
+
actions?: ReactNode;
|
|
55
|
+
newIcon?: ReactNode;
|
|
56
|
+
historyIcon?: ReactNode;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Render New and History controls, plus optional host actions, as one group. */
|
|
60
|
+
export const ThreadActions = ({ actions, newIcon, historyIcon, ...list }: ThreadActionsProps) => {
|
|
61
|
+
const { onSelect, onNew } = list;
|
|
62
|
+
const [historyOpen, setHistoryOpen] = useState(false);
|
|
63
|
+
return (
|
|
64
|
+
<>
|
|
65
|
+
{onNew && (
|
|
66
|
+
<Tooltip>
|
|
67
|
+
<TooltipTrigger asChild>
|
|
68
|
+
<Button
|
|
69
|
+
type="button"
|
|
70
|
+
variant="ghost"
|
|
71
|
+
size="icon"
|
|
72
|
+
onClick={onNew}
|
|
73
|
+
aria-label="New chat"
|
|
74
|
+
className="size-7 shrink-0"
|
|
75
|
+
>
|
|
76
|
+
{newIcon ?? <PlusIcon className="size-4" />}
|
|
77
|
+
</Button>
|
|
78
|
+
</TooltipTrigger>
|
|
79
|
+
<TooltipContent>New chat</TooltipContent>
|
|
80
|
+
</Tooltip>
|
|
81
|
+
)}
|
|
82
|
+
<Popover open={historyOpen} onOpenChange={setHistoryOpen}>
|
|
83
|
+
<Tooltip>
|
|
84
|
+
<TooltipTrigger asChild>
|
|
85
|
+
<PopoverTrigger asChild>
|
|
86
|
+
<Button
|
|
87
|
+
type="button"
|
|
88
|
+
variant="ghost"
|
|
89
|
+
size="icon"
|
|
90
|
+
aria-label="Conversation history"
|
|
91
|
+
className="size-7 shrink-0"
|
|
92
|
+
>
|
|
93
|
+
{historyIcon ?? <HistoryIcon className="size-4" />}
|
|
94
|
+
</Button>
|
|
95
|
+
</PopoverTrigger>
|
|
96
|
+
</TooltipTrigger>
|
|
97
|
+
<TooltipContent>Conversation history</TooltipContent>
|
|
98
|
+
</Tooltip>
|
|
99
|
+
<PopoverContent align="end" aria-label="Conversation history" className="w-72 p-0">
|
|
100
|
+
<ThreadSidebar
|
|
101
|
+
{...list}
|
|
102
|
+
onSelect={(id) => {
|
|
103
|
+
onSelect(id);
|
|
104
|
+
setHistoryOpen(false);
|
|
105
|
+
}}
|
|
106
|
+
{...(onNew
|
|
107
|
+
? {
|
|
108
|
+
onNew: () => {
|
|
109
|
+
onNew();
|
|
110
|
+
setHistoryOpen(false);
|
|
111
|
+
},
|
|
112
|
+
}
|
|
113
|
+
: {})}
|
|
114
|
+
className="h-96 w-full border-none"
|
|
115
|
+
/>
|
|
116
|
+
</PopoverContent>
|
|
117
|
+
</Popover>
|
|
118
|
+
{actions}
|
|
119
|
+
</>
|
|
120
|
+
);
|
|
121
|
+
};
|
|
122
|
+
|
|
41
123
|
/**
|
|
42
124
|
* Tab strip for the `top` thread placement. Each open conversation is a tab
|
|
43
125
|
* showing its title, a spinner while it streams in the background, and a close
|
|
@@ -47,11 +129,17 @@ export interface ThreadTabsProps extends Omit<ThreadSidebarProps, "onHide" | "si
|
|
|
47
129
|
* into view when the selection changes, so switching from history never leaves
|
|
48
130
|
* the current conversation off-screen.
|
|
49
131
|
*/
|
|
50
|
-
export const ThreadTabs = ({
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
132
|
+
export const ThreadTabs = ({
|
|
133
|
+
openThreadIds,
|
|
134
|
+
onCloseTab,
|
|
135
|
+
actions,
|
|
136
|
+
showActions = true,
|
|
137
|
+
newIcon,
|
|
138
|
+
historyIcon,
|
|
139
|
+
className,
|
|
140
|
+
...list
|
|
141
|
+
}: ThreadTabsProps) => {
|
|
142
|
+
const { threads, activeThreadId, streamingThreadIds = [], onSelect } = list;
|
|
55
143
|
const activeTabRef = useRef<HTMLDivElement | null>(null);
|
|
56
144
|
useEffect(() => {
|
|
57
145
|
activeTabRef.current?.scrollIntoView({ block: "nearest", inline: "nearest" });
|
|
@@ -62,129 +150,65 @@ export const ThreadTabs = ({ openThreadIds, onCloseTab, className, ...list }: Th
|
|
|
62
150
|
// in the same centered column as the transcript and composer.
|
|
63
151
|
<div className={cn("w-full border-b border-border text-xs", className)}>
|
|
64
152
|
<div className="mx-auto flex w-full max-w-4xl items-center gap-1 px-2 py-1 md:px-4">
|
|
65
|
-
<div
|
|
153
|
+
<div className="flex min-w-0 flex-1 items-center gap-1 overflow-x-auto">
|
|
66
154
|
{openThreadIds.map((id) => {
|
|
67
155
|
const thread = threads.find((t) => t.id === id) ?? { id };
|
|
68
156
|
const isActive = id === activeThreadId;
|
|
69
157
|
const isStreaming = streamingThreadIds.includes(id);
|
|
70
158
|
const title = threadTitle(thread);
|
|
71
159
|
return (
|
|
72
|
-
<
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
160
|
+
<div
|
|
161
|
+
key={id}
|
|
162
|
+
ref={isActive ? activeTabRef : undefined}
|
|
163
|
+
className={cn(
|
|
164
|
+
"group flex max-w-[12rem] shrink-0 items-center rounded-md border border-transparent",
|
|
165
|
+
"hover:bg-accent hover:text-accent-foreground",
|
|
166
|
+
isActive && "border-border bg-accent text-accent-foreground",
|
|
167
|
+
)}
|
|
168
|
+
>
|
|
169
|
+
<Tooltip>
|
|
170
|
+
<TooltipTrigger asChild>
|
|
171
|
+
<button
|
|
172
|
+
type="button"
|
|
173
|
+
onClick={() => onSelect(id)}
|
|
174
|
+
className="flex min-w-0 flex-1 cursor-pointer items-center gap-1.5 px-2 py-1 text-left"
|
|
175
|
+
aria-current={isActive ? "page" : undefined}
|
|
176
|
+
>
|
|
177
|
+
{isStreaming && (
|
|
178
|
+
<Loader2Icon
|
|
179
|
+
aria-label="Streaming"
|
|
180
|
+
className="size-3 shrink-0 animate-spin text-primary"
|
|
181
|
+
/>
|
|
182
|
+
)}
|
|
183
|
+
<span className="truncate">{title}</span>
|
|
184
|
+
</button>
|
|
185
|
+
</TooltipTrigger>
|
|
186
|
+
<TooltipContent>{title}</TooltipContent>
|
|
187
|
+
</Tooltip>
|
|
188
|
+
{onCloseTab && (
|
|
189
|
+
<Button
|
|
190
|
+
type="button"
|
|
191
|
+
variant="ghost"
|
|
192
|
+
size="icon"
|
|
193
|
+
onClick={() => onCloseTab(id)}
|
|
194
|
+
aria-label="Close tab"
|
|
86
195
|
className={cn(
|
|
87
|
-
"
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
isActive && "
|
|
196
|
+
"mr-1 size-[24px] shrink-0",
|
|
197
|
+
// The active tab always shows its close button; the rest
|
|
198
|
+
// reveal one on hover / keyboard focus.
|
|
199
|
+
!isActive && "opacity-0 group-hover:opacity-100 focus-visible:opacity-100",
|
|
91
200
|
)}
|
|
92
201
|
>
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
/>
|
|
98
|
-
)}
|
|
99
|
-
<span className="truncate">{title}</span>
|
|
100
|
-
{onCloseTab && (
|
|
101
|
-
<Button
|
|
102
|
-
type="button"
|
|
103
|
-
variant="ghost"
|
|
104
|
-
size="icon"
|
|
105
|
-
onClick={(e) => {
|
|
106
|
-
e.stopPropagation();
|
|
107
|
-
onCloseTab(id);
|
|
108
|
-
}}
|
|
109
|
-
aria-label="Close tab"
|
|
110
|
-
className={cn(
|
|
111
|
-
"size-4 shrink-0",
|
|
112
|
-
// The active tab always shows its close button; the
|
|
113
|
-
// rest reveal one on hover / keyboard focus so the
|
|
114
|
-
// strip stays quiet.
|
|
115
|
-
!isActive &&
|
|
116
|
-
"opacity-0 group-hover:opacity-100 focus-visible:opacity-100",
|
|
117
|
-
)}
|
|
118
|
-
>
|
|
119
|
-
<XIcon className="size-3" />
|
|
120
|
-
</Button>
|
|
121
|
-
)}
|
|
122
|
-
</div>
|
|
123
|
-
</TooltipTrigger>
|
|
124
|
-
<TooltipContent>{title}</TooltipContent>
|
|
125
|
-
</Tooltip>
|
|
202
|
+
<XIcon className="size-3" />
|
|
203
|
+
</Button>
|
|
204
|
+
)}
|
|
205
|
+
</div>
|
|
126
206
|
);
|
|
127
207
|
})}
|
|
128
208
|
</div>
|
|
129
|
-
{
|
|
130
|
-
<
|
|
131
|
-
|
|
132
|
-
<Button
|
|
133
|
-
type="button"
|
|
134
|
-
variant="ghost"
|
|
135
|
-
size="icon"
|
|
136
|
-
onClick={onNew}
|
|
137
|
-
aria-label="New chat"
|
|
138
|
-
className="size-7 shrink-0"
|
|
139
|
-
>
|
|
140
|
-
<PlusIcon className="size-4" />
|
|
141
|
-
</Button>
|
|
142
|
-
</TooltipTrigger>
|
|
143
|
-
<TooltipContent>New chat</TooltipContent>
|
|
144
|
-
</Tooltip>
|
|
145
|
-
)}
|
|
146
|
-
<Popover open={historyOpen} onOpenChange={setHistoryOpen}>
|
|
147
|
-
<Tooltip>
|
|
148
|
-
<TooltipTrigger asChild>
|
|
149
|
-
<PopoverTrigger asChild>
|
|
150
|
-
<Button
|
|
151
|
-
type="button"
|
|
152
|
-
variant="ghost"
|
|
153
|
-
size="icon"
|
|
154
|
-
aria-label="Conversation history"
|
|
155
|
-
className="size-7 shrink-0"
|
|
156
|
-
>
|
|
157
|
-
<HistoryIcon className="size-4" />
|
|
158
|
-
</Button>
|
|
159
|
-
</PopoverTrigger>
|
|
160
|
-
</TooltipTrigger>
|
|
161
|
-
<TooltipContent>Conversation history</TooltipContent>
|
|
162
|
-
</Tooltip>
|
|
163
|
-
<PopoverContent align="end" className="w-72 p-0">
|
|
164
|
-
{/*
|
|
165
|
-
* The same list the docked placements render, just framed as a menu:
|
|
166
|
-
* selecting (or starting) a conversation opens it as a tab and
|
|
167
|
-
* dismisses the popover, while rename / delete / cancel work in
|
|
168
|
-
* place.
|
|
169
|
-
*/}
|
|
170
|
-
<ThreadSidebar
|
|
171
|
-
{...list}
|
|
172
|
-
onSelect={(id) => {
|
|
173
|
-
onSelect(id);
|
|
174
|
-
setHistoryOpen(false);
|
|
175
|
-
}}
|
|
176
|
-
{...(onNew
|
|
177
|
-
? {
|
|
178
|
-
onNew: () => {
|
|
179
|
-
onNew();
|
|
180
|
-
setHistoryOpen(false);
|
|
181
|
-
},
|
|
182
|
-
}
|
|
183
|
-
: {})}
|
|
184
|
-
className="h-96 w-full border-none"
|
|
185
|
-
/>
|
|
186
|
-
</PopoverContent>
|
|
187
|
-
</Popover>
|
|
209
|
+
{showActions ? (
|
|
210
|
+
<ThreadActions {...list} actions={actions} newIcon={newIcon} historyIcon={historyIcon} />
|
|
211
|
+
) : null}
|
|
188
212
|
</div>
|
|
189
213
|
</div>
|
|
190
214
|
);
|
package/src/react/tool-pill.tsx
CHANGED
|
@@ -452,6 +452,26 @@ const ToolStatusIcon = ({ status }: { status: "running" | "done" | "error" }) =>
|
|
|
452
452
|
return <CheckIcon className="size-3 text-muted-foreground" />;
|
|
453
453
|
};
|
|
454
454
|
|
|
455
|
+
/** Pretty-print a complete tool payload without slicing or summarizing it. */
|
|
456
|
+
export function formatRawToolPayload(value: unknown): string {
|
|
457
|
+
if (typeof value === "string") return value;
|
|
458
|
+
return JSON.stringify(value, null, 2) ?? String(value);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
const RawToolPayload = ({ label, value }: { label: "Request" | "Response"; value: unknown }) => (
|
|
462
|
+
<Collapsible className="rounded border border-border/60 bg-background/40">
|
|
463
|
+
<CollapsibleTrigger className="group flex w-full items-center gap-1.5 px-2 py-1 text-left text-[11px] uppercase tracking-wide text-muted-foreground hover:text-foreground">
|
|
464
|
+
<ChevronDownIcon className="size-3 shrink-0 transition-transform group-data-[state=closed]:-rotate-90" />
|
|
465
|
+
<span>{label}</span>
|
|
466
|
+
</CollapsibleTrigger>
|
|
467
|
+
<CollapsibleContent>
|
|
468
|
+
<pre className="max-h-80 overflow-auto whitespace-pre-wrap break-words border-t border-border/50 px-2 py-2 font-mono text-[11px] leading-relaxed text-foreground">
|
|
469
|
+
{formatRawToolPayload(value)}
|
|
470
|
+
</pre>
|
|
471
|
+
</CollapsibleContent>
|
|
472
|
+
</Collapsible>
|
|
473
|
+
);
|
|
474
|
+
|
|
455
475
|
/**
|
|
456
476
|
* True when a tool event has anything worth expanding for. The
|
|
457
477
|
* question text already rides on the row header (see
|
|
@@ -460,6 +480,7 @@ const ToolStatusIcon = ({ status }: { status: "running" | "done" | "error" }) =>
|
|
|
460
480
|
*/
|
|
461
481
|
const hasExpandableDetails = (event: ToolEvent): boolean => {
|
|
462
482
|
if (event.status === "running") return true;
|
|
483
|
+
if ("input" in event || "output" in event) return true;
|
|
463
484
|
const summary = summarizeProgress(event.progress ?? []);
|
|
464
485
|
return summary.groups.some(
|
|
465
486
|
(g) => g.attachments.some(isAttachmentRenderable) || g.errors.length > 0,
|
|
@@ -531,8 +552,10 @@ const ToolCallRow = ({ event }: { event: ToolEvent }) => {
|
|
|
531
552
|
{header}
|
|
532
553
|
</CollapsibleTrigger>
|
|
533
554
|
<CollapsibleContent>
|
|
534
|
-
<div className="px-2 pb-2">
|
|
555
|
+
<div className="flex flex-col gap-2 px-2 pb-2">
|
|
535
556
|
<ToolProgressDetails summary={summary} omitQuestion />
|
|
557
|
+
{"input" in event ? <RawToolPayload label="Request" value={event.input} /> : null}
|
|
558
|
+
{"output" in event ? <RawToolPayload label="Response" value={event.output} /> : null}
|
|
536
559
|
</div>
|
|
537
560
|
</CollapsibleContent>
|
|
538
561
|
</Collapsible>
|
package/src/react/types.ts
CHANGED
|
@@ -22,6 +22,10 @@ export type ToolEvent = {
|
|
|
22
22
|
id: string;
|
|
23
23
|
toolName: string;
|
|
24
24
|
status: "running" | "done" | "error";
|
|
25
|
+
/** Complete tool-call arguments as emitted by Mastra. */
|
|
26
|
+
input?: unknown;
|
|
27
|
+
/** Complete tool result/error as emitted by Mastra. */
|
|
28
|
+
output?: unknown;
|
|
25
29
|
progress?: ToolProgress[];
|
|
26
30
|
};
|
|
27
31
|
|
|
@@ -172,11 +176,16 @@ export type ChatViewProps = {
|
|
|
172
176
|
*/
|
|
173
177
|
defaultModelName?: string;
|
|
174
178
|
/**
|
|
175
|
-
* Host-owned controls rendered in the composer
|
|
179
|
+
* Host-owned controls rendered in the composer footer immediately before the
|
|
176
180
|
* model selector. Use this for per-turn options that belong beside model
|
|
177
181
|
* routing without forking the chat surface.
|
|
178
182
|
*/
|
|
179
183
|
composerActions?: ReactNode;
|
|
184
|
+
/**
|
|
185
|
+
* Host-owned actions rendered on the left side of the composer's fixed
|
|
186
|
+
* footer, before Export and Clear.
|
|
187
|
+
*/
|
|
188
|
+
composerLeadingActions?: ReactNode;
|
|
180
189
|
/**
|
|
181
190
|
* Optional infinite-scroll-up handler. Fired when the user scrolls
|
|
182
191
|
* within `TOP_LOAD_MORE_THRESHOLD_PX` of the top of the
|
|
@@ -202,23 +211,16 @@ export type ChatViewProps = {
|
|
|
202
211
|
* stream Response and pipe it through the same chunk handler
|
|
203
212
|
* (this is exactly what `useMastraChat` does).
|
|
204
213
|
*
|
|
205
|
-
* It requires the `runId` Mastra emitted with the approval
|
|
206
|
-
*
|
|
207
|
-
*
|
|
208
|
-
* `pendingApprovalsByMessage` entry. It will be missing only for
|
|
209
|
-
* approvals reconstructed from history (where the original runId
|
|
210
|
-
* is lost), in which case the handler should surface a "this
|
|
211
|
-
* approval is stale, please re-ask the model" message rather than
|
|
212
|
-
* trying to resume a workflow that no longer exists.
|
|
214
|
+
* It requires the `runId` Mastra emitted with the approval chunk.
|
|
215
|
+
* `useMastraChat` also restores run ids from native suspended-run
|
|
216
|
+
* storage when history is reloaded.
|
|
213
217
|
*/
|
|
214
218
|
onResolveToolApproval?: (args: ApprovalDecision) => void | Promise<void>;
|
|
215
219
|
/**
|
|
216
|
-
*
|
|
217
|
-
*
|
|
218
|
-
*
|
|
219
|
-
*
|
|
220
|
-
* UI works without injecting synthetic data parts. Each entry is
|
|
221
|
-
* merged with any approvals already discovered in `message.parts`.
|
|
220
|
+
* Approval requests keyed by assistant message id. This restores
|
|
221
|
+
* persisted suspended runs after a reload and also supports transports
|
|
222
|
+
* that do not surface approvals as `UIMessage` parts. Entries merge with
|
|
223
|
+
* approvals discovered in `message.parts`.
|
|
222
224
|
*/
|
|
223
225
|
pendingApprovalsByMessage?: Record<string, PendingApproval[]>;
|
|
224
226
|
/**
|
|
@@ -246,6 +248,17 @@ export type ChatViewProps = {
|
|
|
246
248
|
* is too narrow for one. See {@link ThreadPlacement}.
|
|
247
249
|
*/
|
|
248
250
|
threadPlacement?: ThreadPlacement;
|
|
251
|
+
/**
|
|
252
|
+
* Host-owned controls appended after the top tab strip's New and History
|
|
253
|
+
* actions. The persistent assistant uses this for its panel Close button.
|
|
254
|
+
*/
|
|
255
|
+
threadBarActions?: ReactNode;
|
|
256
|
+
/** Whether the top tab row renders its built-in New/History controls. */
|
|
257
|
+
showThreadBarActions?: boolean;
|
|
258
|
+
/** Replacement icon for the top tab row's New action. */
|
|
259
|
+
threadNewIcon?: ReactNode;
|
|
260
|
+
/** Replacement icon for the top tab row's History action. */
|
|
261
|
+
threadHistoryIcon?: ReactNode;
|
|
249
262
|
/** Id of the currently-active thread, highlighted in the sidebar. */
|
|
250
263
|
activeThreadId?: string;
|
|
251
264
|
/**
|
|
@@ -340,9 +353,8 @@ export type ApprovalDecision =
|
|
|
340
353
|
toolName: string;
|
|
341
354
|
toolCallId: string;
|
|
342
355
|
/**
|
|
343
|
-
* Mastra run id
|
|
344
|
-
*
|
|
345
|
-
* reconstructed from history (no live runId available).
|
|
356
|
+
* Mastra run id required to resume the suspended workflow. Native
|
|
357
|
+
* suspended-run discovery restores it for persisted approvals.
|
|
346
358
|
*/
|
|
347
359
|
runId?: string;
|
|
348
360
|
input: unknown;
|
|
@@ -359,10 +371,8 @@ export type ApprovalDecision =
|
|
|
359
371
|
/**
|
|
360
372
|
* One approval-gated tool call paused mid-turn. `runId` is the
|
|
361
373
|
* Mastra workflow id needed to resume; it's always present when the
|
|
362
|
-
* card
|
|
363
|
-
*
|
|
364
|
-
* and absent only for approvals reconstructed from a history load
|
|
365
|
-
* where the original runId is lost.
|
|
374
|
+
* card came from a live approval part or native suspended-run storage.
|
|
375
|
+
* It remains optional for controlled transports that cannot provide it.
|
|
366
376
|
*/
|
|
367
377
|
export type PendingApproval = {
|
|
368
378
|
toolName: string;
|