@agents24/chat-react 0.5.0 → 0.5.2
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 +90 -21
- package/compatibility.json +10 -10
- package/dist/chunk-SSUO4EPD.js +189 -0
- package/dist/chunk-SSUO4EPD.js.map +1 -0
- package/dist/chunk-ZCNHLFRB.js +108 -0
- package/dist/chunk-ZCNHLFRB.js.map +1 -0
- package/dist/index.cjs +112 -95
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +362 -68
- package/dist/index.js.map +1 -1
- package/dist/mcp-oauth.d.ts +13 -0
- package/dist/runtime/index.cjs +594 -0
- package/dist/runtime/index.cjs.map +1 -0
- package/dist/runtime/index.d.ts +2 -0
- package/dist/runtime/index.js +458 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/input-contract.d.ts +20 -0
- package/dist/runtime/use-agent-chat-runtime.d.ts +83 -0
- package/dist/scaffold/app.d.ts +30 -0
- package/dist/scaffold/components/chat/chat-composer.d.ts +21 -0
- package/dist/scaffold/components/chat/chat-message.d.ts +21 -0
- package/dist/scaffold/components/chat/chat-shell.d.ts +8 -0
- package/dist/scaffold/components/chat/collapsible-user-message.d.ts +3 -0
- package/dist/scaffold/components/chat/external-link-dialog.d.ts +2 -0
- package/dist/scaffold/components/chat/identity-control.d.ts +4 -0
- package/dist/scaffold/components/chat/message-parts.d.ts +12 -0
- package/dist/scaffold/components/chat/theme-menu.d.ts +1 -0
- package/dist/scaffold/components/chat/thread-sidebar.d.ts +16 -0
- package/dist/scaffold/components/chat/timeline-loading.d.ts +7 -0
- package/dist/scaffold/components/chat/turn-outline.d.ts +4 -0
- package/dist/scaffold/components/theme-provider.d.ts +20 -0
- package/dist/scaffold/components/ui/attachment.d.ts +24 -0
- package/dist/scaffold/components/ui/bubble.d.ts +17 -0
- package/dist/scaffold/components/ui/button-group.d.ts +11 -0
- package/dist/scaffold/components/ui/button.d.ts +10 -0
- package/dist/scaffold/components/ui/dialog.d.ts +17 -0
- package/dist/scaffold/components/ui/dropdown-menu.d.ts +29 -0
- package/dist/scaffold/components/ui/input.d.ts +3 -0
- package/dist/scaffold/components/ui/marker.d.ts +11 -0
- package/dist/scaffold/components/ui/message-scroller.d.ts +10 -0
- package/dist/scaffold/components/ui/message.d.ts +10 -0
- package/dist/scaffold/components/ui/popover.d.ts +6 -0
- package/dist/scaffold/components/ui/select.d.ts +8 -0
- package/dist/scaffold/components/ui/separator.d.ts +4 -0
- package/dist/scaffold/components/ui/sheet.d.ts +14 -0
- package/dist/scaffold/components/ui/sidebar.d.ts +69 -0
- package/dist/scaffold/components/ui/skeleton.d.ts +2 -0
- package/dist/scaffold/components/ui/textarea.d.ts +3 -0
- package/dist/scaffold/components/ui/tooltip.d.ts +15 -0
- package/dist/scaffold/hooks/use-mobile.d.ts +1 -0
- package/dist/scaffold/identity.d.ts +10 -0
- package/dist/scaffold/index.cjs +2270 -0
- package/dist/scaffold/index.cjs.map +1 -0
- package/dist/scaffold/index.d.ts +14 -0
- package/dist/scaffold/index.js +2244 -0
- package/dist/scaffold/index.js.map +1 -0
- package/dist/scaffold/lib/utils.d.ts +2 -0
- package/dist/styles.css +137 -378
- package/dist/types.d.ts +3 -13
- package/dist/ui/adapters.d.ts +1 -1
- package/dist/ui/agent-chat-actions.d.ts +12 -5
- package/dist/ui/agent-chat-composer.d.ts +2 -1
- package/dist/ui/agent-chat-context-compression-row.d.ts +5 -0
- package/dist/ui/agent-chat-context-status.d.ts +13 -0
- package/dist/ui/agent-chat-message.d.ts +7 -5
- package/dist/ui/agent-response-timeline.d.ts +29 -7
- package/dist/ui/ask-user-interaction-state.d.ts +20 -0
- package/dist/ui/ask-user-interaction.d.ts +8 -0
- package/dist/ui/index.cjs +1391 -271
- package/dist/ui/index.cjs.map +1 -1
- package/dist/ui/index.d.ts +5 -0
- package/dist/ui/index.js +2380 -65
- package/dist/ui/index.js.map +1 -1
- package/dist/ui/tool-sessions.d.ts +70 -0
- package/dist/ui/use-audio-recorder.d.ts +8 -0
- package/package.json +27 -12
- package/scaffold/components.json +25 -0
- package/scaffold/manifest.json +28 -0
- package/scaffold/src/app.tsx +71 -0
- package/scaffold/src/chat.css +54 -0
- package/scaffold/src/components/chat/chat-composer.tsx +237 -0
- package/scaffold/src/components/chat/chat-message.tsx +76 -0
- package/scaffold/src/components/chat/chat-shell.tsx +257 -0
- package/scaffold/src/components/chat/collapsible-user-message.tsx +37 -0
- package/scaffold/src/components/chat/external-link-dialog.tsx +48 -0
- package/scaffold/src/components/chat/identity-control.tsx +69 -0
- package/scaffold/src/components/chat/message-parts.tsx +83 -0
- package/scaffold/src/components/chat/theme-menu.tsx +79 -0
- package/scaffold/src/components/chat/thread-sidebar.tsx +114 -0
- package/scaffold/src/components/chat/timeline-loading.ts +17 -0
- package/scaffold/src/components/chat/turn-outline.tsx +73 -0
- package/scaffold/src/components/theme-provider.tsx +91 -0
- package/scaffold/src/components/ui/attachment.tsx +204 -0
- package/scaffold/src/components/ui/bubble.tsx +125 -0
- package/scaffold/src/components/ui/button-group.tsx +83 -0
- package/scaffold/src/components/ui/button.tsx +65 -0
- package/scaffold/src/components/ui/dialog.tsx +171 -0
- package/scaffold/src/components/ui/dropdown-menu.tsx +271 -0
- package/scaffold/src/components/ui/input.tsx +19 -0
- package/scaffold/src/components/ui/marker.tsx +69 -0
- package/scaffold/src/components/ui/message-scroller.tsx +129 -0
- package/scaffold/src/components/ui/message.tsx +92 -0
- package/scaffold/src/components/ui/popover.tsx +40 -0
- package/scaffold/src/components/ui/select.tsx +92 -0
- package/scaffold/src/components/ui/separator.tsx +28 -0
- package/scaffold/src/components/ui/sheet.tsx +150 -0
- package/scaffold/src/components/ui/sidebar.tsx +677 -0
- package/scaffold/src/components/ui/skeleton.tsx +13 -0
- package/scaffold/src/components/ui/textarea.tsx +18 -0
- package/scaffold/src/components/ui/tooltip.tsx +56 -0
- package/scaffold/src/hooks/use-mobile.ts +24 -0
- package/scaffold/src/identity.ts +17 -0
- package/scaffold/src/index.css +222 -0
- package/scaffold/src/index.tsx +39 -0
- package/scaffold/src/lib/utils.ts +6 -0
- package/scaffold/styles.css +3 -0
- package/dist/chunk-CHAU7FNW.js +0 -140
- package/dist/chunk-CHAU7FNW.js.map +0 -1
- package/dist/chunk-LI67M74F.js +0 -445
- package/dist/chunk-LI67M74F.js.map +0 -1
- package/dist/chunk-R7WFL3YR.js +0 -1327
- package/dist/chunk-R7WFL3YR.js.map +0 -1
- package/dist/renderers.d.ts +0 -10
- package/dist/templates/agent-chat-app.d.ts +0 -18
- package/dist/templates/agent-chat-layout.d.ts +0 -77
- package/dist/templates/agent-chat-shell.d.ts +0 -37
- package/dist/templates/appearance-control.d.ts +0 -5
- package/dist/templates/index.cjs +0 -2600
- package/dist/templates/index.cjs.map +0 -1
- package/dist/templates/index.d.ts +0 -4
- package/dist/templates/index.js +0 -846
- package/dist/templates/index.js.map +0 -1
package/dist/ui/index.js
CHANGED
|
@@ -1,48 +1,2283 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
|
|
2
|
+
getActiveStreamingTextPartId,
|
|
3
|
+
useStreamingText
|
|
4
|
+
} from "../chunk-SSUO4EPD.js";
|
|
5
|
+
|
|
6
|
+
// src/ui/agent-chat-composer.tsx
|
|
7
|
+
import * as React from "react";
|
|
8
|
+
import { ArrowUp, Paperclip, Plus, Square, X } from "lucide-react";
|
|
9
|
+
|
|
10
|
+
// src/ui/utils.ts
|
|
11
|
+
import { clsx } from "clsx";
|
|
12
|
+
import { twMerge } from "tailwind-merge";
|
|
13
|
+
function cn(...values) {
|
|
14
|
+
return twMerge(clsx(values));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// src/ui/agent-chat-composer.tsx
|
|
18
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
19
|
+
var createId = () => typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : `file-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
20
|
+
function createComposerFile(file) {
|
|
21
|
+
return {
|
|
22
|
+
id: createId(),
|
|
23
|
+
type: "file",
|
|
24
|
+
url: URL.createObjectURL(file),
|
|
25
|
+
filename: file.name || "attachment",
|
|
26
|
+
mediaType: file.type || "application/octet-stream",
|
|
27
|
+
source: file
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function revokeComposerFiles(files) {
|
|
31
|
+
files.forEach((file) => {
|
|
32
|
+
try {
|
|
33
|
+
URL.revokeObjectURL(file.url);
|
|
34
|
+
} catch {
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
function formatFileCount(files) {
|
|
39
|
+
if (files.length === 1) return files[0].filename;
|
|
40
|
+
return `${files.length} files`;
|
|
41
|
+
}
|
|
42
|
+
var pillButtonClass = "inline-flex size-8 shrink-0 items-center justify-center gap-2 rounded-lg border-0 bg-muted text-sm text-muted-foreground shadow-none transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-0 disabled:pointer-events-none disabled:opacity-50";
|
|
43
|
+
var submitButtonClass = "inline-flex size-9 shrink-0 items-center justify-center gap-2 rounded-lg border-0 bg-primary text-sm text-primary-foreground shadow-none transition-[background-color,color,opacity,transform] duration-200 hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-0 disabled:pointer-events-none disabled:opacity-50";
|
|
44
|
+
function AgentChatComposer({
|
|
45
|
+
accept,
|
|
46
|
+
allowAttachments = true,
|
|
47
|
+
attachmentLayout = "flow",
|
|
48
|
+
className,
|
|
49
|
+
disabled = false,
|
|
50
|
+
forceExpanded = false,
|
|
51
|
+
inputToolbarContent,
|
|
52
|
+
isRunning = false,
|
|
53
|
+
onAttachmentCountChange,
|
|
54
|
+
onStop,
|
|
55
|
+
onSubmit,
|
|
56
|
+
placeholder = "Message the agent",
|
|
57
|
+
textareaRef
|
|
58
|
+
}) {
|
|
59
|
+
const fileInputRef = React.useRef(null);
|
|
60
|
+
const filesRef = React.useRef([]);
|
|
61
|
+
const localTextareaRef = React.useRef(null);
|
|
62
|
+
const [text, setText] = React.useState("");
|
|
63
|
+
const [files, setFiles] = React.useState([]);
|
|
64
|
+
const [isSubmitting, setIsSubmitting] = React.useState(false);
|
|
65
|
+
const isExpanded = forceExpanded || text.includes("\n") || text.length > 62;
|
|
66
|
+
const setTextareaRef = React.useCallback(
|
|
67
|
+
(node) => {
|
|
68
|
+
localTextareaRef.current = node;
|
|
69
|
+
if (typeof textareaRef === "function") {
|
|
70
|
+
textareaRef(node);
|
|
71
|
+
} else if (textareaRef) {
|
|
72
|
+
textareaRef.current = node;
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
[textareaRef]
|
|
76
|
+
);
|
|
77
|
+
React.useEffect(() => {
|
|
78
|
+
filesRef.current = files;
|
|
79
|
+
onAttachmentCountChange?.(files.length);
|
|
80
|
+
}, [files, onAttachmentCountChange]);
|
|
81
|
+
React.useEffect(() => () => revokeComposerFiles(filesRef.current), []);
|
|
82
|
+
React.useLayoutEffect(() => {
|
|
83
|
+
const textarea = localTextareaRef.current;
|
|
84
|
+
if (!textarea) return;
|
|
85
|
+
if (isExpanded) {
|
|
86
|
+
textarea.style.height = "auto";
|
|
87
|
+
const scrollHeight = textarea.scrollHeight;
|
|
88
|
+
textarea.style.height = `${Math.min(scrollHeight, 224)}px`;
|
|
89
|
+
textarea.style.overflowY = scrollHeight >= 224 ? "auto" : "hidden";
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
textarea.style.height = "";
|
|
93
|
+
textarea.style.overflowY = "hidden";
|
|
94
|
+
}, [isExpanded, text]);
|
|
95
|
+
const canSubmit = !disabled && !isSubmitting && !isRunning && (text.trim().length > 0 || files.length > 0);
|
|
96
|
+
const handleFilesChange = (event) => {
|
|
97
|
+
const selected = Array.from(event.target.files || []).map(createComposerFile);
|
|
98
|
+
if (selected.length > 0) {
|
|
99
|
+
setFiles((current) => [...current, ...selected]);
|
|
100
|
+
}
|
|
101
|
+
event.currentTarget.value = "";
|
|
102
|
+
};
|
|
103
|
+
const removeFile = (fileId) => {
|
|
104
|
+
setFiles((current) => {
|
|
105
|
+
const target = current.find((file) => file.id === fileId);
|
|
106
|
+
if (target) revokeComposerFiles([target]);
|
|
107
|
+
return current.filter((file) => file.id !== fileId);
|
|
108
|
+
});
|
|
109
|
+
};
|
|
110
|
+
const submit = async () => {
|
|
111
|
+
if (!canSubmit) return;
|
|
112
|
+
const submittedText = text.trim();
|
|
113
|
+
const submittedFiles = files;
|
|
114
|
+
setIsSubmitting(true);
|
|
115
|
+
try {
|
|
116
|
+
await onSubmit({ text: submittedText, files: submittedFiles });
|
|
117
|
+
setText("");
|
|
118
|
+
setFiles([]);
|
|
119
|
+
revokeComposerFiles(submittedFiles);
|
|
120
|
+
} catch {
|
|
121
|
+
} finally {
|
|
122
|
+
setIsSubmitting(false);
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
const handleKeyDown = (event) => {
|
|
126
|
+
if (event.key === "Enter" && !event.shiftKey) {
|
|
127
|
+
event.preventDefault();
|
|
128
|
+
void submit();
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("a24-composer relative mx-auto w-full max-w-3xl overflow-visible", className), "data-agents24-chat-composer": "", children: [
|
|
132
|
+
files.length > 0 ? /* @__PURE__ */ jsx(
|
|
133
|
+
"div",
|
|
134
|
+
{
|
|
135
|
+
"data-testid": "agent-chat-composer-attachments",
|
|
136
|
+
"data-layout": attachmentLayout,
|
|
137
|
+
className: cn(
|
|
138
|
+
"a24-composer__attachments flex min-w-0 gap-2 py-1",
|
|
139
|
+
attachmentLayout === "overlay" ? "absolute bottom-full left-0 right-0 mb-1.5 flex-nowrap overflow-x-auto overflow-y-hidden overscroll-x-contain px-2" : "mb-2 flex-wrap px-1"
|
|
140
|
+
),
|
|
141
|
+
children: files.map((file) => /* @__PURE__ */ jsxs(
|
|
142
|
+
"div",
|
|
143
|
+
{
|
|
144
|
+
className: "a24-composer__attachment group relative flex h-8 max-w-[min(20rem,calc(100vw-2rem))] cursor-default select-none items-center gap-1.5 rounded-md border border-border bg-card px-1.5 text-sm font-medium text-card-foreground transition-colors hover:bg-accent",
|
|
145
|
+
children: [
|
|
146
|
+
/* @__PURE__ */ jsx("span", { className: "flex size-5 shrink-0 items-center justify-center rounded bg-background text-muted-foreground", children: /* @__PURE__ */ jsx(Paperclip, { className: "size-3" }) }),
|
|
147
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate", children: file.filename }),
|
|
148
|
+
/* @__PURE__ */ jsx(
|
|
149
|
+
"button",
|
|
150
|
+
{
|
|
151
|
+
"aria-label": `Remove ${file.filename}`,
|
|
152
|
+
className: "a24-composer__attachment-remove inline-flex size-5 shrink-0 items-center justify-center rounded text-muted-foreground opacity-70 transition-colors hover:bg-muted hover:text-foreground hover:opacity-100 focus-visible:outline-none",
|
|
153
|
+
onClick: () => removeFile(file.id),
|
|
154
|
+
type: "button",
|
|
155
|
+
children: /* @__PURE__ */ jsx(X, { className: "size-3" })
|
|
156
|
+
}
|
|
157
|
+
)
|
|
158
|
+
]
|
|
159
|
+
},
|
|
160
|
+
file.id
|
|
161
|
+
))
|
|
162
|
+
}
|
|
163
|
+
) : null,
|
|
164
|
+
/* @__PURE__ */ jsx(
|
|
165
|
+
"div",
|
|
166
|
+
{
|
|
167
|
+
"data-expanded": isExpanded ? "" : void 0,
|
|
168
|
+
className: cn(
|
|
169
|
+
"a24-composer__surface relative w-full border border-input bg-background shadow-sm",
|
|
170
|
+
isExpanded ? "rounded-xl p-2 pb-1.5" : "rounded-xl px-2 py-[3px]"
|
|
171
|
+
),
|
|
172
|
+
children: /* @__PURE__ */ jsxs(
|
|
173
|
+
"div",
|
|
174
|
+
{
|
|
175
|
+
className: "a24-composer__grid w-full",
|
|
176
|
+
style: {
|
|
177
|
+
alignItems: "center",
|
|
178
|
+
display: "grid",
|
|
179
|
+
gridTemplateAreas: isExpanded ? `"textarea textarea textarea" "plus toolbar submit"` : `"plus textarea composer mic submit"`,
|
|
180
|
+
gridTemplateColumns: isExpanded ? "auto 1fr auto" : "auto 1fr auto auto auto",
|
|
181
|
+
gap: isExpanded ? "3px 10px" : "8px"
|
|
182
|
+
},
|
|
183
|
+
children: [
|
|
184
|
+
/* @__PURE__ */ jsxs("div", { style: { gridArea: "plus" }, className: "a24-composer__attach-slot flex shrink-0 items-center justify-center", children: [
|
|
185
|
+
/* @__PURE__ */ jsx(
|
|
186
|
+
"input",
|
|
187
|
+
{
|
|
188
|
+
ref: fileInputRef,
|
|
189
|
+
accept,
|
|
190
|
+
"aria-label": "Attach files",
|
|
191
|
+
className: "hidden",
|
|
192
|
+
multiple: true,
|
|
193
|
+
name: "attachments",
|
|
194
|
+
onChange: handleFilesChange,
|
|
195
|
+
type: "file"
|
|
196
|
+
}
|
|
197
|
+
),
|
|
198
|
+
/* @__PURE__ */ jsx(
|
|
199
|
+
"button",
|
|
200
|
+
{
|
|
201
|
+
"aria-label": "Attach files",
|
|
202
|
+
className: cn("a24-composer__attach", pillButtonClass),
|
|
203
|
+
disabled: disabled || isSubmitting || isRunning || !allowAttachments,
|
|
204
|
+
onClick: () => fileInputRef.current?.click(),
|
|
205
|
+
title: "Attach files",
|
|
206
|
+
type: "button",
|
|
207
|
+
children: /* @__PURE__ */ jsx(Plus, { className: "size-4" })
|
|
208
|
+
}
|
|
209
|
+
)
|
|
210
|
+
] }),
|
|
211
|
+
/* @__PURE__ */ jsx("div", { style: { gridArea: "textarea" }, className: "a24-composer__textarea-slot min-w-0 w-full", children: /* @__PURE__ */ jsx(
|
|
212
|
+
"textarea",
|
|
213
|
+
{
|
|
214
|
+
ref: setTextareaRef,
|
|
215
|
+
"aria-label": "Message",
|
|
216
|
+
autoComplete: "off",
|
|
217
|
+
className: cn(
|
|
218
|
+
"a24-composer__textarea w-full resize-none border-0 bg-transparent px-1 text-[15px] leading-relaxed text-foreground shadow-none outline-none placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-0",
|
|
219
|
+
isExpanded ? "min-h-9 max-h-[224px] py-1" : "min-h-9 h-9 max-h-9 py-2"
|
|
220
|
+
),
|
|
221
|
+
disabled: disabled || isSubmitting || isRunning,
|
|
222
|
+
name: "message",
|
|
223
|
+
onChange: (event) => setText(event.target.value),
|
|
224
|
+
onKeyDown: handleKeyDown,
|
|
225
|
+
placeholder,
|
|
226
|
+
rows: isExpanded ? 2 : 1,
|
|
227
|
+
value: text
|
|
228
|
+
}
|
|
229
|
+
) }),
|
|
230
|
+
isExpanded ? /* @__PURE__ */ jsx("div", { style: { gridArea: "toolbar" }, className: "a24-composer__toolbar flex min-w-0 items-center gap-1.5", children: inputToolbarContent }) : null,
|
|
231
|
+
!isExpanded ? /* @__PURE__ */ jsx("div", { style: { gridArea: "composer" }, className: "flex min-w-0 items-center gap-1.5" }) : null,
|
|
232
|
+
!isExpanded ? /* @__PURE__ */ jsx("div", { style: { gridArea: "mic" }, className: "flex shrink-0 items-center justify-end" }) : null,
|
|
233
|
+
/* @__PURE__ */ jsx("div", { style: { gridArea: "submit" }, className: "a24-composer__submit-slot flex shrink-0 items-center justify-end", children: isRunning ? /* @__PURE__ */ jsx(
|
|
234
|
+
"button",
|
|
235
|
+
{
|
|
236
|
+
"aria-label": "Stop generating",
|
|
237
|
+
className: cn("a24-composer__submit", submitButtonClass),
|
|
238
|
+
disabled: disabled || !onStop,
|
|
239
|
+
onClick: onStop,
|
|
240
|
+
title: "Stop",
|
|
241
|
+
type: "button",
|
|
242
|
+
children: /* @__PURE__ */ jsx(Square, { className: "size-3.5 fill-current" })
|
|
243
|
+
}
|
|
244
|
+
) : /* @__PURE__ */ jsx(
|
|
245
|
+
"button",
|
|
246
|
+
{
|
|
247
|
+
"aria-label": files.length > 0 ? `Send ${formatFileCount(files)}` : "Send message",
|
|
248
|
+
className: cn("a24-composer__submit", submitButtonClass, text.trim().length > 0 && "hover:scale-105"),
|
|
249
|
+
disabled: !canSubmit,
|
|
250
|
+
onClick: () => void submit(),
|
|
251
|
+
title: "Send",
|
|
252
|
+
type: "button",
|
|
253
|
+
children: /* @__PURE__ */ jsx(ArrowUp, { className: "size-4", strokeWidth: 2.5 })
|
|
254
|
+
}
|
|
255
|
+
) })
|
|
256
|
+
]
|
|
257
|
+
}
|
|
258
|
+
)
|
|
259
|
+
}
|
|
260
|
+
),
|
|
261
|
+
!isExpanded && inputToolbarContent ? /* @__PURE__ */ jsx("div", { className: "a24-composer__toolbar a24-composer__toolbar--external mt-2 flex items-center gap-2 px-1", children: inputToolbarContent }) : null
|
|
262
|
+
] });
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// src/ui/agent-chat-actions.tsx
|
|
266
|
+
import * as React3 from "react";
|
|
267
|
+
import { Check, Copy, RefreshCcw, ThumbsDown, ThumbsUp } from "lucide-react";
|
|
268
|
+
import { Popover } from "radix-ui";
|
|
269
|
+
|
|
270
|
+
// src/ui/adapters.ts
|
|
271
|
+
import * as React2 from "react";
|
|
272
|
+
|
|
273
|
+
// src/ui/attachment.tsx
|
|
274
|
+
import { cva } from "class-variance-authority";
|
|
275
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
276
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
277
|
+
var attachmentVariants = cva(
|
|
278
|
+
"group/attachment relative flex w-fit max-w-full min-w-0 shrink-0 flex-wrap rounded-xl border bg-card text-card-foreground transition-colors focus-within:border-neutral-300 focus-within:ring-0 has-[>a,>button]:hover:bg-muted/50 data-[state=error]:border-destructive/30 data-[state=idle]:border-dashed",
|
|
279
|
+
{
|
|
280
|
+
variants: {
|
|
281
|
+
size: {
|
|
282
|
+
default: "gap-2 text-sm has-data-[slot=attachment-content]:px-2.5 has-data-[slot=attachment-content]:py-2 has-data-[slot=attachment-media]:p-2",
|
|
283
|
+
sm: "gap-2.5 text-xs has-data-[slot=attachment-content]:px-2 has-data-[slot=attachment-content]:py-1.5 has-data-[slot=attachment-media]:p-1.5",
|
|
284
|
+
xs: "gap-1.5 rounded-lg text-xs has-data-[slot=attachment-content]:px-1.5 has-data-[slot=attachment-content]:py-1 has-data-[slot=attachment-media]:p-1"
|
|
285
|
+
},
|
|
286
|
+
orientation: {
|
|
287
|
+
horizontal: "min-w-40 items-center",
|
|
288
|
+
vertical: "w-24 flex-col has-data-[slot=attachment-content]:w-30"
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
);
|
|
293
|
+
function Attachment({
|
|
294
|
+
className,
|
|
295
|
+
state = "done",
|
|
296
|
+
size = "default",
|
|
297
|
+
orientation = "horizontal",
|
|
298
|
+
...props
|
|
299
|
+
}) {
|
|
300
|
+
return /* @__PURE__ */ jsx2(
|
|
301
|
+
"div",
|
|
302
|
+
{
|
|
303
|
+
"data-slot": "attachment",
|
|
304
|
+
"data-state": state,
|
|
305
|
+
"data-size": size,
|
|
306
|
+
"data-orientation": orientation,
|
|
307
|
+
className: cn(attachmentVariants({ size, orientation }), className),
|
|
308
|
+
...props
|
|
309
|
+
}
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
var attachmentMediaVariants = cva(
|
|
313
|
+
"relative flex aspect-square w-10 shrink-0 items-center justify-center overflow-hidden rounded-lg bg-muted text-foreground group-data-[orientation=vertical]/attachment:w-full group-data-[size=sm]/attachment:w-8 group-data-[size=xs]/attachment:w-7 group-data-[size=xs]/attachment:rounded-md group-data-[state=error]/attachment:bg-destructive/10 group-data-[state=error]/attachment:text-destructive group-data-[orientation=vertical]/attachment:*:data-[slot=spinner]:size-6! [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 group-data-[orientation=vertical]/attachment:[&_svg:not([class*='size-'])]:size-6 group-data-[size=xs]/attachment:[&_svg:not([class*='size-'])]:size-3.5",
|
|
314
|
+
{
|
|
315
|
+
variants: {
|
|
316
|
+
variant: {
|
|
317
|
+
icon: "",
|
|
318
|
+
image: "opacity-60 group-data-[state=done]/attachment:opacity-100 group-data-[state=idle]/attachment:opacity-100 *:[img]:aspect-square *:[img]:w-full *:[img]:object-cover"
|
|
319
|
+
}
|
|
320
|
+
},
|
|
321
|
+
defaultVariants: {
|
|
322
|
+
variant: "icon"
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
);
|
|
326
|
+
function AttachmentMedia({
|
|
327
|
+
className,
|
|
328
|
+
variant = "icon",
|
|
329
|
+
...props
|
|
330
|
+
}) {
|
|
331
|
+
return /* @__PURE__ */ jsx2(
|
|
332
|
+
"div",
|
|
333
|
+
{
|
|
334
|
+
"data-slot": "attachment-media",
|
|
335
|
+
"data-variant": variant,
|
|
336
|
+
className: cn(attachmentMediaVariants({ variant }), className),
|
|
337
|
+
...props
|
|
338
|
+
}
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
function AttachmentContent({
|
|
342
|
+
className,
|
|
343
|
+
...props
|
|
344
|
+
}) {
|
|
345
|
+
return /* @__PURE__ */ jsx2(
|
|
346
|
+
"div",
|
|
347
|
+
{
|
|
348
|
+
"data-slot": "attachment-content",
|
|
349
|
+
className: cn(
|
|
350
|
+
"max-w-full min-w-0 flex-1 leading-tight group-data-[orientation=vertical]/attachment:px-1",
|
|
351
|
+
className
|
|
352
|
+
),
|
|
353
|
+
...props
|
|
354
|
+
}
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
function AttachmentTitle({
|
|
358
|
+
className,
|
|
359
|
+
...props
|
|
360
|
+
}) {
|
|
361
|
+
return /* @__PURE__ */ jsx2(
|
|
362
|
+
"span",
|
|
363
|
+
{
|
|
364
|
+
"data-slot": "attachment-title",
|
|
365
|
+
className: cn(
|
|
366
|
+
"block max-w-full min-w-0 truncate font-medium group-data-[state=processing]/attachment:shimmer group-data-[state=uploading]/attachment:shimmer",
|
|
367
|
+
className
|
|
368
|
+
),
|
|
369
|
+
...props
|
|
370
|
+
}
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
function AttachmentDescription({
|
|
374
|
+
className,
|
|
375
|
+
...props
|
|
376
|
+
}) {
|
|
377
|
+
return /* @__PURE__ */ jsx2(
|
|
378
|
+
"span",
|
|
379
|
+
{
|
|
380
|
+
"data-slot": "attachment-description",
|
|
381
|
+
className: cn(
|
|
382
|
+
"mt-0.5 block min-w-0 truncate text-xs text-muted-foreground group-data-[state=error]/attachment:text-destructive/80",
|
|
383
|
+
"max-w-full",
|
|
384
|
+
className
|
|
385
|
+
),
|
|
386
|
+
...props
|
|
387
|
+
}
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
function AttachmentActions({
|
|
391
|
+
className,
|
|
392
|
+
...props
|
|
393
|
+
}) {
|
|
394
|
+
return /* @__PURE__ */ jsx2(
|
|
395
|
+
"div",
|
|
396
|
+
{
|
|
397
|
+
"data-slot": "attachment-actions",
|
|
398
|
+
className: cn(
|
|
399
|
+
"relative z-20 flex shrink-0 items-center group-data-[orientation=vertical]/attachment:absolute group-data-[orientation=vertical]/attachment:top-3 group-data-[orientation=vertical]/attachment:right-3 group-data-[orientation=vertical]/attachment:gap-1",
|
|
400
|
+
className
|
|
401
|
+
),
|
|
402
|
+
...props
|
|
403
|
+
}
|
|
404
|
+
);
|
|
405
|
+
}
|
|
406
|
+
function AttachmentAction({
|
|
407
|
+
className,
|
|
408
|
+
type = "button",
|
|
409
|
+
...props
|
|
410
|
+
}) {
|
|
411
|
+
return /* @__PURE__ */ jsx2(
|
|
412
|
+
"button",
|
|
413
|
+
{
|
|
414
|
+
"data-slot": "attachment-action",
|
|
415
|
+
type,
|
|
416
|
+
className: cn(
|
|
417
|
+
"inline-flex size-6 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:border-neutral-300 focus-visible:outline-none focus-visible:ring-0 disabled:pointer-events-none disabled:opacity-50",
|
|
418
|
+
className
|
|
419
|
+
),
|
|
420
|
+
...props
|
|
421
|
+
}
|
|
422
|
+
);
|
|
423
|
+
}
|
|
424
|
+
function AttachmentTrigger({
|
|
425
|
+
className,
|
|
426
|
+
asChild = false,
|
|
427
|
+
type,
|
|
428
|
+
...props
|
|
429
|
+
}) {
|
|
430
|
+
const Comp = asChild ? Slot : "button";
|
|
431
|
+
return /* @__PURE__ */ jsx2(
|
|
432
|
+
Comp,
|
|
433
|
+
{
|
|
434
|
+
"data-slot": "attachment-trigger",
|
|
435
|
+
type: asChild ? void 0 : type ?? "button",
|
|
436
|
+
className: cn("absolute inset-0 z-10 outline-none", className),
|
|
437
|
+
...props
|
|
438
|
+
}
|
|
439
|
+
);
|
|
440
|
+
}
|
|
441
|
+
function AttachmentGroup({ className, ...props }) {
|
|
442
|
+
return /* @__PURE__ */ jsx2(
|
|
443
|
+
"div",
|
|
444
|
+
{
|
|
445
|
+
"data-slot": "attachment-group",
|
|
446
|
+
className: cn(
|
|
447
|
+
"flex min-w-0 scroll-fade-x snap-x snap-mandatory scroll-px-1 scrollbar-none gap-3 overflow-x-auto overscroll-x-contain py-1 *:data-[slot=attachment]:flex-none *:data-[slot=attachment]:snap-start",
|
|
448
|
+
className
|
|
449
|
+
),
|
|
450
|
+
...props
|
|
451
|
+
}
|
|
452
|
+
);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// src/ui/message.tsx
|
|
456
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
457
|
+
function MessageGroup({ className, ...props }) {
|
|
458
|
+
return /* @__PURE__ */ jsx3(
|
|
459
|
+
"div",
|
|
460
|
+
{
|
|
461
|
+
"data-slot": "message-group",
|
|
462
|
+
className: cn("flex min-w-0 flex-col gap-2", className),
|
|
463
|
+
...props
|
|
464
|
+
}
|
|
465
|
+
);
|
|
466
|
+
}
|
|
467
|
+
function Message({
|
|
468
|
+
className,
|
|
469
|
+
align = "start",
|
|
470
|
+
...props
|
|
471
|
+
}) {
|
|
472
|
+
return /* @__PURE__ */ jsx3(
|
|
473
|
+
"div",
|
|
474
|
+
{
|
|
475
|
+
"data-slot": "message",
|
|
476
|
+
"data-align": align,
|
|
477
|
+
className: cn(
|
|
478
|
+
"group/message relative flex w-full min-w-0 gap-2 text-sm data-[align=end]:flex-row-reverse",
|
|
479
|
+
className
|
|
480
|
+
),
|
|
481
|
+
...props
|
|
482
|
+
}
|
|
483
|
+
);
|
|
484
|
+
}
|
|
485
|
+
function MessageAvatar({ className, ...props }) {
|
|
486
|
+
return /* @__PURE__ */ jsx3(
|
|
487
|
+
"div",
|
|
488
|
+
{
|
|
489
|
+
"data-slot": "message-avatar",
|
|
490
|
+
className: cn(
|
|
491
|
+
"flex w-fit min-w-8 shrink-0 items-center justify-center self-end overflow-hidden rounded-full bg-muted group-has-data-[slot=message-footer]/message:-translate-y-8",
|
|
492
|
+
className
|
|
493
|
+
),
|
|
494
|
+
...props
|
|
495
|
+
}
|
|
496
|
+
);
|
|
497
|
+
}
|
|
498
|
+
function MessageContent({ className, ...props }) {
|
|
499
|
+
return /* @__PURE__ */ jsx3(
|
|
500
|
+
"div",
|
|
501
|
+
{
|
|
502
|
+
"data-slot": "message-content",
|
|
503
|
+
className: cn(
|
|
504
|
+
"flex w-full min-w-0 flex-col gap-2.5 wrap-break-word group-data-[align=end]/message:*:data-slot:self-end",
|
|
505
|
+
className
|
|
506
|
+
),
|
|
507
|
+
...props
|
|
508
|
+
}
|
|
509
|
+
);
|
|
510
|
+
}
|
|
511
|
+
function MessageHeader({ className, ...props }) {
|
|
512
|
+
return /* @__PURE__ */ jsx3(
|
|
513
|
+
"div",
|
|
514
|
+
{
|
|
515
|
+
"data-slot": "message-header",
|
|
516
|
+
className: cn(
|
|
517
|
+
"flex max-w-full min-w-0 items-center px-3 text-xs font-medium text-muted-foreground group-has-data-[variant=ghost]/message:px-0",
|
|
518
|
+
className
|
|
519
|
+
),
|
|
520
|
+
...props
|
|
521
|
+
}
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
function MessageFooter({ className, ...props }) {
|
|
525
|
+
return /* @__PURE__ */ jsx3(
|
|
526
|
+
"div",
|
|
527
|
+
{
|
|
528
|
+
"data-slot": "message-footer",
|
|
529
|
+
className: cn(
|
|
530
|
+
"flex max-w-full min-w-0 items-center px-3 text-xs font-medium text-muted-foreground group-has-data-[variant=ghost]/message:px-0 group-data-[align=end]/message:justify-end",
|
|
531
|
+
className
|
|
532
|
+
),
|
|
533
|
+
...props
|
|
534
|
+
}
|
|
535
|
+
);
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// src/ui/adapters.ts
|
|
539
|
+
var ChatMessageRoleContext = React2.createContext("assistant");
|
|
540
|
+
function ChatMessage(input) {
|
|
541
|
+
const { className, from, align, ...props } = input;
|
|
542
|
+
const isUser = from === "user";
|
|
543
|
+
const isRtl = props.dir === "rtl";
|
|
544
|
+
const resolvedAlign = align || "start";
|
|
545
|
+
const messageProps = Object.assign({}, props);
|
|
546
|
+
messageProps.align = resolvedAlign;
|
|
547
|
+
messageProps.className = cn(
|
|
548
|
+
"a24-chat-message group flex w-full flex-col gap-2",
|
|
549
|
+
isUser ? cn("a24-chat-message--user is-user max-w-[80%] justify-end", isRtl ? "mr-auto" : "ml-auto") : "a24-chat-message--assistant is-assistant max-w-full",
|
|
550
|
+
className
|
|
551
|
+
);
|
|
552
|
+
return React2.createElement(
|
|
553
|
+
ChatMessageRoleContext.Provider,
|
|
554
|
+
{ value: from },
|
|
555
|
+
React2.createElement(Message, messageProps)
|
|
556
|
+
);
|
|
557
|
+
}
|
|
558
|
+
function ChatMessageContent({
|
|
559
|
+
className,
|
|
560
|
+
from,
|
|
561
|
+
...props
|
|
562
|
+
}) {
|
|
563
|
+
const inheritedRole = React2.useContext(ChatMessageRoleContext);
|
|
564
|
+
const isUser = (from ?? inheritedRole) === "user";
|
|
565
|
+
return React2.createElement("div", {
|
|
566
|
+
className: cn(
|
|
567
|
+
"a24-chat-message-content flex max-w-full min-w-0 flex-col gap-2 text-sm",
|
|
568
|
+
isUser ? "a24-chat-message-content--user is-user:dark w-fit overflow-visible rounded-lg bg-secondary px-4 py-3 text-foreground" : "a24-chat-message-content--assistant w-full overflow-visible text-foreground",
|
|
569
|
+
className
|
|
570
|
+
),
|
|
571
|
+
"data-slot": "bubble-content",
|
|
572
|
+
...props
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
function ChatMessageActions({
|
|
576
|
+
className,
|
|
577
|
+
...props
|
|
578
|
+
}) {
|
|
579
|
+
return React2.createElement("div", {
|
|
580
|
+
className: cn("flex items-center gap-1", className),
|
|
581
|
+
"data-slot": "message-actions",
|
|
582
|
+
...props
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
function ChatMessageAction({
|
|
586
|
+
children,
|
|
587
|
+
className,
|
|
588
|
+
label,
|
|
589
|
+
tooltip,
|
|
590
|
+
type = "button",
|
|
591
|
+
...props
|
|
592
|
+
}) {
|
|
593
|
+
const tooltipLabel = typeof tooltip === "string" ? tooltip : void 0;
|
|
594
|
+
return React2.createElement(
|
|
595
|
+
"button",
|
|
596
|
+
{
|
|
597
|
+
"aria-label": label || tooltipLabel,
|
|
598
|
+
className: cn(
|
|
599
|
+
"inline-flex size-8 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:border-neutral-300 focus-visible:outline-none focus-visible:ring-0 disabled:pointer-events-none disabled:opacity-50",
|
|
600
|
+
className
|
|
601
|
+
),
|
|
602
|
+
title: tooltipLabel,
|
|
603
|
+
type,
|
|
604
|
+
...props
|
|
605
|
+
},
|
|
606
|
+
children,
|
|
607
|
+
React2.createElement(
|
|
608
|
+
"span",
|
|
609
|
+
{ className: "sr-only" },
|
|
610
|
+
label || tooltipLabel
|
|
611
|
+
)
|
|
612
|
+
);
|
|
613
|
+
}
|
|
614
|
+
function ChatAttachment({
|
|
615
|
+
data,
|
|
616
|
+
onRemove,
|
|
617
|
+
orientation,
|
|
618
|
+
...props
|
|
619
|
+
}) {
|
|
620
|
+
const mediaType = data.mediaType || data.contentType || String(data.type || "application/octet-stream");
|
|
621
|
+
const filename = data.filename || data.name || "Attachment";
|
|
622
|
+
const isImage = mediaType.startsWith("image/") && Boolean(data.url);
|
|
623
|
+
return React2.createElement(
|
|
624
|
+
Attachment,
|
|
625
|
+
{
|
|
626
|
+
orientation: orientation || (isImage ? "vertical" : "horizontal"),
|
|
627
|
+
...props
|
|
628
|
+
},
|
|
629
|
+
React2.createElement(
|
|
630
|
+
AttachmentMedia,
|
|
631
|
+
{ variant: isImage ? "image" : "icon" },
|
|
632
|
+
isImage ? React2.createElement("img", {
|
|
633
|
+
alt: filename,
|
|
634
|
+
className: "size-full object-cover",
|
|
635
|
+
src: data.url
|
|
636
|
+
}) : React2.createElement(
|
|
637
|
+
"span",
|
|
638
|
+
{ "aria-hidden": "true", className: "text-sm" },
|
|
639
|
+
filename.slice(0, 1).toUpperCase()
|
|
640
|
+
)
|
|
641
|
+
),
|
|
642
|
+
React2.createElement(
|
|
643
|
+
AttachmentContent,
|
|
644
|
+
null,
|
|
645
|
+
React2.createElement(AttachmentTitle, null, filename),
|
|
646
|
+
React2.createElement(AttachmentDescription, null, mediaType)
|
|
647
|
+
),
|
|
648
|
+
onRemove ? React2.createElement(
|
|
649
|
+
"button",
|
|
650
|
+
{
|
|
651
|
+
"aria-label": "Remove attachment",
|
|
652
|
+
className: "relative z-20 mr-1 inline-flex size-6 shrink-0 items-center justify-center rounded-md text-muted-foreground hover:bg-muted hover:text-foreground",
|
|
653
|
+
onClick: (event) => {
|
|
654
|
+
event.stopPropagation();
|
|
655
|
+
onRemove();
|
|
656
|
+
},
|
|
657
|
+
type: "button"
|
|
658
|
+
},
|
|
659
|
+
React2.createElement("span", { "aria-hidden": "true" }, "x")
|
|
660
|
+
) : null
|
|
661
|
+
);
|
|
662
|
+
}
|
|
663
|
+
function ChatAttachments({
|
|
664
|
+
children,
|
|
665
|
+
className,
|
|
666
|
+
...props
|
|
667
|
+
}) {
|
|
668
|
+
if (!children) return null;
|
|
669
|
+
return React2.createElement(
|
|
670
|
+
AttachmentGroup,
|
|
671
|
+
{
|
|
672
|
+
className: cn(
|
|
673
|
+
props.dir === "rtl" ? "mr-auto" : "ml-auto",
|
|
674
|
+
"w-fit flex-wrap overflow-visible",
|
|
675
|
+
className
|
|
676
|
+
),
|
|
677
|
+
...props
|
|
678
|
+
},
|
|
679
|
+
children
|
|
680
|
+
);
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
// src/ui/agent-chat-actions.tsx
|
|
684
|
+
import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
685
|
+
var FEEDBACK_REASONS = [
|
|
686
|
+
["incorrect", "Incorrect"],
|
|
687
|
+
["irrelevant", "Irrelevant"],
|
|
688
|
+
["incomplete", "Incomplete"],
|
|
689
|
+
["unsafe", "Unsafe"],
|
|
690
|
+
["citation_issue", "Citation issue"],
|
|
691
|
+
["other", "Other"]
|
|
692
|
+
];
|
|
693
|
+
function AgentChatDefaultActions({
|
|
694
|
+
additionalActions,
|
|
695
|
+
dir,
|
|
696
|
+
handlers,
|
|
697
|
+
message
|
|
698
|
+
}) {
|
|
699
|
+
const [feedbackOpen, setFeedbackOpen] = React3.useState(false);
|
|
700
|
+
const [feedbackTrigger, setFeedbackTrigger] = React3.useState(null);
|
|
701
|
+
const [reason, setReason] = React3.useState(handlers?.feedback?.reason ?? null);
|
|
702
|
+
const [comment, setComment] = React3.useState(handlers?.feedback?.comment ?? "");
|
|
703
|
+
React3.useEffect(() => {
|
|
704
|
+
setReason(handlers?.feedback?.reason ?? null);
|
|
705
|
+
setComment(handlers?.feedback?.comment ?? "");
|
|
706
|
+
}, [handlers?.feedback?.comment, handlers?.feedback?.reason]);
|
|
707
|
+
if (!handlers || message.role !== "assistant" || message.isFinal === false) return null;
|
|
708
|
+
const canWriteFeedback = Boolean(message.runId && handlers.onSetFeedback);
|
|
709
|
+
return /* @__PURE__ */ jsxs2(ChatMessageActions, { children: [
|
|
710
|
+
handlers.onRetry ? /* @__PURE__ */ jsx4(
|
|
711
|
+
ChatMessageAction,
|
|
712
|
+
{
|
|
713
|
+
label: "Retry",
|
|
714
|
+
onClick: () => void handlers.onRetry?.(message),
|
|
715
|
+
tooltip: "Regenerate response",
|
|
716
|
+
children: /* @__PURE__ */ jsx4(RefreshCcw, { className: "size-4" })
|
|
717
|
+
}
|
|
718
|
+
) : null,
|
|
719
|
+
canWriteFeedback ? /* @__PURE__ */ jsx4(
|
|
720
|
+
ChatMessageAction,
|
|
721
|
+
{
|
|
722
|
+
label: "Like",
|
|
723
|
+
onClick: () => void handlers.onSetFeedback?.(message, {
|
|
724
|
+
rating: handlers.feedback?.rating === "like" ? null : "like"
|
|
725
|
+
}),
|
|
726
|
+
tooltip: "Like this response",
|
|
727
|
+
children: /* @__PURE__ */ jsx4(ThumbsUp, { className: "size-4", fill: handlers.feedback?.rating === "like" ? "currentColor" : "none" })
|
|
728
|
+
}
|
|
729
|
+
) : null,
|
|
730
|
+
canWriteFeedback ? /* @__PURE__ */ jsxs2(Popover.Root, { onOpenChange: setFeedbackOpen, open: feedbackOpen, children: [
|
|
731
|
+
/* @__PURE__ */ jsx4(Popover.Trigger, { asChild: true, children: /* @__PURE__ */ jsx4(
|
|
732
|
+
ChatMessageAction,
|
|
733
|
+
{
|
|
734
|
+
label: "Dislike",
|
|
735
|
+
onClick: () => {
|
|
736
|
+
if (handlers.feedback?.rating !== "dislike") {
|
|
737
|
+
void handlers.onSetFeedback?.(message, { rating: "dislike" });
|
|
738
|
+
}
|
|
739
|
+
setFeedbackOpen(true);
|
|
740
|
+
},
|
|
741
|
+
ref: setFeedbackTrigger,
|
|
742
|
+
tooltip: "Dislike this response",
|
|
743
|
+
children: /* @__PURE__ */ jsx4(ThumbsDown, { className: "size-4", fill: handlers.feedback?.rating === "dislike" ? "currentColor" : "none" })
|
|
744
|
+
}
|
|
745
|
+
) }),
|
|
746
|
+
/* @__PURE__ */ jsx4(
|
|
747
|
+
Popover.Portal,
|
|
748
|
+
{
|
|
749
|
+
container: feedbackTrigger?.closest("[data-agents24-theme-root]") ?? void 0,
|
|
750
|
+
children: /* @__PURE__ */ jsxs2(
|
|
751
|
+
Popover.Content,
|
|
752
|
+
{
|
|
753
|
+
align: "end",
|
|
754
|
+
"aria-label": "Dislike details",
|
|
755
|
+
className: "z-50 w-80 rounded-lg border border-border bg-popover p-4 text-popover-foreground shadow-md outline-none",
|
|
756
|
+
dir,
|
|
757
|
+
side: "top",
|
|
758
|
+
sideOffset: 8,
|
|
759
|
+
children: [
|
|
760
|
+
/* @__PURE__ */ jsx4("p", { className: "text-sm font-medium", children: "What could be better?" }),
|
|
761
|
+
/* @__PURE__ */ jsx4("div", { className: "mt-3 flex flex-wrap gap-1.5", children: FEEDBACK_REASONS.map(([value, label]) => /* @__PURE__ */ jsx4(
|
|
762
|
+
"button",
|
|
763
|
+
{
|
|
764
|
+
"aria-pressed": reason === value,
|
|
765
|
+
className: "rounded-md border border-border px-2.5 py-1.5 text-xs font-medium transition-colors hover:bg-muted aria-pressed:border-transparent aria-pressed:bg-secondary",
|
|
766
|
+
onClick: () => setReason(reason === value ? null : value),
|
|
767
|
+
type: "button",
|
|
768
|
+
children: label
|
|
769
|
+
},
|
|
770
|
+
value
|
|
771
|
+
)) }),
|
|
772
|
+
/* @__PURE__ */ jsx4(
|
|
773
|
+
"textarea",
|
|
774
|
+
{
|
|
775
|
+
"aria-label": "Optional feedback comment",
|
|
776
|
+
className: "mt-3 min-h-20 w-full resize-none rounded-md border border-input bg-background px-3 py-2 text-sm outline-none placeholder:text-muted-foreground focus-visible:ring-2 focus-visible:ring-ring",
|
|
777
|
+
maxLength: 1e3,
|
|
778
|
+
onChange: (event) => setComment(event.target.value),
|
|
779
|
+
placeholder: "Add a comment (optional)",
|
|
780
|
+
value: comment
|
|
781
|
+
}
|
|
782
|
+
),
|
|
783
|
+
/* @__PURE__ */ jsxs2("div", { className: "mt-3 flex items-center justify-between gap-2", children: [
|
|
784
|
+
/* @__PURE__ */ jsx4(
|
|
785
|
+
"button",
|
|
786
|
+
{
|
|
787
|
+
className: "rounded-md px-3 py-2 text-sm font-medium hover:bg-muted",
|
|
788
|
+
onClick: () => {
|
|
789
|
+
void Promise.resolve(handlers.onSetFeedback?.(message, { rating: null })).then(() => setFeedbackOpen(false)).catch(() => void 0);
|
|
790
|
+
},
|
|
791
|
+
type: "button",
|
|
792
|
+
children: "Remove feedback"
|
|
793
|
+
}
|
|
794
|
+
),
|
|
795
|
+
/* @__PURE__ */ jsx4(
|
|
796
|
+
"button",
|
|
797
|
+
{
|
|
798
|
+
className: "rounded-md bg-primary px-3 py-2 text-sm font-medium text-primary-foreground hover:opacity-90",
|
|
799
|
+
onClick: () => {
|
|
800
|
+
void Promise.resolve(handlers.onSetFeedback?.(message, { rating: "dislike", reason, comment })).then(() => setFeedbackOpen(false)).catch(() => void 0);
|
|
801
|
+
},
|
|
802
|
+
type: "button",
|
|
803
|
+
children: "Save"
|
|
804
|
+
}
|
|
805
|
+
)
|
|
806
|
+
] })
|
|
807
|
+
]
|
|
808
|
+
}
|
|
809
|
+
)
|
|
810
|
+
}
|
|
811
|
+
)
|
|
812
|
+
] }) : null,
|
|
813
|
+
additionalActions,
|
|
814
|
+
handlers.onCopy ? /* @__PURE__ */ jsx4(
|
|
815
|
+
ChatMessageAction,
|
|
816
|
+
{
|
|
817
|
+
label: "Copy",
|
|
818
|
+
onClick: () => handlers.onCopy?.(message.content || "", message.id),
|
|
819
|
+
tooltip: handlers.copied ? "Copied!" : "Copy to clipboard",
|
|
820
|
+
children: handlers.copied ? /* @__PURE__ */ jsx4(Check, { className: "size-4 text-green-600" }) : /* @__PURE__ */ jsx4(Copy, { className: "size-4" })
|
|
821
|
+
}
|
|
822
|
+
) : null
|
|
823
|
+
] });
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
// src/ui/agent-chat-message.tsx
|
|
827
|
+
import * as React8 from "react";
|
|
828
|
+
import { ChevronDown as ChevronDown3, ChevronUp } from "lucide-react";
|
|
829
|
+
|
|
830
|
+
// src/ui/agent-response-timeline.tsx
|
|
831
|
+
import * as React7 from "react";
|
|
832
|
+
|
|
833
|
+
// src/ui/ask-user-interaction.tsx
|
|
834
|
+
import * as React4 from "react";
|
|
835
|
+
import {
|
|
836
|
+
ChevronDown,
|
|
837
|
+
ChevronLeft,
|
|
838
|
+
ChevronRight,
|
|
839
|
+
Pencil
|
|
840
|
+
} from "lucide-react";
|
|
841
|
+
|
|
842
|
+
// src/ui/ask-user-interaction-state.ts
|
|
843
|
+
function emptyAskUserDrafts(questions) {
|
|
844
|
+
return Object.fromEntries(
|
|
845
|
+
questions.map((question) => [
|
|
846
|
+
question.id,
|
|
847
|
+
{ customText: "", selectedOptionIds: [] }
|
|
848
|
+
])
|
|
849
|
+
);
|
|
850
|
+
}
|
|
851
|
+
function askUserDraftHasAnswer(draft) {
|
|
852
|
+
return Boolean(
|
|
853
|
+
draft && (draft.selectedOptionIds.length > 0 || draft.customText.trim().length > 0)
|
|
854
|
+
);
|
|
855
|
+
}
|
|
856
|
+
function selectAskUserOption({
|
|
857
|
+
draft,
|
|
858
|
+
drafts,
|
|
859
|
+
optionId,
|
|
860
|
+
question
|
|
861
|
+
}) {
|
|
862
|
+
const selectedOptionIds = question.multiSelect ? draft.selectedOptionIds.includes(optionId) ? draft.selectedOptionIds.filter((id) => id !== optionId) : [...draft.selectedOptionIds, optionId] : [optionId];
|
|
863
|
+
return {
|
|
864
|
+
...drafts,
|
|
865
|
+
[question.id]: {
|
|
866
|
+
customText: "",
|
|
867
|
+
selectedOptionIds
|
|
868
|
+
}
|
|
869
|
+
};
|
|
870
|
+
}
|
|
871
|
+
function updateAskUserCustomText({
|
|
872
|
+
customText,
|
|
873
|
+
drafts,
|
|
874
|
+
question
|
|
875
|
+
}) {
|
|
876
|
+
return {
|
|
877
|
+
...drafts,
|
|
878
|
+
[question.id]: {
|
|
879
|
+
customText,
|
|
880
|
+
selectedOptionIds: question.multiSelect ? drafts[question.id]?.selectedOptionIds || [] : []
|
|
881
|
+
}
|
|
882
|
+
};
|
|
883
|
+
}
|
|
884
|
+
function askUserAnswersFromDrafts(questions, drafts) {
|
|
885
|
+
return questions.map((question) => {
|
|
886
|
+
const answer = drafts[question.id];
|
|
887
|
+
return {
|
|
888
|
+
questionId: question.id,
|
|
889
|
+
selectedOptionIds: answer?.selectedOptionIds || [],
|
|
890
|
+
...answer?.customText.trim() ? { customText: answer.customText.trim() } : {}
|
|
891
|
+
};
|
|
892
|
+
});
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
// src/ui/ask-user-interaction.tsx
|
|
896
|
+
import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
897
|
+
function resolvedAnswerText(question, answer) {
|
|
898
|
+
if (!answer) return ["No response recorded"];
|
|
899
|
+
const labels = answer.selectedOptionIds.flatMap((optionId) => {
|
|
900
|
+
const option = question.options.find((item) => item.id === optionId);
|
|
901
|
+
return option ? [option.label] : [];
|
|
902
|
+
});
|
|
903
|
+
if (answer.customText) labels.push(answer.customText);
|
|
904
|
+
return labels.length ? labels : ["No response recorded"];
|
|
905
|
+
}
|
|
906
|
+
function ResolvedQuestions({
|
|
907
|
+
buttonRef,
|
|
908
|
+
part
|
|
909
|
+
}) {
|
|
910
|
+
const [open, setOpen] = React4.useState(false);
|
|
911
|
+
const count = part.questions.length;
|
|
912
|
+
const answers = new Map(
|
|
913
|
+
(part.resolution?.answers || []).map((answer) => [
|
|
914
|
+
answer.questionId,
|
|
915
|
+
answer
|
|
916
|
+
])
|
|
917
|
+
);
|
|
918
|
+
return /* @__PURE__ */ jsxs3("div", { className: "a24-ask-user a24-ask-user--resolved w-full", children: [
|
|
919
|
+
/* @__PURE__ */ jsxs3(
|
|
920
|
+
"button",
|
|
921
|
+
{
|
|
922
|
+
"aria-expanded": open,
|
|
923
|
+
className: "group flex min-w-0 items-center gap-1.5 rounded-md px-0 py-0.5 text-start text-sm text-muted-foreground transition-colors hover:text-foreground motion-reduce:transition-none",
|
|
924
|
+
onClick: () => setOpen((value) => !value),
|
|
925
|
+
ref: buttonRef,
|
|
926
|
+
type: "button",
|
|
927
|
+
children: [
|
|
928
|
+
/* @__PURE__ */ jsxs3("span", { children: [
|
|
929
|
+
"Asked ",
|
|
930
|
+
count,
|
|
931
|
+
" question",
|
|
932
|
+
count === 1 ? "" : "s"
|
|
933
|
+
] }),
|
|
934
|
+
open ? /* @__PURE__ */ jsx5(ChevronDown, { "aria-hidden": "true", className: "size-3.5" }) : /* @__PURE__ */ jsx5(
|
|
935
|
+
ChevronRight,
|
|
936
|
+
{
|
|
937
|
+
"aria-hidden": "true",
|
|
938
|
+
className: "size-3.5 opacity-70 rtl:rotate-180"
|
|
939
|
+
}
|
|
940
|
+
)
|
|
941
|
+
]
|
|
942
|
+
}
|
|
943
|
+
),
|
|
944
|
+
open ? /* @__PURE__ */ jsx5("div", { className: "mt-2 space-y-3 ps-4", children: part.questions.map((question) => /* @__PURE__ */ jsxs3("div", { className: "min-w-0", children: [
|
|
945
|
+
/* @__PURE__ */ jsx5("div", { className: "text-sm font-medium text-foreground", children: question.question }),
|
|
946
|
+
/* @__PURE__ */ jsx5("div", { className: "mt-1 space-y-1 text-sm text-muted-foreground", children: resolvedAnswerText(
|
|
947
|
+
question,
|
|
948
|
+
answers.get(question.id)
|
|
949
|
+
).map((value, index) => /* @__PURE__ */ jsx5(
|
|
950
|
+
"div",
|
|
951
|
+
{
|
|
952
|
+
className: "whitespace-pre-wrap break-words",
|
|
953
|
+
children: value
|
|
954
|
+
},
|
|
955
|
+
`${question.id}:${index}`
|
|
956
|
+
)) })
|
|
957
|
+
] }, question.id)) }) : null
|
|
958
|
+
] });
|
|
959
|
+
}
|
|
960
|
+
function AskUserInteraction({
|
|
961
|
+
error,
|
|
962
|
+
isBusy = false,
|
|
963
|
+
onSubmit,
|
|
964
|
+
part
|
|
965
|
+
}) {
|
|
966
|
+
const questions = part.questions;
|
|
967
|
+
const [step, setStep] = React4.useState(0);
|
|
968
|
+
const [drafts, setDrafts] = React4.useState(
|
|
969
|
+
() => emptyAskUserDrafts(questions)
|
|
970
|
+
);
|
|
971
|
+
const headingRef = React4.useRef(null);
|
|
972
|
+
const questionsRef = React4.useRef(questions);
|
|
973
|
+
const resolvedButtonRef = React4.useRef(null);
|
|
974
|
+
const wasResolvedRef = React4.useRef(part.status !== "pending");
|
|
975
|
+
questionsRef.current = questions;
|
|
976
|
+
React4.useEffect(() => {
|
|
977
|
+
setStep(0);
|
|
978
|
+
setDrafts(emptyAskUserDrafts(questionsRef.current));
|
|
979
|
+
}, [part.interruptId]);
|
|
980
|
+
React4.useEffect(() => {
|
|
981
|
+
headingRef.current?.focus();
|
|
982
|
+
}, [step]);
|
|
983
|
+
React4.useEffect(() => {
|
|
984
|
+
const isResolved = part.status !== "pending";
|
|
985
|
+
if (isResolved && !wasResolvedRef.current) {
|
|
986
|
+
resolvedButtonRef.current?.focus();
|
|
987
|
+
}
|
|
988
|
+
wasResolvedRef.current = isResolved;
|
|
989
|
+
}, [part.status]);
|
|
990
|
+
if (part.status !== "pending") {
|
|
991
|
+
return /* @__PURE__ */ jsx5(ResolvedQuestions, { buttonRef: resolvedButtonRef, part });
|
|
992
|
+
}
|
|
993
|
+
const question = questions[step];
|
|
994
|
+
if (!question) {
|
|
995
|
+
return /* @__PURE__ */ jsx5("div", { className: "text-sm text-destructive", role: "alert", children: "This question request is unavailable." });
|
|
996
|
+
}
|
|
997
|
+
const draft = drafts[question.id] || {
|
|
998
|
+
customText: "",
|
|
999
|
+
selectedOptionIds: []
|
|
1000
|
+
};
|
|
1001
|
+
const isLastQuestion = step === questions.length - 1;
|
|
1002
|
+
const canMoveForward = askUserDraftHasAnswer(draft);
|
|
1003
|
+
const completeStep = (nextDrafts) => {
|
|
1004
|
+
if (isLastQuestion) {
|
|
1005
|
+
onSubmit?.(askUserAnswersFromDrafts(questions, nextDrafts));
|
|
1006
|
+
return;
|
|
1007
|
+
}
|
|
1008
|
+
setStep((current) => Math.min(current + 1, questions.length - 1));
|
|
1009
|
+
};
|
|
1010
|
+
const selectOption = (optionId) => {
|
|
1011
|
+
const nextDrafts = selectAskUserOption({
|
|
1012
|
+
draft,
|
|
1013
|
+
drafts,
|
|
1014
|
+
optionId,
|
|
1015
|
+
question
|
|
1016
|
+
});
|
|
1017
|
+
setDrafts(nextDrafts);
|
|
1018
|
+
if (!question.multiSelect) completeStep(nextDrafts);
|
|
1019
|
+
};
|
|
1020
|
+
const updateCustomText = (customText) => {
|
|
1021
|
+
setDrafts(
|
|
1022
|
+
(current) => updateAskUserCustomText({
|
|
1023
|
+
customText,
|
|
1024
|
+
drafts: current,
|
|
1025
|
+
question
|
|
1026
|
+
})
|
|
1027
|
+
);
|
|
1028
|
+
};
|
|
1029
|
+
return /* @__PURE__ */ jsxs3("div", { className: "a24-ask-user space-y-3 rounded-xl border border-border/50 bg-background p-4 shadow-sm", children: [
|
|
1030
|
+
/* @__PURE__ */ jsxs3("div", { className: "flex min-w-0 items-start justify-between gap-4", children: [
|
|
1031
|
+
/* @__PURE__ */ jsx5(
|
|
1032
|
+
"h3",
|
|
1033
|
+
{
|
|
1034
|
+
className: "min-w-0 flex-1 text-base font-medium leading-6 text-foreground outline-none",
|
|
1035
|
+
ref: headingRef,
|
|
1036
|
+
tabIndex: -1,
|
|
1037
|
+
children: question.question
|
|
1038
|
+
}
|
|
1039
|
+
),
|
|
1040
|
+
/* @__PURE__ */ jsxs3("div", { className: "flex shrink-0 items-center gap-1 text-muted-foreground", children: [
|
|
1041
|
+
/* @__PURE__ */ jsx5(
|
|
1042
|
+
"button",
|
|
1043
|
+
{
|
|
1044
|
+
"aria-label": "Previous question",
|
|
1045
|
+
className: "flex size-8 items-center justify-center rounded-full transition-colors hover:bg-muted hover:text-foreground disabled:pointer-events-none disabled:opacity-30",
|
|
1046
|
+
disabled: isBusy || step === 0,
|
|
1047
|
+
onClick: () => setStep((current) => Math.max(0, current - 1)),
|
|
1048
|
+
type: "button",
|
|
1049
|
+
children: /* @__PURE__ */ jsx5(
|
|
1050
|
+
ChevronLeft,
|
|
1051
|
+
{
|
|
1052
|
+
"aria-hidden": "true",
|
|
1053
|
+
className: "size-4 rtl:rotate-180"
|
|
1054
|
+
}
|
|
1055
|
+
)
|
|
1056
|
+
}
|
|
1057
|
+
),
|
|
1058
|
+
/* @__PURE__ */ jsxs3(
|
|
1059
|
+
"span",
|
|
1060
|
+
{
|
|
1061
|
+
"aria-label": `Question ${step + 1} of ${questions.length}`,
|
|
1062
|
+
className: "min-w-12 text-center text-sm tabular-nums",
|
|
1063
|
+
children: [
|
|
1064
|
+
step + 1,
|
|
1065
|
+
" / ",
|
|
1066
|
+
questions.length
|
|
1067
|
+
]
|
|
1068
|
+
}
|
|
1069
|
+
),
|
|
1070
|
+
/* @__PURE__ */ jsx5(
|
|
1071
|
+
"button",
|
|
1072
|
+
{
|
|
1073
|
+
"aria-label": isLastQuestion ? "Submit answers" : "Next question",
|
|
1074
|
+
className: "flex size-8 items-center justify-center rounded-full transition-colors hover:bg-muted hover:text-foreground disabled:pointer-events-none disabled:opacity-30",
|
|
1075
|
+
disabled: isBusy || !onSubmit || !canMoveForward,
|
|
1076
|
+
onClick: () => completeStep(drafts),
|
|
1077
|
+
type: "button",
|
|
1078
|
+
children: /* @__PURE__ */ jsx5(
|
|
1079
|
+
ChevronRight,
|
|
1080
|
+
{
|
|
1081
|
+
"aria-hidden": "true",
|
|
1082
|
+
className: "size-4 rtl:rotate-180"
|
|
1083
|
+
}
|
|
1084
|
+
)
|
|
1085
|
+
}
|
|
1086
|
+
)
|
|
1087
|
+
] })
|
|
1088
|
+
] }),
|
|
1089
|
+
/* @__PURE__ */ jsxs3("div", { className: "space-y-1", children: [
|
|
1090
|
+
question.options.map((option, index) => {
|
|
1091
|
+
const selected = draft.selectedOptionIds.includes(option.id);
|
|
1092
|
+
return /* @__PURE__ */ jsxs3(
|
|
1093
|
+
"button",
|
|
1094
|
+
{
|
|
1095
|
+
"aria-pressed": selected,
|
|
1096
|
+
className: cn(
|
|
1097
|
+
"group flex min-h-11 w-full items-center gap-3 rounded-xl px-2.5 py-2 text-start text-sm transition-colors motion-reduce:transition-none",
|
|
1098
|
+
selected ? "bg-muted" : "hover:bg-muted/70"
|
|
1099
|
+
),
|
|
1100
|
+
disabled: isBusy || !onSubmit,
|
|
1101
|
+
onClick: () => selectOption(option.id),
|
|
1102
|
+
type: "button",
|
|
1103
|
+
children: [
|
|
1104
|
+
/* @__PURE__ */ jsx5(
|
|
1105
|
+
"span",
|
|
1106
|
+
{
|
|
1107
|
+
className: cn(
|
|
1108
|
+
"flex size-8 shrink-0 items-center justify-center rounded-full border text-sm tabular-nums transition-colors",
|
|
1109
|
+
selected ? "border-border bg-background text-foreground" : "border-border/70 bg-muted/60 text-muted-foreground"
|
|
1110
|
+
),
|
|
1111
|
+
children: index + 1
|
|
1112
|
+
}
|
|
1113
|
+
),
|
|
1114
|
+
/* @__PURE__ */ jsx5("span", { className: "min-w-0 flex-1 font-medium text-foreground", children: option.label }),
|
|
1115
|
+
/* @__PURE__ */ jsx5(
|
|
1116
|
+
ChevronRight,
|
|
1117
|
+
{
|
|
1118
|
+
"aria-hidden": "true",
|
|
1119
|
+
className: "size-4 shrink-0 text-muted-foreground opacity-0 transition-opacity group-hover:opacity-100 group-focus-visible:opacity-100 rtl:rotate-180"
|
|
1120
|
+
}
|
|
1121
|
+
)
|
|
1122
|
+
]
|
|
1123
|
+
},
|
|
1124
|
+
option.id
|
|
1125
|
+
);
|
|
1126
|
+
}),
|
|
1127
|
+
/* @__PURE__ */ jsxs3("div", { className: "flex min-h-11 items-center gap-2 rounded-xl bg-muted px-2.5 py-1.5 focus-within:ring-1 focus-within:ring-ring", children: [
|
|
1128
|
+
/* @__PURE__ */ jsx5("span", { className: "flex size-8 shrink-0 items-center justify-center rounded-full border border-border/70 bg-background/70 text-muted-foreground", children: /* @__PURE__ */ jsx5(Pencil, { "aria-hidden": "true", className: "size-4" }) }),
|
|
1129
|
+
/* @__PURE__ */ jsx5(
|
|
1130
|
+
"input",
|
|
1131
|
+
{
|
|
1132
|
+
"aria-label": "Other response",
|
|
1133
|
+
autoComplete: "off",
|
|
1134
|
+
className: "h-8 min-w-0 flex-1 bg-transparent px-1 text-sm text-foreground outline-none placeholder:text-muted-foreground",
|
|
1135
|
+
disabled: isBusy || !onSubmit,
|
|
1136
|
+
maxLength: 2e3,
|
|
1137
|
+
onChange: (event) => updateCustomText(event.target.value),
|
|
1138
|
+
onKeyDown: (event) => {
|
|
1139
|
+
if (event.key === "Enter" && !event.nativeEvent.isComposing && draft.customText.trim()) {
|
|
1140
|
+
event.preventDefault();
|
|
1141
|
+
completeStep(drafts);
|
|
1142
|
+
}
|
|
1143
|
+
},
|
|
1144
|
+
placeholder: "Other\u2026",
|
|
1145
|
+
value: draft.customText
|
|
1146
|
+
}
|
|
1147
|
+
),
|
|
1148
|
+
draft.customText.trim() ? /* @__PURE__ */ jsx5(
|
|
1149
|
+
"button",
|
|
1150
|
+
{
|
|
1151
|
+
className: "h-8 shrink-0 rounded-full bg-foreground px-4 text-sm font-medium text-background transition-colors hover:opacity-85 disabled:opacity-50",
|
|
1152
|
+
disabled: isBusy || !onSubmit,
|
|
1153
|
+
onClick: () => completeStep(drafts),
|
|
1154
|
+
type: "button",
|
|
1155
|
+
children: isLastQuestion ? "Submit" : "Next"
|
|
1156
|
+
}
|
|
1157
|
+
) : null
|
|
1158
|
+
] })
|
|
1159
|
+
] }),
|
|
1160
|
+
error ? /* @__PURE__ */ jsx5("div", { className: "text-sm text-destructive", role: "alert", children: error }) : null
|
|
1161
|
+
] });
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
// src/ui/agent-chat-context-compression-row.tsx
|
|
1165
|
+
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
1166
|
+
function compressionPresentation(part) {
|
|
1167
|
+
if (part.status === "failed") {
|
|
1168
|
+
return { label: "Could not compact message history", status: "failed" };
|
|
1169
|
+
}
|
|
1170
|
+
if (part.status === "completed") {
|
|
1171
|
+
return { label: "Compacted message history", status: "completed" };
|
|
1172
|
+
}
|
|
1173
|
+
return { label: "Compacting message history", status: "running" };
|
|
1174
|
+
}
|
|
1175
|
+
function AgentChatContextCompressionRow({
|
|
1176
|
+
className,
|
|
1177
|
+
part
|
|
1178
|
+
}) {
|
|
1179
|
+
const presentation = compressionPresentation(part);
|
|
1180
|
+
return /* @__PURE__ */ jsx6(
|
|
1181
|
+
"div",
|
|
1182
|
+
{
|
|
1183
|
+
"aria-live": "polite",
|
|
1184
|
+
className: cn("a24-context-compression-row", className),
|
|
1185
|
+
"data-agents24-context-compression": "",
|
|
1186
|
+
"data-status": part.status,
|
|
1187
|
+
role: "status",
|
|
1188
|
+
children: /* @__PURE__ */ jsx6("div", { className: "a24-tool-trigger-row", children: /* @__PURE__ */ jsx6("div", { className: cn(
|
|
1189
|
+
"a24-tool-trigger a24-tool-trigger--root a24-tool-trigger--without-orb",
|
|
1190
|
+
presentation.status === "failed" && "a24-tool-trigger--error",
|
|
1191
|
+
presentation.status === "running" && "a24-tool-trigger--active"
|
|
1192
|
+
), children: /* @__PURE__ */ jsx6("span", { className: cn(
|
|
1193
|
+
"a24-tool-trigger__label",
|
|
1194
|
+
presentation.status === "running" && "a24-tool-shimmer"
|
|
1195
|
+
), children: presentation.label }) }) })
|
|
1196
|
+
}
|
|
1197
|
+
);
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
// src/ui/message-response.tsx
|
|
1201
|
+
import * as React5 from "react";
|
|
1202
|
+
import { defaultRehypePlugins, Streamdown } from "streamdown";
|
|
1203
|
+
|
|
1204
|
+
// src/active-content.ts
|
|
1205
|
+
var CHAT_MARKDOWN_HARDEN_OPTIONS = Object.freeze({
|
|
1206
|
+
allowDataImages: false,
|
|
1207
|
+
allowedImagePrefixes: [],
|
|
1208
|
+
allowedLinkPrefixes: ["*"],
|
|
1209
|
+
allowedProtocols: [],
|
|
1210
|
+
defaultOrigin: void 0,
|
|
1211
|
+
imageBlockPolicy: "text-only",
|
|
1212
|
+
linkBlockPolicy: "text-only"
|
|
1213
|
+
});
|
|
1214
|
+
function safeChatMarkdownHref(value) {
|
|
1215
|
+
if (!value) return void 0;
|
|
1216
|
+
if (value.startsWith("#")) return value;
|
|
1217
|
+
try {
|
|
1218
|
+
const url = new URL(value);
|
|
1219
|
+
return url.protocol === "https:" || url.protocol === "http:" ? url.href : void 0;
|
|
1220
|
+
} catch {
|
|
1221
|
+
return void 0;
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
// src/ui/message-response.tsx
|
|
1226
|
+
import { Fragment, jsx as jsx7 } from "react/jsx-runtime";
|
|
1227
|
+
function SafeMarkdownLink({ children, href, ...props }) {
|
|
1228
|
+
const domProps = { ...props };
|
|
1229
|
+
delete domProps.node;
|
|
1230
|
+
const safeHref = safeChatMarkdownHref(href);
|
|
1231
|
+
if (!safeHref) return /* @__PURE__ */ jsx7("span", { children });
|
|
1232
|
+
if (safeHref.startsWith("#")) return /* @__PURE__ */ jsx7("a", { ...domProps, href: safeHref, children });
|
|
1233
|
+
return /* @__PURE__ */ jsx7("a", { ...domProps, href: safeHref, rel: "noopener noreferrer", target: "_blank", children });
|
|
1234
|
+
}
|
|
1235
|
+
function BlockedMarkdownImage({ alt }) {
|
|
1236
|
+
return alt ? /* @__PURE__ */ jsx7("span", { "aria-label": `Image blocked: ${alt}`, role: "img", children: alt }) : null;
|
|
1237
|
+
}
|
|
1238
|
+
var BLOCK_MARKDOWN_TAGS = /* @__PURE__ */ new Set([
|
|
1239
|
+
"blockquote",
|
|
1240
|
+
"div",
|
|
1241
|
+
"h1",
|
|
1242
|
+
"h2",
|
|
1243
|
+
"h3",
|
|
1244
|
+
"h4",
|
|
1245
|
+
"h5",
|
|
1246
|
+
"h6",
|
|
1247
|
+
"hr",
|
|
1248
|
+
"ol",
|
|
1249
|
+
"pre",
|
|
1250
|
+
"table",
|
|
1251
|
+
"ul"
|
|
1252
|
+
]);
|
|
1253
|
+
function isMultilineCodeNode(node) {
|
|
1254
|
+
return node?.tagName === "code" && node.position?.start?.line !== void 0 && node.position?.end?.line !== void 0 && node.position.start.line !== node.position.end.line;
|
|
1255
|
+
}
|
|
1256
|
+
function hasBlockMarkdownChild(child) {
|
|
1257
|
+
if (!React5.isValidElement(child)) {
|
|
1258
|
+
return false;
|
|
1259
|
+
}
|
|
1260
|
+
if (typeof child.type === "string" && BLOCK_MARKDOWN_TAGS.has(child.type)) {
|
|
1261
|
+
return true;
|
|
1262
|
+
}
|
|
1263
|
+
const props = child.props;
|
|
1264
|
+
if (BLOCK_MARKDOWN_TAGS.has(props.node?.tagName || "") || isMultilineCodeNode(props.node)) {
|
|
1265
|
+
return true;
|
|
1266
|
+
}
|
|
1267
|
+
return React5.Children.toArray(props.children).some(hasBlockMarkdownChild);
|
|
1268
|
+
}
|
|
1269
|
+
function MessageMarkdownParagraph({
|
|
1270
|
+
children,
|
|
1271
|
+
...props
|
|
1272
|
+
}) {
|
|
1273
|
+
const domProps = { ...props };
|
|
1274
|
+
delete domProps.node;
|
|
1275
|
+
const childArray = React5.Children.toArray(children).filter(
|
|
1276
|
+
(child) => child !== null && child !== ""
|
|
1277
|
+
);
|
|
1278
|
+
const isImageOnly = childArray.length === 1 && React5.isValidElement(childArray[0]) && childArray[0].props.node?.tagName === "img";
|
|
1279
|
+
if (isImageOnly) {
|
|
1280
|
+
return /* @__PURE__ */ jsx7(Fragment, { children });
|
|
1281
|
+
}
|
|
1282
|
+
if (childArray.some(hasBlockMarkdownChild)) {
|
|
1283
|
+
return /* @__PURE__ */ jsx7("div", { ...domProps, children });
|
|
1284
|
+
}
|
|
1285
|
+
return /* @__PURE__ */ jsx7("p", { ...domProps, children });
|
|
1286
|
+
}
|
|
1287
|
+
var MESSAGE_RESPONSE_COMPONENTS = {
|
|
1288
|
+
a: SafeMarkdownLink,
|
|
1289
|
+
img: BlockedMarkdownImage,
|
|
1290
|
+
p: MessageMarkdownParagraph
|
|
1291
|
+
};
|
|
1292
|
+
var streamdownDefaultRehypePlugins = defaultRehypePlugins || {};
|
|
1293
|
+
var streamdownHardenPlugin = Array.isArray(
|
|
1294
|
+
streamdownDefaultRehypePlugins.harden
|
|
1295
|
+
) ? streamdownDefaultRehypePlugins.harden[0] : streamdownDefaultRehypePlugins.harden;
|
|
1296
|
+
var MESSAGE_RESPONSE_REHYPE_PLUGINS = [
|
|
1297
|
+
...Object.entries(streamdownDefaultRehypePlugins).filter(([key]) => key !== "harden").map(([, plugin]) => plugin),
|
|
1298
|
+
...streamdownHardenPlugin ? [
|
|
1299
|
+
[
|
|
1300
|
+
streamdownHardenPlugin,
|
|
1301
|
+
CHAT_MARKDOWN_HARDEN_OPTIONS
|
|
1302
|
+
]
|
|
1303
|
+
] : []
|
|
1304
|
+
];
|
|
1305
|
+
var MessageResponse = React5.memo(
|
|
1306
|
+
({
|
|
1307
|
+
children,
|
|
1308
|
+
className,
|
|
1309
|
+
components,
|
|
1310
|
+
rehypePlugins,
|
|
1311
|
+
streaming = false,
|
|
1312
|
+
streamingId,
|
|
1313
|
+
...props
|
|
1314
|
+
}) => {
|
|
1315
|
+
const text = typeof children === "string" || typeof children === "number" ? String(children) : "";
|
|
1316
|
+
const streamingText = useStreamingText({
|
|
1317
|
+
charsPerSecond: 120,
|
|
1318
|
+
id: streamingId || text,
|
|
1319
|
+
isStreaming: streaming && text.length > 0,
|
|
1320
|
+
maxCatchupChars: 32,
|
|
1321
|
+
text
|
|
1322
|
+
});
|
|
1323
|
+
const streamdownComponents = React5.useMemo(
|
|
1324
|
+
() => ({ ...MESSAGE_RESPONSE_COMPONENTS, ...components }),
|
|
1325
|
+
[components]
|
|
1326
|
+
);
|
|
1327
|
+
return /* @__PURE__ */ jsx7(
|
|
1328
|
+
Streamdown,
|
|
1329
|
+
{
|
|
1330
|
+
className: cn("a24-message-response", className),
|
|
1331
|
+
components: streamdownComponents,
|
|
1332
|
+
rehypePlugins: rehypePlugins || MESSAGE_RESPONSE_REHYPE_PLUGINS,
|
|
1333
|
+
...props,
|
|
1334
|
+
children: text ? streamingText.displayedText : children
|
|
1335
|
+
}
|
|
1336
|
+
);
|
|
1337
|
+
},
|
|
1338
|
+
(prevProps, nextProps) => prevProps.children === nextProps.children && prevProps.className === nextProps.className && prevProps.streaming === nextProps.streaming && prevProps.streamingId === nextProps.streamingId && prevProps.components === nextProps.components
|
|
1339
|
+
);
|
|
1340
|
+
MessageResponse.displayName = "MessageResponse";
|
|
1341
|
+
|
|
1342
|
+
// src/ui/hitl-presentation.ts
|
|
1343
|
+
function optionalString(value) {
|
|
1344
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
1345
|
+
}
|
|
1346
|
+
function hitlActionLabel(part, action) {
|
|
1347
|
+
if (action === "approve") return ["tool_review", "app_data_permission"].includes(part.hitlKind) ? "Allow once" : "Approve";
|
|
1348
|
+
if (action === "reject") return ["tool_review", "app_data_permission"].includes(part.hitlKind) ? "Don\u2019t allow" : "Reject";
|
|
1349
|
+
if (action === "connect") return "Connect";
|
|
1350
|
+
if (action === "respond") return "Submit";
|
|
1351
|
+
return "Skip";
|
|
1352
|
+
}
|
|
1353
|
+
function hitlTitle(part) {
|
|
1354
|
+
const presentation = part.presentation || {};
|
|
1355
|
+
if (part.hitlKind === "tool_review") return `Run ${optionalString(presentation.tool_name) || "this tool"}?`;
|
|
1356
|
+
if (part.hitlKind === "mcp_auth") {
|
|
1357
|
+
const serverName = optionalString(presentation.server_name);
|
|
1358
|
+
return serverName ? `Connect ${serverName} to continue` : part.message;
|
|
1359
|
+
}
|
|
1360
|
+
if (part.hitlKind === "app_data_permission") return "Allow this data change?";
|
|
1361
|
+
return part.message;
|
|
1362
|
+
}
|
|
1363
|
+
function resolvedHitlLabel(part) {
|
|
1364
|
+
const outcome = String(part.resolution?.outcome || part.status).trim().toLowerCase();
|
|
1365
|
+
return {
|
|
1366
|
+
approved: part.hitlKind === "user_approval" ? "Approved" : "Allowed",
|
|
1367
|
+
rejected: part.hitlKind === "user_approval" ? "Rejected" : "Not allowed",
|
|
1368
|
+
connected: "connected",
|
|
1369
|
+
skipped: "Skipped",
|
|
1370
|
+
timeout: "Timed out",
|
|
1371
|
+
expired: "Timed out",
|
|
1372
|
+
cancelled: "Cancelled",
|
|
1373
|
+
invalidated: "No longer available",
|
|
1374
|
+
policy_denied: "Not allowed"
|
|
1375
|
+
}[outcome] || outcome.replace(/_/g, " ");
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
// src/ui/tool-sessions.tsx
|
|
1379
|
+
import * as React6 from "react";
|
|
1380
|
+
import { ChevronDown as ChevronDown2, ChevronRight as ChevronRight2 } from "lucide-react";
|
|
1381
|
+
import { ThinkingOrb } from "thinking-orbs";
|
|
1382
|
+
import { Fragment as Fragment2, jsx as jsx8, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1383
|
+
function agentChatToolLabel(part) {
|
|
1384
|
+
const presentation = part.presentation || {};
|
|
1385
|
+
return optionalText(presentation.activity) || optionalText(presentation.title) || optionalText(part.displayName) || optionalText(part.summary) || optionalText(part.toolName) || "Tool activity";
|
|
1386
|
+
}
|
|
1387
|
+
function isAgentChatSubagent(part) {
|
|
1388
|
+
const sourceKind = String(part.sourceKind || "").trim().toLowerCase();
|
|
1389
|
+
return sourceKind === "agent" || sourceKind === "agent_call" || Boolean(part.presentation.childRun);
|
|
1390
|
+
}
|
|
1391
|
+
function isAgentChatToolRunning(part) {
|
|
1392
|
+
return part.state === "input-available" || part.state === "input-streaming";
|
|
1393
|
+
}
|
|
1394
|
+
function latestRunningAgentChatToolId(parts) {
|
|
1395
|
+
const active = [...parts].reverse().find(isAgentChatToolRunning);
|
|
1396
|
+
return active?.toolCallId || active?.id || null;
|
|
1397
|
+
}
|
|
1398
|
+
function partitionAgentChatToolTimeline(parts, toolViewMode = "closed") {
|
|
1399
|
+
const segments = [];
|
|
1400
|
+
let index = 0;
|
|
1401
|
+
while (index < parts.length) {
|
|
1402
|
+
const part = parts[index];
|
|
1403
|
+
const leadingModelTurn = part.kind === "model-turn" && parts[index + 1]?.kind === "tool" && !isAgentChatSubagent(parts[index + 1]) ? part : void 0;
|
|
1404
|
+
if (leadingModelTurn) index += 1;
|
|
1405
|
+
const candidate = parts[index];
|
|
1406
|
+
if (candidate.kind !== "tool") {
|
|
1407
|
+
segments.push({ kind: "part", part: candidate });
|
|
1408
|
+
index += 1;
|
|
1409
|
+
continue;
|
|
1410
|
+
}
|
|
1411
|
+
if (isAgentChatSubagent(candidate)) {
|
|
1412
|
+
segments.push({ kind: "subagent", part: candidate });
|
|
1413
|
+
index += 1;
|
|
1414
|
+
continue;
|
|
1415
|
+
}
|
|
1416
|
+
const streak = [];
|
|
1417
|
+
while (index < parts.length) {
|
|
1418
|
+
const candidate2 = parts[index];
|
|
1419
|
+
if (candidate2.kind !== "tool" || isAgentChatSubagent(candidate2)) break;
|
|
1420
|
+
streak.push(candidate2);
|
|
1421
|
+
index += 1;
|
|
1422
|
+
}
|
|
1423
|
+
const modelTurn = parts[index]?.kind === "model-turn" ? parts[index] : void 0;
|
|
1424
|
+
if (modelTurn) index += 1;
|
|
1425
|
+
if (toolViewMode === "expanded") {
|
|
1426
|
+
if (leadingModelTurn) segments.push({ kind: "part", part: leadingModelTurn });
|
|
1427
|
+
segments.push(...streak.map((toolPart) => ({ kind: "tool", part: toolPart })));
|
|
1428
|
+
if (modelTurn) segments.push({ kind: "part", part: modelTurn });
|
|
1429
|
+
} else if (streak.length === 1 && !modelTurn && !leadingModelTurn) {
|
|
1430
|
+
segments.push({ kind: "tool", part: streak[0] });
|
|
1431
|
+
} else {
|
|
1432
|
+
segments.push({
|
|
1433
|
+
kind: "tool-session",
|
|
1434
|
+
parts: streak,
|
|
1435
|
+
...leadingModelTurn ? { leadingModelTurn } : {},
|
|
1436
|
+
...modelTurn ? { modelTurn } : {}
|
|
1437
|
+
});
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
return segments;
|
|
1441
|
+
}
|
|
1442
|
+
function optionalText(value) {
|
|
1443
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
1444
|
+
}
|
|
1445
|
+
function isActive(part, activeToolId) {
|
|
1446
|
+
return Boolean(activeToolId && (part.id === activeToolId || part.toolCallId === activeToolId));
|
|
1447
|
+
}
|
|
1448
|
+
function ToolActivityReel({
|
|
1449
|
+
announce = true,
|
|
1450
|
+
initialLabel,
|
|
1451
|
+
initialShownAt,
|
|
1452
|
+
label
|
|
1453
|
+
}) {
|
|
1454
|
+
const initial = initialLabel && initialLabel !== label ? initialLabel : label;
|
|
1455
|
+
const parsedInitialShownAt = initialShownAt ? Date.parse(initialShownAt) : Number.NaN;
|
|
1456
|
+
const [displayed, setDisplayed] = React6.useState(initial);
|
|
1457
|
+
const [previous, setPrevious] = React6.useState(null);
|
|
1458
|
+
const displayedRef = React6.useRef(initial);
|
|
1459
|
+
const latestRef = React6.useRef(label);
|
|
1460
|
+
const shownAtRef = React6.useRef(Number.isFinite(parsedInitialShownAt) ? parsedInitialShownAt : Date.now());
|
|
1461
|
+
React6.useEffect(() => {
|
|
1462
|
+
latestRef.current = label;
|
|
1463
|
+
if (displayedRef.current === label) return;
|
|
1464
|
+
const showLatest = () => {
|
|
1465
|
+
const next = latestRef.current;
|
|
1466
|
+
if (displayedRef.current === next) return;
|
|
1467
|
+
const old = displayedRef.current;
|
|
1468
|
+
displayedRef.current = next;
|
|
1469
|
+
shownAtRef.current = Date.now();
|
|
1470
|
+
setPrevious(old);
|
|
1471
|
+
setDisplayed(next);
|
|
1472
|
+
};
|
|
1473
|
+
const remaining = Math.max(0, 500 - (Date.now() - shownAtRef.current));
|
|
1474
|
+
if (remaining === 0) {
|
|
1475
|
+
showLatest();
|
|
1476
|
+
return;
|
|
1477
|
+
}
|
|
1478
|
+
const timer = window.setTimeout(showLatest, remaining);
|
|
1479
|
+
return () => window.clearTimeout(timer);
|
|
1480
|
+
}, [label]);
|
|
1481
|
+
React6.useEffect(() => {
|
|
1482
|
+
if (!previous) return;
|
|
1483
|
+
const timer = window.setTimeout(() => setPrevious(null), 200);
|
|
1484
|
+
return () => window.clearTimeout(timer);
|
|
1485
|
+
}, [previous]);
|
|
1486
|
+
return /* @__PURE__ */ jsxs4("span", { className: "a24-tool-reel", "aria-live": announce ? "polite" : "off", children: [
|
|
1487
|
+
previous ? /* @__PURE__ */ jsx8("span", { "aria-hidden": "true", className: "a24-tool-reel__previous", children: previous }) : null,
|
|
1488
|
+
/* @__PURE__ */ jsx8("span", { className: previous ? "a24-tool-reel__current" : void 0, children: displayed })
|
|
1489
|
+
] });
|
|
1490
|
+
}
|
|
1491
|
+
function ToolActivityOrb({ label }) {
|
|
1492
|
+
return /* @__PURE__ */ jsx8("span", { className: "a24-tool-orb", children: /* @__PURE__ */ jsx8(ThinkingOrb, { "aria-label": label || "Working", size: 20, speed: 0.8, state: "searching" }) });
|
|
1493
|
+
}
|
|
1494
|
+
function AgentChatActivityRow({
|
|
1495
|
+
label,
|
|
1496
|
+
modelTurnId,
|
|
1497
|
+
onSelect
|
|
1498
|
+
}) {
|
|
1499
|
+
const content = /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
1500
|
+
/* @__PURE__ */ jsx8(ToolActivityOrb, { label }),
|
|
1501
|
+
/* @__PURE__ */ jsx8("span", { className: "a24-tool-trigger__label a24-tool-shimmer", children: /* @__PURE__ */ jsx8(ToolActivityReel, { label }) })
|
|
1502
|
+
] });
|
|
1503
|
+
return /* @__PURE__ */ jsx8("div", { className: "a24-tool-trigger-row", "data-agents24-model-turn": modelTurnId, role: "status", children: onSelect ? /* @__PURE__ */ jsx8("button", { className: "a24-tool-trigger a24-tool-trigger--root a24-tool-trigger--active", onClick: onSelect, type: "button", children: content }) : /* @__PURE__ */ jsx8("div", { className: "a24-tool-trigger a24-tool-trigger--root a24-tool-trigger--active", children: content }) });
|
|
1504
|
+
}
|
|
1505
|
+
function AgentChatModelTurnRow({ part }) {
|
|
1506
|
+
return /* @__PURE__ */ jsx8(AgentChatActivityRow, { label: "Planning next moves", modelTurnId: part.modelTurnId });
|
|
1507
|
+
}
|
|
1508
|
+
function AgentChatNodeActivityRow({
|
|
1509
|
+
onSelect,
|
|
1510
|
+
part
|
|
1511
|
+
}) {
|
|
1512
|
+
const [open, setOpen] = React6.useState(false);
|
|
1513
|
+
const contentId = React6.useId();
|
|
1514
|
+
const running = part.status === "running";
|
|
1515
|
+
const failed = part.status === "failed";
|
|
1516
|
+
const failureMessage = part.failure?.message || "The node could not complete the request.";
|
|
1517
|
+
const triggerClassName = cn(
|
|
1518
|
+
"a24-tool-trigger",
|
|
1519
|
+
"a24-tool-trigger--root",
|
|
1520
|
+
!running && "a24-tool-trigger--without-orb",
|
|
1521
|
+
running && "a24-tool-trigger--active",
|
|
1522
|
+
failed && "a24-tool-trigger--error"
|
|
1523
|
+
);
|
|
1524
|
+
const label = running ? /* @__PURE__ */ jsx8("span", { className: "a24-tool-shimmer", children: /* @__PURE__ */ jsx8(ToolActivityReel, { label: part.label }) }) : part.label;
|
|
1525
|
+
if (!failed) {
|
|
1526
|
+
return /* @__PURE__ */ jsx8("div", { className: "a24-tool-trigger-row", role: "status", children: onSelect ? /* @__PURE__ */ jsxs4("button", { className: triggerClassName, onClick: () => onSelect(part), type: "button", children: [
|
|
1527
|
+
running ? /* @__PURE__ */ jsx8(ToolActivityOrb, { label: part.label }) : null,
|
|
1528
|
+
/* @__PURE__ */ jsx8("span", { className: "a24-tool-trigger__label", children: label })
|
|
1529
|
+
] }) : /* @__PURE__ */ jsxs4("div", { className: triggerClassName, children: [
|
|
1530
|
+
running ? /* @__PURE__ */ jsx8(ToolActivityOrb, { label: part.label }) : null,
|
|
1531
|
+
/* @__PURE__ */ jsx8("span", { className: "a24-tool-trigger__label", children: label })
|
|
1532
|
+
] }) });
|
|
1533
|
+
}
|
|
1534
|
+
return /* @__PURE__ */ jsxs4("div", { className: "a24-tool-disclosure", children: [
|
|
1535
|
+
/* @__PURE__ */ jsx8("div", { className: "a24-tool-trigger-row", children: /* @__PURE__ */ jsxs4(
|
|
1536
|
+
"button",
|
|
1537
|
+
{
|
|
1538
|
+
"aria-controls": contentId,
|
|
1539
|
+
"aria-expanded": open,
|
|
1540
|
+
className: triggerClassName,
|
|
1541
|
+
onClick: () => {
|
|
1542
|
+
onSelect?.(part);
|
|
1543
|
+
setOpen((value) => !value);
|
|
1544
|
+
},
|
|
1545
|
+
type: "button",
|
|
1546
|
+
children: [
|
|
1547
|
+
/* @__PURE__ */ jsx8("span", { className: "a24-tool-trigger__label", children: label }),
|
|
1548
|
+
open ? /* @__PURE__ */ jsx8(ChevronDown2, { "aria-hidden": "true", className: "a24-tool-trigger__chevron" }) : /* @__PURE__ */ jsx8(ChevronRight2, { "aria-hidden": "true", className: "a24-tool-trigger__chevron" })
|
|
1549
|
+
]
|
|
1550
|
+
}
|
|
1551
|
+
) }),
|
|
1552
|
+
open ? /* @__PURE__ */ jsx8("div", { className: "a24-tool-detail a24-tool-detail--error", id: contentId, children: failureMessage }) : null
|
|
1553
|
+
] });
|
|
1554
|
+
}
|
|
1555
|
+
function toolRowLabel(part) {
|
|
1556
|
+
const label = agentChatToolLabel(part);
|
|
1557
|
+
return isAgentChatToolRunning(part) ? label : label.replace(/^(?:running|ran)\s+/i, "");
|
|
1558
|
+
}
|
|
1559
|
+
function ToolDisclosure({
|
|
1560
|
+
children,
|
|
1561
|
+
error,
|
|
1562
|
+
label,
|
|
1563
|
+
loading,
|
|
1564
|
+
onExpandedChange,
|
|
1565
|
+
onSelect,
|
|
1566
|
+
part,
|
|
1567
|
+
root,
|
|
1568
|
+
showOrb,
|
|
1569
|
+
statusLabel,
|
|
1570
|
+
accessory
|
|
1571
|
+
}) {
|
|
1572
|
+
const [open, setOpen] = React6.useState(false);
|
|
1573
|
+
const contentId = React6.useId();
|
|
1574
|
+
const expandable = React6.Children.toArray(children).length > 0;
|
|
1575
|
+
if (!expandable) {
|
|
1576
|
+
const triggerClassName = cn("a24-tool-trigger", root && "a24-tool-trigger--root", root && !showOrb && "a24-tool-trigger--without-orb", error && "a24-tool-trigger--error", loading && "a24-tool-trigger--active");
|
|
1577
|
+
return /* @__PURE__ */ jsxs4("div", { className: "a24-tool-trigger-row", children: [
|
|
1578
|
+
onSelect && part ? /* @__PURE__ */ jsxs4("button", { className: triggerClassName, onClick: () => onSelect(part), type: "button", children: [
|
|
1579
|
+
showOrb ? /* @__PURE__ */ jsx8(ToolActivityOrb, { label: statusLabel }) : null,
|
|
1580
|
+
/* @__PURE__ */ jsx8("span", { className: "a24-tool-trigger__label", children: label })
|
|
1581
|
+
] }) : /* @__PURE__ */ jsxs4("div", { className: triggerClassName, children: [
|
|
1582
|
+
showOrb ? /* @__PURE__ */ jsx8(ToolActivityOrb, { label: statusLabel }) : null,
|
|
1583
|
+
/* @__PURE__ */ jsx8("span", { className: "a24-tool-trigger__label", children: label })
|
|
1584
|
+
] }),
|
|
1585
|
+
accessory
|
|
1586
|
+
] });
|
|
1587
|
+
}
|
|
1588
|
+
return /* @__PURE__ */ jsxs4("div", { className: "a24-tool-disclosure", children: [
|
|
1589
|
+
/* @__PURE__ */ jsxs4("div", { className: "a24-tool-trigger-row", children: [
|
|
1590
|
+
/* @__PURE__ */ jsxs4(
|
|
1591
|
+
"button",
|
|
1592
|
+
{
|
|
1593
|
+
"aria-controls": contentId,
|
|
1594
|
+
"aria-expanded": open,
|
|
1595
|
+
className: cn("a24-tool-trigger", root && "a24-tool-trigger--root", root && !showOrb && "a24-tool-trigger--without-orb", error && "a24-tool-trigger--error", loading && "a24-tool-trigger--active"),
|
|
1596
|
+
onClick: () => {
|
|
1597
|
+
const nextOpen = !open;
|
|
1598
|
+
setOpen(nextOpen);
|
|
1599
|
+
if (part) onSelect?.(part);
|
|
1600
|
+
onExpandedChange?.(nextOpen, part);
|
|
1601
|
+
},
|
|
1602
|
+
type: "button",
|
|
1603
|
+
children: [
|
|
1604
|
+
showOrb ? /* @__PURE__ */ jsx8(ToolActivityOrb, { label: statusLabel }) : null,
|
|
1605
|
+
/* @__PURE__ */ jsx8("span", { className: "a24-tool-trigger__label", children: label }),
|
|
1606
|
+
open ? /* @__PURE__ */ jsx8(ChevronDown2, { "aria-hidden": "true", className: "a24-tool-trigger__chevron" }) : /* @__PURE__ */ jsx8(ChevronRight2, { "aria-hidden": "true", className: "a24-tool-trigger__chevron" })
|
|
1607
|
+
]
|
|
1608
|
+
}
|
|
1609
|
+
),
|
|
1610
|
+
accessory
|
|
1611
|
+
] }),
|
|
1612
|
+
open ? /* @__PURE__ */ jsx8("div", { className: "a24-tool-disclosure__content", id: contentId, children }) : null
|
|
1613
|
+
] });
|
|
1614
|
+
}
|
|
1615
|
+
function ToolLabel({ active, label, part, slots }) {
|
|
1616
|
+
const rendered = slots?.renderToolLabel?.({ isActive: active, label, part }) ?? label;
|
|
1617
|
+
return active ? /* @__PURE__ */ jsx8("span", { className: "a24-tool-shimmer", children: rendered }) : /* @__PURE__ */ jsx8(Fragment2, { children: rendered });
|
|
1618
|
+
}
|
|
1619
|
+
function ToolList({ children }) {
|
|
1620
|
+
const listRef = React6.useRef(null);
|
|
1621
|
+
const [fades, setFades] = React6.useState({ bottom: false, top: false });
|
|
1622
|
+
const updateFades = React6.useCallback(() => {
|
|
1623
|
+
const list = listRef.current;
|
|
1624
|
+
if (!list) return;
|
|
1625
|
+
const next = {
|
|
1626
|
+
bottom: list.scrollTop + list.clientHeight < list.scrollHeight - 1,
|
|
1627
|
+
top: list.scrollTop > 1
|
|
1628
|
+
};
|
|
1629
|
+
setFades((current) => current.bottom === next.bottom && current.top === next.top ? current : next);
|
|
1630
|
+
}, []);
|
|
1631
|
+
React6.useEffect(() => {
|
|
1632
|
+
updateFades();
|
|
1633
|
+
const list = listRef.current;
|
|
1634
|
+
if (!list || typeof ResizeObserver === "undefined") return;
|
|
1635
|
+
const observer = new ResizeObserver(updateFades);
|
|
1636
|
+
observer.observe(list);
|
|
1637
|
+
return () => observer.disconnect();
|
|
1638
|
+
}, [children, updateFades]);
|
|
1639
|
+
return /* @__PURE__ */ jsx8(
|
|
1640
|
+
"div",
|
|
1641
|
+
{
|
|
1642
|
+
className: "a24-tool-list-frame",
|
|
1643
|
+
"data-scroll-fade-bottom": fades.bottom ? "" : void 0,
|
|
1644
|
+
"data-scroll-fade-top": fades.top ? "" : void 0,
|
|
1645
|
+
children: /* @__PURE__ */ jsx8("div", { className: "a24-tool-list", onScroll: updateFades, ref: listRef, role: "list", children })
|
|
1646
|
+
}
|
|
1647
|
+
);
|
|
1648
|
+
}
|
|
1649
|
+
function AgentChatToolRow({
|
|
1650
|
+
activeToolId,
|
|
1651
|
+
part,
|
|
1652
|
+
root = true,
|
|
1653
|
+
showOrb,
|
|
1654
|
+
slots
|
|
1655
|
+
}) {
|
|
1656
|
+
const active = isActive(part, activeToolId);
|
|
1657
|
+
const failed = part.state === "output-error";
|
|
1658
|
+
const failureMessage = part.failure?.message || null;
|
|
1659
|
+
const detail = failed ? failureMessage : optionalText(part.presentation.detail);
|
|
1660
|
+
const hostDetails = failed ? null : slots?.renderToolDetails?.(part);
|
|
1661
|
+
const context = { isActive: active, label: toolRowLabel(part), part };
|
|
1662
|
+
return /* @__PURE__ */ jsx8("div", { "data-agents24-tool-part": part.type, "data-state": part.state, children: /* @__PURE__ */ jsxs4(
|
|
1663
|
+
ToolDisclosure,
|
|
1664
|
+
{
|
|
1665
|
+
error: failed,
|
|
1666
|
+
accessory: slots?.renderToolAccessory?.(context),
|
|
1667
|
+
label: /* @__PURE__ */ jsx8(ToolLabel, { active, label: context.label, part, slots }),
|
|
1668
|
+
loading: active,
|
|
1669
|
+
onExpandedChange: slots?.onExpandedChange,
|
|
1670
|
+
onSelect: slots?.onToolSelect,
|
|
1671
|
+
part,
|
|
1672
|
+
root,
|
|
1673
|
+
showOrb: showOrb ?? active,
|
|
1674
|
+
statusLabel: active ? context.label : void 0,
|
|
1675
|
+
children: [
|
|
1676
|
+
detail ? /* @__PURE__ */ jsx8("div", { className: cn("a24-tool-detail", failed && "a24-tool-detail--error"), children: detail }) : null,
|
|
1677
|
+
hostDetails
|
|
1678
|
+
]
|
|
1679
|
+
}
|
|
1680
|
+
) });
|
|
1681
|
+
}
|
|
1682
|
+
function sessionLabel(parts, visiblePart, running) {
|
|
1683
|
+
const label = agentChatToolLabel(visiblePart);
|
|
1684
|
+
const terminalLabel = label.replace(/^(?:running|ran)\s+/i, "");
|
|
1685
|
+
return running ? label : `Ran ${terminalLabel} + ${parts.length - 1} more`;
|
|
1686
|
+
}
|
|
1687
|
+
function AgentChatToolSession({
|
|
1688
|
+
activeToolId,
|
|
1689
|
+
isLatestActivity = true,
|
|
1690
|
+
isLive = false,
|
|
1691
|
+
leadingModelTurn,
|
|
1692
|
+
modelTurn,
|
|
1693
|
+
parts,
|
|
1694
|
+
slots
|
|
1695
|
+
}) {
|
|
1696
|
+
const presentedLiveRef = React6.useRef(false);
|
|
1697
|
+
if (isLive && isLatestActivity) presentedLiveRef.current = true;
|
|
1698
|
+
const activeIndex = parts.findLastIndex((part) => isActive(part, activeToolId));
|
|
1699
|
+
const running = activeIndex >= 0;
|
|
1700
|
+
if (parts.length === 1 && !modelTurn && !leadingModelTurn) return /* @__PURE__ */ jsx8(AgentChatToolRow, { activeToolId, part: parts[0], slots });
|
|
1701
|
+
const visibleIndex = running ? activeIndex : parts.length - 1;
|
|
1702
|
+
const visiblePart = parts[visibleIndex];
|
|
1703
|
+
const planning = Boolean(modelTurn);
|
|
1704
|
+
const presentingActivity = isLatestActivity && (planning || running || isLive);
|
|
1705
|
+
const failed = isLatestActivity && (visiblePart.state === "output-error" || visiblePart.state === "cancelled");
|
|
1706
|
+
const label = planning ? "Planning next moves" : isLive && isLatestActivity ? agentChatToolLabel(visiblePart) : sessionLabel(parts, visiblePart, running);
|
|
1707
|
+
const labelContent = presentedLiveRef.current ? /* @__PURE__ */ jsx8("span", { className: presentingActivity ? "a24-tool-shimmer" : void 0, children: /* @__PURE__ */ jsx8(ToolActivityReel, { initialLabel: leadingModelTurn ? "Planning next moves" : void 0, initialShownAt: leadingModelTurn?.timestamp, label }) }) : label;
|
|
1708
|
+
const context = { isActive: presentingActivity, label, part: visiblePart };
|
|
1709
|
+
return /* @__PURE__ */ jsx8(
|
|
1710
|
+
ToolDisclosure,
|
|
1711
|
+
{
|
|
1712
|
+
accessory: slots?.renderToolAccessory?.(context),
|
|
1713
|
+
error: failed,
|
|
1714
|
+
label: labelContent,
|
|
1715
|
+
loading: presentingActivity,
|
|
1716
|
+
onExpandedChange: slots?.onExpandedChange,
|
|
1717
|
+
part: visiblePart,
|
|
1718
|
+
root: true,
|
|
1719
|
+
showOrb: isLive && isLatestActivity,
|
|
1720
|
+
statusLabel: planning ? "Planning next moves" : label,
|
|
1721
|
+
children: /* @__PURE__ */ jsx8(ToolList, { children: parts.map((part) => /* @__PURE__ */ jsx8("div", { role: "listitem", children: /* @__PURE__ */ jsx8(AgentChatToolRow, { activeToolId, part, root: false, showOrb: false, slots }) }, part.id)) })
|
|
1722
|
+
}
|
|
1723
|
+
);
|
|
1724
|
+
}
|
|
1725
|
+
function childLabel(child) {
|
|
1726
|
+
return optionalText(child.title) || optionalText(child.toolName) || "Tool";
|
|
1727
|
+
}
|
|
1728
|
+
function subagentLabel(part) {
|
|
1729
|
+
const childRun = part.presentation.childRun;
|
|
1730
|
+
const children = part.presentation.childTools || [];
|
|
1731
|
+
const name = optionalText(childRun?.agentName) || optionalText(part.displayName) || "agent";
|
|
1732
|
+
const status = String(childRun?.status || (isAgentChatToolRunning(part) ? "running" : "completed")).toLowerCase();
|
|
1733
|
+
const activeChild = children.find((child) => child.id === part.presentation.activeChildToolId) || [...children].reverse().find((child) => child.status === "running") || children[children.length - 1];
|
|
1734
|
+
if (status === "running" && activeChild) return { label: `Agent ${name}, running ${childLabel(activeChild)}`, running: true };
|
|
1735
|
+
if (status === "running") return { label: `Agent ${name}, starting`, running: true };
|
|
1736
|
+
if (status === "failed" || part.state === "output-error") return { label: `Agent ${name}, failed`, running: false };
|
|
1737
|
+
if (status === "cancelled" || status === "canceled" || part.state === "cancelled") return { label: `Agent ${name}, cancelled`, running: false };
|
|
1738
|
+
if (status === "paused") return { label: `Agent ${name}, paused`, running: false };
|
|
1739
|
+
return { label: `Agent ${name}, ran ${children.length} ${children.length === 1 ? "tool" : "tools"}`, running: false };
|
|
1740
|
+
}
|
|
1741
|
+
function AgentChatSubagentGroup({ isLatestActivity = true, isLive = false, part, slots }) {
|
|
1742
|
+
const presentedLiveRef = React6.useRef(false);
|
|
1743
|
+
if (isLive && isLatestActivity) presentedLiveRef.current = true;
|
|
1744
|
+
const children = part.presentation.childTools || [];
|
|
1745
|
+
const state = subagentLabel(part);
|
|
1746
|
+
const childRunStatus = String(part.presentation.childRun?.status || "").toLowerCase();
|
|
1747
|
+
const failed = part.state === "output-error" || part.state === "cancelled" || childRunStatus === "failed" || childRunStatus === "cancelled" || childRunStatus === "canceled";
|
|
1748
|
+
const hostDetails = failed ? null : slots?.renderToolDetails?.(part);
|
|
1749
|
+
const failureMessage = part.failure?.message || null;
|
|
1750
|
+
const presentingActivity = isLatestActivity && state.running;
|
|
1751
|
+
const labelContent = presentedLiveRef.current ? /* @__PURE__ */ jsx8("span", { className: presentingActivity ? "a24-tool-shimmer" : void 0, children: /* @__PURE__ */ jsx8(ToolActivityReel, { label: state.label }) }) : state.label;
|
|
1752
|
+
const context = { isActive: presentingActivity, label: state.label, part };
|
|
1753
|
+
return /* @__PURE__ */ jsx8(
|
|
1754
|
+
ToolDisclosure,
|
|
1755
|
+
{
|
|
1756
|
+
accessory: slots?.renderToolAccessory?.(context),
|
|
1757
|
+
error: failed,
|
|
1758
|
+
label: labelContent,
|
|
1759
|
+
loading: presentingActivity,
|
|
1760
|
+
onExpandedChange: slots?.onExpandedChange,
|
|
1761
|
+
part,
|
|
1762
|
+
root: true,
|
|
1763
|
+
showOrb: isLive && isLatestActivity,
|
|
1764
|
+
statusLabel: state.label,
|
|
1765
|
+
children: /* @__PURE__ */ jsxs4(ToolList, { children: [
|
|
1766
|
+
children.map((child) => /* @__PURE__ */ jsx8("div", { className: cn("a24-child-tool", child.status === "failed" && "a24-child-tool--error"), role: "listitem", children: slots?.renderChildTool?.(child, part) ?? /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
1767
|
+
/* @__PURE__ */ jsx8("span", { className: cn(isLive && isLatestActivity && child.status === "running" && "a24-tool-shimmer"), children: childLabel(child) }),
|
|
1768
|
+
child.detail ? /* @__PURE__ */ jsx8("span", { className: "a24-child-tool__detail", children: child.detail }) : null
|
|
1769
|
+
] }) }, child.id)),
|
|
1770
|
+
failureMessage ? /* @__PURE__ */ jsx8("div", { className: "a24-tool-detail a24-tool-detail--error", children: failureMessage }) : null,
|
|
1771
|
+
hostDetails
|
|
1772
|
+
] })
|
|
1773
|
+
}
|
|
1774
|
+
);
|
|
1775
|
+
}
|
|
1776
|
+
|
|
1777
|
+
// src/ui/agent-response-timeline.tsx
|
|
1778
|
+
import { jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1779
|
+
function isInternalAskUserTool(part) {
|
|
1780
|
+
return [part.toolName, part.toolKey, part.action].some((value) => value?.trim().toLowerCase() === "runtime__ask_user");
|
|
1781
|
+
}
|
|
1782
|
+
function Shimmer({ children, className }) {
|
|
1783
|
+
return /* @__PURE__ */ jsx9("span", { className: cn("inline-block animate-pulse", className), children });
|
|
1784
|
+
}
|
|
1785
|
+
function AgentChatToolPart({
|
|
1786
|
+
activeToolId,
|
|
1787
|
+
part
|
|
1788
|
+
}) {
|
|
1789
|
+
return /* @__PURE__ */ jsx9(AgentChatToolRow, { activeToolId, part });
|
|
1790
|
+
}
|
|
1791
|
+
function AgentChatToolGroup({
|
|
1792
|
+
activeToolId,
|
|
1793
|
+
parts
|
|
1794
|
+
}) {
|
|
1795
|
+
return /* @__PURE__ */ jsx9(AgentChatToolSession, { activeToolId, parts });
|
|
1796
|
+
}
|
|
1797
|
+
function HitlPartRow({
|
|
1798
|
+
getHitlActionState,
|
|
1799
|
+
isLoading,
|
|
1800
|
+
onHitlAction,
|
|
1801
|
+
part
|
|
1802
|
+
}) {
|
|
1803
|
+
const state = getHitlActionState?.(part);
|
|
1804
|
+
const [comment, setComment] = React7.useState("");
|
|
1805
|
+
const presentation = part.presentation || {};
|
|
1806
|
+
const requireComment = presentation.require_comment === true;
|
|
1807
|
+
const isTerminal = part.status !== "pending";
|
|
1808
|
+
const resolvedRef = React7.useRef(null);
|
|
1809
|
+
const wasTerminal = React7.useRef(isTerminal);
|
|
1810
|
+
React7.useEffect(() => {
|
|
1811
|
+
if (isTerminal && !wasTerminal.current) resolvedRef.current?.focus();
|
|
1812
|
+
wasTerminal.current = isTerminal;
|
|
1813
|
+
}, [isTerminal]);
|
|
1814
|
+
if (part.hitlKind === "user_input") {
|
|
1815
|
+
return /* @__PURE__ */ jsx9(
|
|
1816
|
+
AskUserInteraction,
|
|
1817
|
+
{
|
|
1818
|
+
error: state?.error,
|
|
1819
|
+
isBusy: isLoading || state?.status === "resuming",
|
|
1820
|
+
onSubmit: onHitlAction ? (answers) => onHitlAction(part, {
|
|
1821
|
+
action: "respond",
|
|
1822
|
+
answers: answers.map((answer) => ({
|
|
1823
|
+
question_id: answer.questionId,
|
|
1824
|
+
selected_option_ids: answer.selectedOptionIds,
|
|
1825
|
+
...answer.customText ? { custom_text: answer.customText } : {}
|
|
1826
|
+
}))
|
|
1827
|
+
}) : void 0,
|
|
1828
|
+
part
|
|
1829
|
+
}
|
|
1830
|
+
);
|
|
1831
|
+
}
|
|
1832
|
+
if (isTerminal) {
|
|
1833
|
+
return /* @__PURE__ */ jsx9("div", { className: "a24-hitl a24-hitl--resolved px-1 py-1 text-sm text-muted-foreground", ref: resolvedRef, role: "status", tabIndex: -1, children: resolvedHitlLabel(part) });
|
|
1834
|
+
}
|
|
1835
|
+
return /* @__PURE__ */ jsxs5("div", { className: "a24-hitl space-y-2.5 px-1 py-2", children: [
|
|
1836
|
+
/* @__PURE__ */ jsx9("div", { className: "a24-hitl__title text-sm font-medium text-foreground text-pretty", children: hitlTitle(part) }),
|
|
1837
|
+
requireComment ? /* @__PURE__ */ jsx9(
|
|
1838
|
+
"textarea",
|
|
1839
|
+
{
|
|
1840
|
+
"aria-label": "Approval comment",
|
|
1841
|
+
autoComplete: "off",
|
|
1842
|
+
className: "a24-hitl__comment min-h-20 w-full resize-y rounded-md border border-border/60 bg-background px-3 py-2 text-sm outline-none focus-visible:border-neutral-300 focus-visible:ring-0 dark:focus-visible:border-neutral-600",
|
|
1843
|
+
name: "hitl-comment",
|
|
1844
|
+
onChange: (event) => setComment(event.target.value),
|
|
1845
|
+
placeholder: "Comment required\u2026",
|
|
1846
|
+
value: comment
|
|
1847
|
+
}
|
|
1848
|
+
) : null,
|
|
1849
|
+
state?.error ? /* @__PURE__ */ jsx9("div", { className: "a24-hitl__error text-sm text-destructive", children: state.error }) : null,
|
|
1850
|
+
/* @__PURE__ */ jsx9("div", { className: "a24-hitl__actions flex flex-wrap items-center gap-1.5", children: part.allowedActions.map((action) => /* @__PURE__ */ jsx9(
|
|
1851
|
+
"button",
|
|
1852
|
+
{
|
|
1853
|
+
"aria-label": hitlActionLabel(part, action),
|
|
1854
|
+
className: cn(
|
|
1855
|
+
"a24-hitl__action min-h-10 rounded-md px-3 text-sm font-medium transition-[background-color,color,opacity,transform] duration-150 ease-out active:scale-[0.96] disabled:pointer-events-none disabled:opacity-60",
|
|
1856
|
+
action === "approve" || action === "connect" ? "bg-neutral-900 text-white hover:bg-neutral-800 dark:bg-neutral-100 dark:text-neutral-900 dark:hover:bg-white" : "bg-transparent text-muted-foreground hover:bg-background/80 hover:text-foreground"
|
|
1857
|
+
),
|
|
1858
|
+
"data-primary": action === "approve" || action === "connect" ? "" : void 0,
|
|
1859
|
+
disabled: !onHitlAction || isLoading || state?.status === "connecting" || state?.status === "resuming" || requireComment && !comment.trim(),
|
|
1860
|
+
onClick: () => onHitlAction?.(part, {
|
|
1861
|
+
action,
|
|
1862
|
+
...comment.trim() ? { comment: comment.trim() } : {}
|
|
1863
|
+
}),
|
|
1864
|
+
type: "button",
|
|
1865
|
+
children: hitlActionLabel(part, action)
|
|
1866
|
+
},
|
|
1867
|
+
action
|
|
1868
|
+
)) })
|
|
1869
|
+
] });
|
|
1870
|
+
}
|
|
1871
|
+
function AgentChatPart({
|
|
1872
|
+
activeToolId,
|
|
1873
|
+
getHitlActionState,
|
|
1874
|
+
isLoading,
|
|
1875
|
+
isTextStreaming,
|
|
1876
|
+
message,
|
|
1877
|
+
onHitlAction,
|
|
1878
|
+
part,
|
|
1879
|
+
responseProps,
|
|
1880
|
+
toolSlots
|
|
1881
|
+
}) {
|
|
1882
|
+
if (part.kind === "text") {
|
|
1883
|
+
return /* @__PURE__ */ jsx9(
|
|
1884
|
+
MessageResponse,
|
|
1885
|
+
{
|
|
1886
|
+
...responseProps,
|
|
1887
|
+
streaming: isTextStreaming ?? Boolean(isLoading && part.raw?.status === "streaming"),
|
|
1888
|
+
streamingId: part.id,
|
|
1889
|
+
children: part.text
|
|
1890
|
+
}
|
|
1891
|
+
);
|
|
1892
|
+
}
|
|
1893
|
+
if (part.kind === "tool") return isAgentChatSubagent(part) ? /* @__PURE__ */ jsx9(AgentChatSubagentGroup, { part, slots: toolSlots }) : /* @__PURE__ */ jsx9(AgentChatToolRow, { activeToolId, part, slots: toolSlots });
|
|
1894
|
+
if (part.kind === "model-turn") return /* @__PURE__ */ jsx9(AgentChatModelTurnRow, { part });
|
|
1895
|
+
if (part.kind === "reasoning") {
|
|
1896
|
+
const label = part.text || part.label || "Thinking";
|
|
1897
|
+
return part.status === "running" || part.status === "streaming" ? /* @__PURE__ */ jsx9(Shimmer, { className: "text-sm text-muted-foreground", children: label }) : /* @__PURE__ */ jsx9("div", { className: "text-sm text-muted-foreground", children: label });
|
|
1898
|
+
}
|
|
1899
|
+
if (part.kind === "context-compression") {
|
|
1900
|
+
return /* @__PURE__ */ jsx9(AgentChatContextCompressionRow, { part });
|
|
1901
|
+
}
|
|
1902
|
+
if (part.kind === "ui-blocks") {
|
|
1903
|
+
if (part.state === "output-error") {
|
|
1904
|
+
return /* @__PURE__ */ jsx9("div", { className: "a24-timeline-error text-sm text-destructive", children: part.errorText || "Failed to render UI content." });
|
|
1905
|
+
}
|
|
1906
|
+
return part.bundle ? /* @__PURE__ */ jsx9(SafeUiBlocks, { bundle: part.bundle }) : /* @__PURE__ */ jsx9("div", { className: "text-sm text-muted-foreground", children: part.state === "input-streaming" ? /* @__PURE__ */ jsx9(Shimmer, { children: "Rendering UI block" }) : "Rendered UI block" });
|
|
1907
|
+
}
|
|
1908
|
+
if (part.kind === "hitl") {
|
|
1909
|
+
return /* @__PURE__ */ jsx9(
|
|
1910
|
+
HitlPartRow,
|
|
1911
|
+
{
|
|
1912
|
+
getHitlActionState,
|
|
1913
|
+
isLoading,
|
|
1914
|
+
onHitlAction,
|
|
1915
|
+
part
|
|
1916
|
+
}
|
|
1917
|
+
);
|
|
1918
|
+
}
|
|
1919
|
+
if (part.kind === "error") return /* @__PURE__ */ jsx9("div", { className: "a24-timeline-error text-sm text-destructive", role: "alert", children: part.errorText });
|
|
1920
|
+
if (part.kind === "source") {
|
|
1921
|
+
const href = safeExternalUrl(part.url);
|
|
1922
|
+
return href ? /* @__PURE__ */ jsx9("a", { className: "a24-source-link", href, rel: "noopener noreferrer", target: "_blank", children: part.title }) : /* @__PURE__ */ jsx9("span", { className: "a24-source-link", children: part.title });
|
|
1923
|
+
}
|
|
1924
|
+
if (part.kind === "citation") {
|
|
1925
|
+
const href = safeExternalUrl(part.url);
|
|
1926
|
+
const label = part.label || "Citation";
|
|
1927
|
+
return href ? /* @__PURE__ */ jsx9("a", { className: "a24-citation", href, rel: "noopener noreferrer", target: "_blank", children: label }) : /* @__PURE__ */ jsx9("span", { className: "a24-citation", children: label });
|
|
1928
|
+
}
|
|
1929
|
+
if (part.kind === "attachment") return /* @__PURE__ */ jsx9("div", { className: "a24-output-attachment", children: part.attachment.filename || "Attachment" });
|
|
1930
|
+
if (part.kind === "data") return /* @__PURE__ */ jsx9("div", { "data-agents24-data-part": part.name, className: "hidden", children: message.id });
|
|
1931
|
+
return null;
|
|
1932
|
+
}
|
|
1933
|
+
function safeExternalUrl(value) {
|
|
1934
|
+
if (!value) return null;
|
|
1935
|
+
try {
|
|
1936
|
+
const url = new URL(value);
|
|
1937
|
+
return url.protocol === "https:" || url.protocol === "http:" ? url.href : null;
|
|
1938
|
+
} catch {
|
|
1939
|
+
return null;
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1942
|
+
function SafeUiBlocks({ bundle }) {
|
|
1943
|
+
const rows = Array.isArray(bundle.rows) ? bundle.rows : [];
|
|
1944
|
+
return /* @__PURE__ */ jsxs5("section", { "aria-label": typeof bundle.title === "string" ? bundle.title : "Agent result", className: "a24-ui-blocks", children: [
|
|
1945
|
+
typeof bundle.title === "string" ? /* @__PURE__ */ jsx9("h3", { children: bundle.title }) : null,
|
|
1946
|
+
rows.map((rawRow, rowIndex) => {
|
|
1947
|
+
const row = rawRow && typeof rawRow === "object" && !Array.isArray(rawRow) ? rawRow : {};
|
|
1948
|
+
const blocks = Array.isArray(row.blocks) ? row.blocks : [];
|
|
1949
|
+
return /* @__PURE__ */ jsx9("div", { className: "a24-ui-blocks__row", children: blocks.map((rawBlock, blockIndex) => {
|
|
1950
|
+
const block = rawBlock && typeof rawBlock === "object" && !Array.isArray(rawBlock) ? rawBlock : {};
|
|
1951
|
+
const title = typeof block.title === "string" ? block.title : "Result";
|
|
1952
|
+
const kind = String(block.kind || "note");
|
|
1953
|
+
if (kind === "table" && Array.isArray(block.columns) && Array.isArray(block.rows)) {
|
|
1954
|
+
return /* @__PURE__ */ jsxs5("div", { className: "a24-ui-block", children: [
|
|
1955
|
+
/* @__PURE__ */ jsx9("h4", { children: title }),
|
|
1956
|
+
/* @__PURE__ */ jsx9("div", { className: "a24-ui-table-scroll", children: /* @__PURE__ */ jsxs5("table", { children: [
|
|
1957
|
+
/* @__PURE__ */ jsx9("thead", { children: /* @__PURE__ */ jsx9("tr", { children: block.columns.map((column, index) => /* @__PURE__ */ jsx9("th", { children: String(column) }, index)) }) }),
|
|
1958
|
+
/* @__PURE__ */ jsx9("tbody", { children: block.rows.map((rawCells, index) => /* @__PURE__ */ jsx9("tr", { children: (Array.isArray(rawCells) ? rawCells : []).map((cell, cellIndex) => /* @__PURE__ */ jsx9("td", { children: String(cell) }, cellIndex)) }, index)) })
|
|
1959
|
+
] }) })
|
|
1960
|
+
] }, String(block.id || blockIndex));
|
|
1961
|
+
}
|
|
1962
|
+
const value = block.value ?? block.text ?? "";
|
|
1963
|
+
return /* @__PURE__ */ jsxs5("div", { className: "a24-ui-block", children: [
|
|
1964
|
+
/* @__PURE__ */ jsx9("h4", { children: title }),
|
|
1965
|
+
/* @__PURE__ */ jsx9("p", { children: String(value) })
|
|
1966
|
+
] }, String(block.id || blockIndex));
|
|
1967
|
+
}) }, `row-${rowIndex}`);
|
|
1968
|
+
})
|
|
1969
|
+
] });
|
|
1970
|
+
}
|
|
1971
|
+
function AgentResponseTimeline({
|
|
1972
|
+
getHitlActionState,
|
|
1973
|
+
isLoading = false,
|
|
1974
|
+
message,
|
|
1975
|
+
nodeActivityMode = "default",
|
|
1976
|
+
onHitlAction,
|
|
1977
|
+
parts: displayParts,
|
|
1978
|
+
responseProps,
|
|
1979
|
+
toolSlots,
|
|
1980
|
+
toolViewMode = "closed"
|
|
1981
|
+
}) {
|
|
1982
|
+
const parts = (displayParts || message.parts || []).filter((part) => (part.kind !== "tool" || !isInternalAskUserTool(part)) && (part.kind !== "model-turn" || part.turnKind === "agent") && (part.kind !== "node-activity" || nodeActivityMode === "all" || nodeActivityMode === "default" && part.defaultChatVisible));
|
|
1983
|
+
const toolParts = parts.filter((part) => part.kind === "tool");
|
|
1984
|
+
const activeToolId = latestRunningAgentChatToolId(toolParts);
|
|
1985
|
+
const activeTextPartId = getActiveStreamingTextPartId(
|
|
1986
|
+
{ ...message, parts: [...parts] },
|
|
1987
|
+
isLoading ? message.id : null
|
|
1988
|
+
);
|
|
1989
|
+
const rendered = [];
|
|
1990
|
+
let activeActivityClaimed = false;
|
|
1991
|
+
const segments = partitionAgentChatToolTimeline(parts, toolViewMode);
|
|
1992
|
+
for (const [segmentIndex, segment] of segments.entries()) {
|
|
1993
|
+
if (segment.kind === "tool") {
|
|
1994
|
+
const isLatestActivity = segmentIndex === segments.length - 1;
|
|
1995
|
+
const showOrb = toolViewMode === "closed" ? isLoading && isLatestActivity : Boolean(activeToolId && (segment.part.id === activeToolId || segment.part.toolCallId === activeToolId));
|
|
1996
|
+
rendered.push(/* @__PURE__ */ jsx9(AgentChatToolRow, { activeToolId, part: segment.part, showOrb, slots: toolSlots }, segment.part.id));
|
|
1997
|
+
continue;
|
|
1998
|
+
}
|
|
1999
|
+
if (segment.kind === "tool-session") {
|
|
2000
|
+
rendered.push(/* @__PURE__ */ jsx9(AgentChatToolSession, { activeToolId, isLatestActivity: segmentIndex === segments.length - 1, isLive: isLoading, leadingModelTurn: segment.leadingModelTurn, modelTurn: segment.modelTurn, parts: segment.parts, slots: toolSlots }, `tool-session-${segment.parts[0].id}`));
|
|
2001
|
+
continue;
|
|
2002
|
+
}
|
|
2003
|
+
if (segment.kind === "subagent") {
|
|
2004
|
+
rendered.push(/* @__PURE__ */ jsx9(AgentChatSubagentGroup, { isLatestActivity: segmentIndex === segments.length - 1, isLive: isLoading, part: segment.part, slots: toolSlots }, segment.part.id));
|
|
2005
|
+
continue;
|
|
2006
|
+
}
|
|
2007
|
+
const part = segment.part;
|
|
2008
|
+
if (part.kind === "model-turn") {
|
|
2009
|
+
const key = activeActivityClaimed ? part.id : `${message.id}-active-activity`;
|
|
2010
|
+
activeActivityClaimed = true;
|
|
2011
|
+
rendered.push(/* @__PURE__ */ jsx9(AgentChatActivityRow, { label: "Planning next moves", modelTurnId: part.modelTurnId }, key));
|
|
2012
|
+
continue;
|
|
2013
|
+
}
|
|
2014
|
+
if (part.kind === "node-activity") {
|
|
2015
|
+
const key = activeActivityClaimed ? part.id : `${message.id}-active-activity`;
|
|
2016
|
+
if (part.status === "running") activeActivityClaimed = true;
|
|
2017
|
+
rendered.push(
|
|
2018
|
+
part.status === "running" ? /* @__PURE__ */ jsx9(AgentChatActivityRow, { label: part.label }, key) : /* @__PURE__ */ jsx9(AgentChatNodeActivityRow, { part }, part.id)
|
|
2019
|
+
);
|
|
2020
|
+
continue;
|
|
2021
|
+
}
|
|
2022
|
+
rendered.push(
|
|
2023
|
+
/* @__PURE__ */ jsx9(React7.Fragment, { children: /* @__PURE__ */ jsx9(
|
|
2024
|
+
AgentChatPart,
|
|
2025
|
+
{
|
|
2026
|
+
activeToolId,
|
|
2027
|
+
getHitlActionState,
|
|
2028
|
+
isLoading,
|
|
2029
|
+
isTextStreaming: part.kind === "text" && part.id === activeTextPartId,
|
|
2030
|
+
message,
|
|
2031
|
+
onHitlAction,
|
|
2032
|
+
part,
|
|
2033
|
+
responseProps,
|
|
2034
|
+
toolSlots
|
|
2035
|
+
}
|
|
2036
|
+
) }, part.id)
|
|
2037
|
+
);
|
|
2038
|
+
}
|
|
2039
|
+
if (rendered.length === 0 && message.content) {
|
|
2040
|
+
rendered.push(/* @__PURE__ */ jsx9(MessageResponse, { children: message.content }, `${message.id}-fallback`));
|
|
2041
|
+
}
|
|
2042
|
+
if (rendered.length === 0 && isLoading) {
|
|
2043
|
+
rendered.push(/* @__PURE__ */ jsx9(AgentChatActivityRow, { label: "Thinking" }, `${message.id}-active-activity`));
|
|
2044
|
+
}
|
|
2045
|
+
return /* @__PURE__ */ jsx9("div", { className: "space-y-3", children: rendered });
|
|
2046
|
+
}
|
|
2047
|
+
|
|
2048
|
+
// src/ui/agent-chat-message.tsx
|
|
2049
|
+
import { jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
2050
|
+
var DEFAULT_USER_MESSAGE_COLLAPSE_THRESHOLD = 360;
|
|
2051
|
+
var DEFAULT_USER_MESSAGE_COLLAPSED_LINES = 4;
|
|
2052
|
+
function shouldCollapseUserMessage(content, threshold = DEFAULT_USER_MESSAGE_COLLAPSE_THRESHOLD) {
|
|
2053
|
+
return typeof threshold === "number" && threshold > 0 && content.length > threshold;
|
|
2054
|
+
}
|
|
2055
|
+
function AgentChatUserMessageContent({
|
|
2056
|
+
collapsedLines = DEFAULT_USER_MESSAGE_COLLAPSED_LINES,
|
|
2057
|
+
collapseThreshold = DEFAULT_USER_MESSAGE_COLLAPSE_THRESHOLD,
|
|
2058
|
+
content,
|
|
2059
|
+
showLessLabel = "Show less",
|
|
2060
|
+
showMoreLabel = "Show more"
|
|
2061
|
+
}) {
|
|
2062
|
+
const [isExpanded, setIsExpanded] = React8.useState(false);
|
|
2063
|
+
const contentId = React8.useId();
|
|
2064
|
+
const shouldCollapse = shouldCollapseUserMessage(content, collapseThreshold);
|
|
2065
|
+
const Icon = isExpanded ? ChevronUp : ChevronDown3;
|
|
2066
|
+
const clampedStyle = shouldCollapse && !isExpanded ? {
|
|
2067
|
+
WebkitBoxOrient: "vertical",
|
|
2068
|
+
WebkitLineClamp: Math.max(1, collapsedLines),
|
|
2069
|
+
display: "-webkit-box",
|
|
2070
|
+
overflow: "hidden"
|
|
2071
|
+
} : void 0;
|
|
2072
|
+
return /* @__PURE__ */ jsxs6("div", { className: "flex min-w-0 flex-col gap-3", children: [
|
|
2073
|
+
/* @__PURE__ */ jsx10("div", { id: contentId, className: "min-w-0", style: clampedStyle, children: content }),
|
|
2074
|
+
shouldCollapse ? /* @__PURE__ */ jsxs6(
|
|
2075
|
+
"button",
|
|
2076
|
+
{
|
|
2077
|
+
"aria-controls": contentId,
|
|
2078
|
+
"aria-expanded": isExpanded,
|
|
2079
|
+
className: cn(
|
|
2080
|
+
"inline-flex w-fit items-center gap-1 rounded-md text-left text-sm font-medium text-muted-foreground transition-colors",
|
|
2081
|
+
"hover:text-foreground focus-visible:border-neutral-300 focus-visible:outline-none focus-visible:ring-0"
|
|
2082
|
+
),
|
|
2083
|
+
onClick: () => setIsExpanded((current) => !current),
|
|
2084
|
+
type: "button",
|
|
2085
|
+
children: [
|
|
2086
|
+
isExpanded ? showLessLabel : showMoreLabel,
|
|
2087
|
+
/* @__PURE__ */ jsx10(Icon, { "aria-hidden": "true", className: "size-4", strokeWidth: 2 })
|
|
2088
|
+
]
|
|
2089
|
+
}
|
|
2090
|
+
) : null
|
|
2091
|
+
] });
|
|
2092
|
+
}
|
|
2093
|
+
function AgentChatMessage({
|
|
2094
|
+
actionHandlers,
|
|
2095
|
+
additionalActions,
|
|
2096
|
+
actions,
|
|
2097
|
+
actionsClassName,
|
|
2098
|
+
className,
|
|
2099
|
+
contentClassName,
|
|
2100
|
+
dir,
|
|
2101
|
+
getHitlActionState,
|
|
2102
|
+
message,
|
|
2103
|
+
nodeActivityMode,
|
|
2104
|
+
onHitlAction,
|
|
2105
|
+
Timeline = AgentResponseTimeline,
|
|
2106
|
+
renderAssistantContent,
|
|
2107
|
+
renderUserContent,
|
|
2108
|
+
showAssistantAttachments = false,
|
|
2109
|
+
streamingMessageId,
|
|
2110
|
+
userMessageCollapsedLines,
|
|
2111
|
+
userMessageContent,
|
|
2112
|
+
userMessageCollapseThreshold,
|
|
2113
|
+
userMessageShowLessLabel,
|
|
2114
|
+
userMessageShowMoreLabel
|
|
2115
|
+
}) {
|
|
2116
|
+
const isAssistant = message.role === "assistant";
|
|
2117
|
+
const isStreaming = isAssistant && message.id === streamingMessageId;
|
|
2118
|
+
const resolvedUserContent = userMessageContent ?? message.content;
|
|
2119
|
+
const shouldRenderContent = isAssistant || Boolean(renderUserContent || resolvedUserContent);
|
|
2120
|
+
const showAttachments = Boolean(message.attachments?.length) && (message.role === "user" || showAssistantAttachments);
|
|
2121
|
+
const renderedActions = actions ?? (actionHandlers ? /* @__PURE__ */ jsx10(
|
|
2122
|
+
AgentChatDefaultActions,
|
|
2123
|
+
{
|
|
2124
|
+
additionalActions,
|
|
2125
|
+
dir,
|
|
2126
|
+
handlers: actionHandlers,
|
|
2127
|
+
message
|
|
2128
|
+
}
|
|
2129
|
+
) : null);
|
|
2130
|
+
return /* @__PURE__ */ jsxs6(ChatMessage, { className, dir, from: message.role, children: [
|
|
2131
|
+
showAttachments ? /* @__PURE__ */ jsx10(ChatAttachments, { className: "mb-2", dir, children: message.attachments?.map((attachment, index) => /* @__PURE__ */ jsx10(
|
|
2132
|
+
ChatAttachment,
|
|
2133
|
+
{
|
|
2134
|
+
data: attachment,
|
|
2135
|
+
dir: "ltr"
|
|
2136
|
+
},
|
|
2137
|
+
String(attachment.url || attachment.filename || index)
|
|
2138
|
+
)) }) : null,
|
|
2139
|
+
shouldRenderContent ? /* @__PURE__ */ jsxs6(
|
|
2140
|
+
ChatMessageContent,
|
|
2141
|
+
{
|
|
2142
|
+
className: cn(isAssistant ? "bg-transparent p-0" : void 0, contentClassName),
|
|
2143
|
+
dir,
|
|
2144
|
+
from: message.role,
|
|
2145
|
+
children: [
|
|
2146
|
+
isAssistant ? renderAssistantContent ? renderAssistantContent({ isStreaming, message }) : /* @__PURE__ */ jsx10(
|
|
2147
|
+
Timeline,
|
|
2148
|
+
{
|
|
2149
|
+
getHitlActionState,
|
|
2150
|
+
isLoading: isStreaming,
|
|
2151
|
+
message,
|
|
2152
|
+
nodeActivityMode,
|
|
2153
|
+
onHitlAction,
|
|
2154
|
+
parts: message.parts
|
|
2155
|
+
}
|
|
2156
|
+
) : null,
|
|
2157
|
+
!isAssistant ? renderUserContent ? renderUserContent({ message }) : resolvedUserContent ? /* @__PURE__ */ jsx10(
|
|
2158
|
+
AgentChatUserMessageContent,
|
|
2159
|
+
{
|
|
2160
|
+
collapsedLines: userMessageCollapsedLines,
|
|
2161
|
+
collapseThreshold: userMessageCollapseThreshold,
|
|
2162
|
+
content: resolvedUserContent,
|
|
2163
|
+
showLessLabel: userMessageShowLessLabel,
|
|
2164
|
+
showMoreLabel: userMessageShowMoreLabel
|
|
2165
|
+
}
|
|
2166
|
+
) : null : null
|
|
2167
|
+
]
|
|
2168
|
+
}
|
|
2169
|
+
) : null,
|
|
2170
|
+
renderedActions ? /* @__PURE__ */ jsx10("div", { className: cn(dir === "rtl" ? "mt-1 h-9 w-full" : "mt-1 h-9 w-full -translate-x-2", actionsClassName), children: renderedActions }) : null
|
|
2171
|
+
] });
|
|
2172
|
+
}
|
|
2173
|
+
|
|
2174
|
+
// src/ui/agent-chat-context-status.tsx
|
|
2175
|
+
import * as React9 from "react";
|
|
2176
|
+
import { jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
2177
|
+
var COMPACT_TOKENS = new Intl.NumberFormat("en-US", { notation: "compact" });
|
|
2178
|
+
var PERCENT = new Intl.NumberFormat("en-US", {
|
|
2179
|
+
style: "percent",
|
|
2180
|
+
maximumFractionDigits: 1
|
|
2181
|
+
});
|
|
2182
|
+
var EXACT_SOURCES = /* @__PURE__ */ new Set(["exact", "provider_usage"]);
|
|
2183
|
+
function contextStatusMetrics(contextWindow) {
|
|
2184
|
+
const maximum = Number(contextWindow?.max_tokens || 0);
|
|
2185
|
+
const input = Math.max(0, Number(contextWindow?.input_tokens || 0));
|
|
2186
|
+
const admissionInput = Math.max(0, Number(contextWindow?.admission_input_tokens ?? input));
|
|
2187
|
+
const usageRatio = Math.max(0, Math.min(1, Number(
|
|
2188
|
+
contextWindow?.usage_ratio ?? input / maximum
|
|
2189
|
+
)));
|
|
2190
|
+
const admissionRatio = Math.max(0, Math.min(1, Number(
|
|
2191
|
+
contextWindow?.admission_ratio ?? admissionInput / maximum
|
|
2192
|
+
)));
|
|
2193
|
+
return { maximum, input, admissionInput, usageRatio, admissionRatio };
|
|
2194
|
+
}
|
|
2195
|
+
function AgentChatContextStatus({
|
|
2196
|
+
className,
|
|
2197
|
+
contextWindow
|
|
2198
|
+
}) {
|
|
2199
|
+
const tooltipId = React9.useId();
|
|
2200
|
+
const [open, setOpen] = React9.useState(false);
|
|
2201
|
+
const { maximum, input, admissionInput, usageRatio, admissionRatio } = contextStatusMetrics(contextWindow);
|
|
2202
|
+
const percentage = Math.round(usageRatio * 100);
|
|
2203
|
+
const exact = EXACT_SOURCES.has(contextWindow?.source || "unknown");
|
|
2204
|
+
if (!Number.isFinite(maximum) || maximum <= 0) return null;
|
|
2205
|
+
return /* @__PURE__ */ jsxs7(
|
|
2206
|
+
"div",
|
|
2207
|
+
{
|
|
2208
|
+
className: cn("a24-context-status", className),
|
|
2209
|
+
onBlurCapture: (event) => {
|
|
2210
|
+
if (!event.currentTarget.contains(event.relatedTarget)) setOpen(false);
|
|
2211
|
+
},
|
|
2212
|
+
onFocusCapture: () => setOpen(true),
|
|
2213
|
+
onMouseEnter: () => setOpen(true),
|
|
2214
|
+
onMouseLeave: () => setOpen(false),
|
|
2215
|
+
children: [
|
|
2216
|
+
/* @__PURE__ */ jsxs7(
|
|
2217
|
+
"button",
|
|
2218
|
+
{
|
|
2219
|
+
"aria-describedby": open ? tooltipId : void 0,
|
|
2220
|
+
"aria-label": `Context window ${percentage}% used`,
|
|
2221
|
+
className: "a24-context-status__trigger",
|
|
2222
|
+
onKeyDown: (event) => {
|
|
2223
|
+
if (event.key === "Escape") setOpen(false);
|
|
2224
|
+
},
|
|
2225
|
+
type: "button",
|
|
2226
|
+
children: [
|
|
2227
|
+
/* @__PURE__ */ jsxs7("svg", { "aria-hidden": "true", className: "a24-context-status__ring", viewBox: "0 0 24 24", children: [
|
|
2228
|
+
/* @__PURE__ */ jsx11("circle", { className: "a24-context-status__track", cx: "12", cy: "12", r: "10" }),
|
|
2229
|
+
/* @__PURE__ */ jsx11(
|
|
2230
|
+
"circle",
|
|
2231
|
+
{
|
|
2232
|
+
className: "a24-context-status__value",
|
|
2233
|
+
cx: "12",
|
|
2234
|
+
cy: "12",
|
|
2235
|
+
pathLength: "100",
|
|
2236
|
+
r: "10",
|
|
2237
|
+
strokeDasharray: `${percentage} 100`
|
|
2238
|
+
}
|
|
2239
|
+
)
|
|
2240
|
+
] }),
|
|
2241
|
+
/* @__PURE__ */ jsxs7("span", { children: [
|
|
2242
|
+
percentage,
|
|
2243
|
+
"%"
|
|
2244
|
+
] })
|
|
2245
|
+
]
|
|
2246
|
+
}
|
|
2247
|
+
),
|
|
2248
|
+
open ? /* @__PURE__ */ jsxs7("div", { className: "a24-context-status__popover", id: tooltipId, role: "tooltip", children: [
|
|
2249
|
+
/* @__PURE__ */ jsx11("span", { className: "a24-context-status__sr-only", children: "Context window" }),
|
|
2250
|
+
/* @__PURE__ */ jsxs7("strong", { children: [
|
|
2251
|
+
PERCENT.format(usageRatio),
|
|
2252
|
+
" context used"
|
|
2253
|
+
] }),
|
|
2254
|
+
/* @__PURE__ */ jsxs7("span", { children: [
|
|
2255
|
+
exact ? "Input" : "Estimated input",
|
|
2256
|
+
": ",
|
|
2257
|
+
COMPACT_TOKENS.format(input),
|
|
2258
|
+
" / ",
|
|
2259
|
+
COMPACT_TOKENS.format(maximum),
|
|
2260
|
+
" tokens"
|
|
2261
|
+
] }),
|
|
2262
|
+
admissionInput !== input ? /* @__PURE__ */ jsxs7("span", { children: [
|
|
2263
|
+
"Safety bound: ",
|
|
2264
|
+
COMPACT_TOKENS.format(admissionInput),
|
|
2265
|
+
" tokens (",
|
|
2266
|
+
PERCENT.format(admissionRatio),
|
|
2267
|
+
")"
|
|
2268
|
+
] }) : null
|
|
2269
|
+
] }) : null
|
|
2270
|
+
]
|
|
2271
|
+
}
|
|
2272
|
+
);
|
|
2273
|
+
}
|
|
39
2274
|
|
|
40
2275
|
// src/ui/bubble.tsx
|
|
41
|
-
import { cva } from "class-variance-authority";
|
|
42
|
-
import { Slot } from "@radix-ui/react-slot";
|
|
43
|
-
import { jsx } from "react/jsx-runtime";
|
|
2276
|
+
import { cva as cva2 } from "class-variance-authority";
|
|
2277
|
+
import { Slot as Slot2 } from "@radix-ui/react-slot";
|
|
2278
|
+
import { jsx as jsx12 } from "react/jsx-runtime";
|
|
44
2279
|
function BubbleGroup({ className, ...props }) {
|
|
45
|
-
return /* @__PURE__ */
|
|
2280
|
+
return /* @__PURE__ */ jsx12(
|
|
46
2281
|
"div",
|
|
47
2282
|
{
|
|
48
2283
|
"data-slot": "bubble-group",
|
|
@@ -51,7 +2286,7 @@ function BubbleGroup({ className, ...props }) {
|
|
|
51
2286
|
}
|
|
52
2287
|
);
|
|
53
2288
|
}
|
|
54
|
-
var bubbleVariants =
|
|
2289
|
+
var bubbleVariants = cva2(
|
|
55
2290
|
"group/bubble relative flex w-fit max-w-[80%] min-w-0 flex-col gap-1 group-data-[align=end]/message:self-end data-[align=end]:self-end data-[variant=ghost]:max-w-full",
|
|
56
2291
|
{
|
|
57
2292
|
variants: {
|
|
@@ -76,7 +2311,7 @@ function Bubble({
|
|
|
76
2311
|
className,
|
|
77
2312
|
...props
|
|
78
2313
|
}) {
|
|
79
|
-
return /* @__PURE__ */
|
|
2314
|
+
return /* @__PURE__ */ jsx12(
|
|
80
2315
|
"div",
|
|
81
2316
|
{
|
|
82
2317
|
"data-slot": "bubble",
|
|
@@ -92,8 +2327,8 @@ function BubbleContent({
|
|
|
92
2327
|
className,
|
|
93
2328
|
...props
|
|
94
2329
|
}) {
|
|
95
|
-
const Comp = asChild ?
|
|
96
|
-
return /* @__PURE__ */
|
|
2330
|
+
const Comp = asChild ? Slot2 : "div";
|
|
2331
|
+
return /* @__PURE__ */ jsx12(
|
|
97
2332
|
Comp,
|
|
98
2333
|
{
|
|
99
2334
|
"data-slot": "bubble-content",
|
|
@@ -105,7 +2340,7 @@ function BubbleContent({
|
|
|
105
2340
|
}
|
|
106
2341
|
);
|
|
107
2342
|
}
|
|
108
|
-
var bubbleReactionsVariants =
|
|
2343
|
+
var bubbleReactionsVariants = cva2(
|
|
109
2344
|
"absolute z-10 flex w-fit shrink-0 items-center justify-center gap-1 rounded-full bg-muted px-1.5 py-0.5 text-sm ring-3 ring-card has-[button]:p-0",
|
|
110
2345
|
{
|
|
111
2346
|
variants: {
|
|
@@ -130,7 +2365,7 @@ function BubbleReactions({
|
|
|
130
2365
|
className,
|
|
131
2366
|
...props
|
|
132
2367
|
}) {
|
|
133
|
-
return /* @__PURE__ */
|
|
2368
|
+
return /* @__PURE__ */ jsx12(
|
|
134
2369
|
"div",
|
|
135
2370
|
{
|
|
136
2371
|
"data-slot": "bubble-reactions",
|
|
@@ -143,10 +2378,10 @@ function BubbleReactions({
|
|
|
143
2378
|
}
|
|
144
2379
|
|
|
145
2380
|
// src/ui/marker.tsx
|
|
146
|
-
import { cva as
|
|
147
|
-
import { Slot as
|
|
148
|
-
import { jsx as
|
|
149
|
-
var markerVariants =
|
|
2381
|
+
import { cva as cva3 } from "class-variance-authority";
|
|
2382
|
+
import { Slot as Slot3 } from "@radix-ui/react-slot";
|
|
2383
|
+
import { jsx as jsx13 } from "react/jsx-runtime";
|
|
2384
|
+
var markerVariants = cva3(
|
|
150
2385
|
"group/marker relative flex min-h-4 w-full items-center gap-2 text-left text-sm text-muted-foreground [&_svg:not([class*='size-'])]:size-4 [a]:underline [a]:underline-offset-3 [a]:hover:text-foreground",
|
|
151
2386
|
{
|
|
152
2387
|
variants: {
|
|
@@ -164,8 +2399,8 @@ function Marker({
|
|
|
164
2399
|
asChild = false,
|
|
165
2400
|
...props
|
|
166
2401
|
}) {
|
|
167
|
-
const Comp = asChild ?
|
|
168
|
-
return /* @__PURE__ */
|
|
2402
|
+
const Comp = asChild ? Slot3 : "div";
|
|
2403
|
+
return /* @__PURE__ */ jsx13(
|
|
169
2404
|
Comp,
|
|
170
2405
|
{
|
|
171
2406
|
"data-slot": "marker",
|
|
@@ -176,7 +2411,7 @@ function Marker({
|
|
|
176
2411
|
);
|
|
177
2412
|
}
|
|
178
2413
|
function MarkerIcon({ className, ...props }) {
|
|
179
|
-
return /* @__PURE__ */
|
|
2414
|
+
return /* @__PURE__ */ jsx13(
|
|
180
2415
|
"span",
|
|
181
2416
|
{
|
|
182
2417
|
"data-slot": "marker-icon",
|
|
@@ -190,7 +2425,7 @@ function MarkerIcon({ className, ...props }) {
|
|
|
190
2425
|
);
|
|
191
2426
|
}
|
|
192
2427
|
function MarkerContent({ className, ...props }) {
|
|
193
|
-
return /* @__PURE__ */
|
|
2428
|
+
return /* @__PURE__ */ jsx13(
|
|
194
2429
|
"span",
|
|
195
2430
|
{
|
|
196
2431
|
"data-slot": "marker-content",
|
|
@@ -204,7 +2439,7 @@ function MarkerContent({ className, ...props }) {
|
|
|
204
2439
|
}
|
|
205
2440
|
|
|
206
2441
|
// src/ui/mcp-connect-required-card.tsx
|
|
207
|
-
import { jsx as
|
|
2442
|
+
import { jsx as jsx14, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
208
2443
|
var STATUS_LABELS = {
|
|
209
2444
|
pending: "Connect",
|
|
210
2445
|
connecting: "Connecting",
|
|
@@ -228,7 +2463,7 @@ function McpConnectRequiredCard({
|
|
|
228
2463
|
const isBusy = loading || status === "connecting" || status === "resuming";
|
|
229
2464
|
const isConnected = status === "connected";
|
|
230
2465
|
const buttonDisabled = disabled || isBusy || isConnected || !onConnect;
|
|
231
|
-
return /* @__PURE__ */
|
|
2466
|
+
return /* @__PURE__ */ jsx14(
|
|
232
2467
|
"div",
|
|
233
2468
|
{
|
|
234
2469
|
"data-agents24-mcp-connect-card": true,
|
|
@@ -238,13 +2473,13 @@ function McpConnectRequiredCard({
|
|
|
238
2473
|
className
|
|
239
2474
|
),
|
|
240
2475
|
...props,
|
|
241
|
-
children: /* @__PURE__ */
|
|
242
|
-
/* @__PURE__ */
|
|
243
|
-
/* @__PURE__ */
|
|
244
|
-
/* @__PURE__ */
|
|
245
|
-
status === "error" && error ? /* @__PURE__ */
|
|
2476
|
+
children: /* @__PURE__ */ jsxs8("div", { className: "flex min-w-0 items-start justify-between gap-3", children: [
|
|
2477
|
+
/* @__PURE__ */ jsxs8("div", { className: "min-w-0", children: [
|
|
2478
|
+
/* @__PURE__ */ jsx14("div", { className: "truncate text-sm font-medium", children: label }),
|
|
2479
|
+
/* @__PURE__ */ jsx14("div", { className: "mt-1 text-sm text-muted-foreground", children: message || "Connect your account to continue." }),
|
|
2480
|
+
status === "error" && error ? /* @__PURE__ */ jsx14("div", { className: "mt-2 text-sm text-destructive", children: error }) : null
|
|
246
2481
|
] }),
|
|
247
|
-
/* @__PURE__ */
|
|
2482
|
+
/* @__PURE__ */ jsx14(
|
|
248
2483
|
"button",
|
|
249
2484
|
{
|
|
250
2485
|
type: "button",
|
|
@@ -261,13 +2496,86 @@ function McpConnectRequiredCard({
|
|
|
261
2496
|
}
|
|
262
2497
|
);
|
|
263
2498
|
}
|
|
2499
|
+
|
|
2500
|
+
// src/ui/use-audio-recorder.ts
|
|
2501
|
+
import * as React10 from "react";
|
|
2502
|
+
var RECORDING_CANDIDATES = ["audio/webm;codecs=opus", "audio/webm", "audio/mp4"];
|
|
2503
|
+
function recordingExtension(mimeType) {
|
|
2504
|
+
return mimeType.startsWith("audio/mp4") ? "m4a" : "webm";
|
|
2505
|
+
}
|
|
2506
|
+
function useAudioRecorder(onRecorded, options = {}) {
|
|
2507
|
+
const recorderRef = React10.useRef(null);
|
|
2508
|
+
const streamRef = React10.useRef(null);
|
|
2509
|
+
const onRecordedRef = React10.useRef(onRecorded);
|
|
2510
|
+
const onStreamChangeRef = React10.useRef(options.onStreamChange);
|
|
2511
|
+
onRecordedRef.current = onRecorded;
|
|
2512
|
+
onStreamChangeRef.current = options.onStreamChange;
|
|
2513
|
+
const [isRecording, setIsRecording] = React10.useState(false);
|
|
2514
|
+
const [error, setError] = React10.useState(null);
|
|
2515
|
+
const stop = React10.useCallback(() => recorderRef.current?.stop(), []);
|
|
2516
|
+
const start = React10.useCallback(async () => {
|
|
2517
|
+
setError(null);
|
|
2518
|
+
if (!navigator.mediaDevices?.getUserMedia || typeof MediaRecorder === "undefined") {
|
|
2519
|
+
setError("Audio recording is not supported in this browser.");
|
|
2520
|
+
return;
|
|
2521
|
+
}
|
|
2522
|
+
try {
|
|
2523
|
+
const mimeType = RECORDING_CANDIDATES.find((candidate) => MediaRecorder.isTypeSupported(candidate));
|
|
2524
|
+
if (!mimeType) {
|
|
2525
|
+
setError("Audio recording is not supported in this browser.");
|
|
2526
|
+
return;
|
|
2527
|
+
}
|
|
2528
|
+
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
2529
|
+
onStreamChangeRef.current?.(stream);
|
|
2530
|
+
const recorder = new MediaRecorder(stream, { mimeType });
|
|
2531
|
+
const chunks = [];
|
|
2532
|
+
recorder.ondataavailable = (event) => {
|
|
2533
|
+
if (event.data.size) chunks.push(event.data);
|
|
2534
|
+
};
|
|
2535
|
+
recorder.onstop = () => {
|
|
2536
|
+
const actualType = recorder.mimeType || "audio/webm";
|
|
2537
|
+
onRecordedRef.current(new File([new Blob(chunks, { type: actualType })], `recording-${Date.now()}.${recordingExtension(actualType)}`, { type: actualType }));
|
|
2538
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
2539
|
+
onStreamChangeRef.current?.(null);
|
|
2540
|
+
recorderRef.current = null;
|
|
2541
|
+
streamRef.current = null;
|
|
2542
|
+
setIsRecording(false);
|
|
2543
|
+
};
|
|
2544
|
+
recorderRef.current = recorder;
|
|
2545
|
+
streamRef.current = stream;
|
|
2546
|
+
recorder.start();
|
|
2547
|
+
setIsRecording(true);
|
|
2548
|
+
} catch {
|
|
2549
|
+
streamRef.current?.getTracks().forEach((track) => track.stop());
|
|
2550
|
+
onStreamChangeRef.current?.(null);
|
|
2551
|
+
setError("Microphone access is unavailable.");
|
|
2552
|
+
}
|
|
2553
|
+
}, []);
|
|
2554
|
+
React10.useEffect(() => () => {
|
|
2555
|
+
recorderRef.current?.stop();
|
|
2556
|
+
streamRef.current?.getTracks().forEach((track) => track.stop());
|
|
2557
|
+
onStreamChangeRef.current?.(null);
|
|
2558
|
+
}, []);
|
|
2559
|
+
return { error, isRecording, start, stop };
|
|
2560
|
+
}
|
|
264
2561
|
export {
|
|
2562
|
+
AgentChatActivityRow,
|
|
265
2563
|
AgentChatComposer,
|
|
2564
|
+
AgentChatContextCompressionRow,
|
|
2565
|
+
AgentChatContextStatus,
|
|
266
2566
|
AgentChatDefaultActions,
|
|
267
2567
|
AgentChatMessage,
|
|
268
|
-
|
|
2568
|
+
AgentChatModelTurnRow,
|
|
2569
|
+
AgentChatNodeActivityRow,
|
|
2570
|
+
AgentChatPart,
|
|
2571
|
+
AgentChatSubagentGroup,
|
|
2572
|
+
AgentChatToolGroup,
|
|
2573
|
+
AgentChatToolPart,
|
|
2574
|
+
AgentChatToolRow,
|
|
2575
|
+
AgentChatToolSession,
|
|
269
2576
|
AgentChatUserMessageContent,
|
|
270
2577
|
AgentResponseTimeline,
|
|
2578
|
+
AskUserInteraction,
|
|
271
2579
|
Attachment,
|
|
272
2580
|
AttachmentAction,
|
|
273
2581
|
AttachmentActions,
|
|
@@ -304,14 +2612,21 @@ export {
|
|
|
304
2612
|
MessageResponse,
|
|
305
2613
|
Message as ShadcnMessage,
|
|
306
2614
|
MessageContent as ShadcnMessageContent,
|
|
2615
|
+
agentChatToolLabel,
|
|
307
2616
|
attachmentMediaVariants,
|
|
308
2617
|
attachmentVariants,
|
|
309
2618
|
bubbleVariants,
|
|
310
2619
|
cn,
|
|
2620
|
+
contextStatusMetrics,
|
|
311
2621
|
hitlActionLabel,
|
|
312
2622
|
hitlTitle,
|
|
2623
|
+
isAgentChatSubagent,
|
|
2624
|
+
isAgentChatToolRunning,
|
|
2625
|
+
latestRunningAgentChatToolId,
|
|
313
2626
|
markerVariants,
|
|
2627
|
+
partitionAgentChatToolTimeline,
|
|
314
2628
|
resolvedHitlLabel,
|
|
315
|
-
shouldCollapseUserMessage
|
|
2629
|
+
shouldCollapseUserMessage,
|
|
2630
|
+
useAudioRecorder
|
|
316
2631
|
};
|
|
317
2632
|
//# sourceMappingURL=index.js.map
|