@agents24/chat-react 0.3.2 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/LICENSE +19 -0
  2. package/README.md +24 -64
  3. package/compatibility.json +54 -0
  4. package/dist/active-content.d.ts +10 -0
  5. package/dist/{chunk-EWZO4QJI.js → chunk-CHAU7FNW.js} +3 -3
  6. package/dist/chunk-CHAU7FNW.js.map +1 -0
  7. package/dist/{chunk-FFO6KEB4.js → chunk-LI67M74F.js} +98 -12
  8. package/dist/chunk-LI67M74F.js.map +1 -0
  9. package/dist/chunk-R7WFL3YR.js +1327 -0
  10. package/dist/chunk-R7WFL3YR.js.map +1 -0
  11. package/dist/index.cjs +66 -1852
  12. package/dist/index.cjs.map +1 -1
  13. package/dist/index.d.ts +0 -6
  14. package/dist/index.js +6 -1841
  15. package/dist/index.js.map +1 -1
  16. package/dist/latest-thread-scroller.d.ts +7 -7
  17. package/dist/mcp-oauth.d.ts +3 -1
  18. package/dist/renderers.d.ts +3 -3
  19. package/dist/styles.css +493 -0
  20. package/dist/templates/agent-chat-app.d.ts +18 -0
  21. package/dist/templates/agent-chat-layout.d.ts +77 -0
  22. package/dist/templates/agent-chat-shell.d.ts +1 -1
  23. package/dist/templates/appearance-control.d.ts +5 -0
  24. package/dist/templates/index.cjs +2028 -18
  25. package/dist/templates/index.cjs.map +1 -1
  26. package/dist/templates/index.d.ts +3 -0
  27. package/dist/templates/index.js +652 -11
  28. package/dist/templates/index.js.map +1 -1
  29. package/dist/types.d.ts +12 -380
  30. package/dist/ui/agent-chat-actions.d.ts +1 -2
  31. package/dist/ui/agent-chat-composer.d.ts +2 -1
  32. package/dist/ui/agent-chat-message.d.ts +3 -3
  33. package/dist/ui/agent-response-timeline.d.ts +2 -2
  34. package/dist/ui/attachment.d.ts +9 -9
  35. package/dist/ui/bubble.d.ts +4 -4
  36. package/dist/ui/index.cjs +138 -44
  37. package/dist/ui/index.cjs.map +1 -1
  38. package/dist/ui/index.js +62 -1220
  39. package/dist/ui/index.js.map +1 -1
  40. package/dist/ui/marker.d.ts +3 -3
  41. package/dist/ui/mcp-connect-required-card.d.ts +1 -1
  42. package/dist/ui/message-response.d.ts +1 -1
  43. package/dist/ui/message.d.ts +6 -6
  44. package/package.json +27 -8
  45. package/dist/chunk-EWZO4QJI.js.map +0 -1
  46. package/dist/chunk-FFO6KEB4.js.map +0 -1
  47. package/dist/chunk-UU7OXHL3.js +0 -11
  48. package/dist/chunk-UU7OXHL3.js.map +0 -1
  49. package/dist/context-window.d.ts +0 -38
  50. package/dist/controller-actions.d.ts +0 -45
  51. package/dist/controller-helpers.d.ts +0 -27
  52. package/dist/controller-hitl.d.ts +0 -22
  53. package/dist/controller-options.d.ts +0 -15
  54. package/dist/controller-thread-events.d.ts +0 -7
  55. package/dist/controller.d.ts +0 -5
  56. package/dist/message-lifecycle.d.ts +0 -15
  57. package/dist/model.d.ts +0 -30
  58. package/dist/sse.d.ts +0 -5
  59. package/dist/transport.d.ts +0 -43
package/dist/ui/index.js CHANGED
@@ -1,1206 +1,48 @@
1
1
  import {
2
- useStreamingText
3
- } from "../chunk-EWZO4QJI.js";
4
- import {
5
- cn
6
- } from "../chunk-UU7OXHL3.js";
7
-
8
- // src/ui/agent-chat-composer.tsx
9
- import * as React from "react";
10
- import { ArrowUp, Paperclip, Plus, Square, X } from "lucide-react";
11
- import { jsx, jsxs } from "react/jsx-runtime";
12
- var createId = () => typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : `file-${Date.now()}-${Math.random().toString(36).slice(2)}`;
13
- function createComposerFile(file) {
14
- return {
15
- id: createId(),
16
- type: "file",
17
- url: URL.createObjectURL(file),
18
- filename: file.name || "attachment",
19
- mediaType: file.type || "application/octet-stream"
20
- };
21
- }
22
- function revokeComposerFiles(files) {
23
- files.forEach((file) => {
24
- try {
25
- URL.revokeObjectURL(file.url);
26
- } catch {
27
- }
28
- });
29
- }
30
- function formatFileCount(files) {
31
- if (files.length === 1) return files[0].filename;
32
- return `${files.length} files`;
33
- }
34
- var pillButtonClass = "inline-flex size-8 shrink-0 items-center justify-center gap-2 rounded-full border-0 bg-neutral-100 text-sm text-neutral-600 shadow-none transition-colors hover:bg-neutral-200 hover:text-neutral-800 focus-visible:border-neutral-300 focus-visible:outline-none focus-visible:ring-0 disabled:pointer-events-none disabled:opacity-50 dark:bg-neutral-800 dark:text-neutral-300 dark:hover:bg-neutral-700";
35
- var submitButtonClass = "inline-flex size-9 shrink-0 items-center justify-center gap-2 rounded-full border-0 bg-neutral-900 text-sm text-white shadow-none transition-all duration-200 hover:bg-neutral-800 hover:text-white focus-visible:border-neutral-300 focus-visible:outline-none focus-visible:ring-0 disabled:pointer-events-none disabled:opacity-50 dark:bg-neutral-100 dark:text-neutral-900 dark:hover:bg-white";
36
- function AgentChatComposer({
37
- accept,
38
- allowAttachments = true,
39
- attachmentLayout = "flow",
40
- className,
41
- disabled = false,
42
- inputToolbarContent,
43
- isRunning = false,
44
- onAttachmentCountChange,
45
- onStop,
46
- onSubmit,
47
- placeholder = "Send follow-up",
48
- textareaRef
49
- }) {
50
- const fileInputRef = React.useRef(null);
51
- const filesRef = React.useRef([]);
52
- const localTextareaRef = React.useRef(null);
53
- const [text, setText] = React.useState("");
54
- const [files, setFiles] = React.useState([]);
55
- const [isSubmitting, setIsSubmitting] = React.useState(false);
56
- const isExpanded = text.includes("\n") || text.length > 62;
57
- const setTextareaRef = React.useCallback(
58
- (node) => {
59
- localTextareaRef.current = node;
60
- if (typeof textareaRef === "function") {
61
- textareaRef(node);
62
- } else if (textareaRef) {
63
- textareaRef.current = node;
64
- }
65
- },
66
- [textareaRef]
67
- );
68
- React.useEffect(() => {
69
- filesRef.current = files;
70
- onAttachmentCountChange?.(files.length);
71
- }, [files, onAttachmentCountChange]);
72
- React.useEffect(() => () => revokeComposerFiles(filesRef.current), []);
73
- React.useLayoutEffect(() => {
74
- const textarea = localTextareaRef.current;
75
- if (!textarea) return;
76
- if (isExpanded) {
77
- textarea.style.height = "auto";
78
- const scrollHeight = textarea.scrollHeight;
79
- textarea.style.height = `${Math.min(scrollHeight, 224)}px`;
80
- textarea.style.overflowY = scrollHeight >= 224 ? "auto" : "hidden";
81
- return;
82
- }
83
- textarea.style.height = "";
84
- textarea.style.overflowY = "hidden";
85
- }, [isExpanded, text]);
86
- const canSubmit = !disabled && !isSubmitting && !isRunning && (text.trim().length > 0 || files.length > 0);
87
- const handleFilesChange = (event) => {
88
- const selected = Array.from(event.target.files || []).map(createComposerFile);
89
- if (selected.length > 0) {
90
- setFiles((current) => [...current, ...selected]);
91
- }
92
- event.currentTarget.value = "";
93
- };
94
- const removeFile = (fileId) => {
95
- setFiles((current) => {
96
- const target = current.find((file) => file.id === fileId);
97
- if (target) revokeComposerFiles([target]);
98
- return current.filter((file) => file.id !== fileId);
99
- });
100
- };
101
- const submit = async () => {
102
- if (!canSubmit) return;
103
- const submittedText = text.trim();
104
- const submittedFiles = files;
105
- setIsSubmitting(true);
106
- try {
107
- await onSubmit({ text: submittedText, files: submittedFiles });
108
- setText("");
109
- setFiles([]);
110
- revokeComposerFiles(submittedFiles);
111
- } finally {
112
- setIsSubmitting(false);
113
- }
114
- };
115
- const handleKeyDown = (event) => {
116
- if (event.key === "Enter" && !event.shiftKey) {
117
- event.preventDefault();
118
- void submit();
119
- }
120
- };
121
- return /* @__PURE__ */ jsxs("div", { className: cn("relative mx-auto w-full max-w-3xl overflow-visible", className), children: [
122
- files.length > 0 ? /* @__PURE__ */ jsx(
123
- "div",
124
- {
125
- "data-testid": "agent-chat-composer-attachments",
126
- className: cn(
127
- "flex min-w-0 gap-2 py-1",
128
- 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"
129
- ),
130
- children: files.map((file) => /* @__PURE__ */ jsxs(
131
- "div",
132
- {
133
- className: "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-neutral-200 bg-white px-1.5 text-sm font-medium text-foreground transition-colors hover:bg-neutral-50",
134
- children: [
135
- /* @__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" }) }),
136
- /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate", children: file.filename }),
137
- /* @__PURE__ */ jsx(
138
- "button",
139
- {
140
- "aria-label": `Remove ${file.filename}`,
141
- className: "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",
142
- onClick: () => removeFile(file.id),
143
- type: "button",
144
- children: /* @__PURE__ */ jsx(X, { className: "size-3" })
145
- }
146
- )
147
- ]
148
- },
149
- file.id
150
- ))
151
- }
152
- ) : null,
153
- /* @__PURE__ */ jsx(
154
- "div",
155
- {
156
- className: cn(
157
- "relative w-full border border-neutral-200 bg-white shadow-sm transition-colors duration-200 focus-within:border-neutral-300 dark:border-neutral-700 dark:bg-card dark:focus-within:border-neutral-600",
158
- isExpanded ? "rounded-[18px] p-2 pb-1.5" : "rounded-full px-2 py-[3px]"
159
- ),
160
- children: /* @__PURE__ */ jsxs(
161
- "div",
162
- {
163
- className: "w-full",
164
- style: {
165
- alignItems: "center",
166
- display: "grid",
167
- gridTemplateAreas: isExpanded ? `"textarea textarea textarea" "plus toolbar submit"` : `"plus textarea composer mic submit"`,
168
- gridTemplateColumns: isExpanded ? "auto 1fr auto" : "auto 1fr auto auto auto",
169
- gap: isExpanded ? "3px 10px" : "8px"
170
- },
171
- children: [
172
- /* @__PURE__ */ jsxs("div", { style: { gridArea: "plus" }, className: "flex shrink-0 items-center justify-center", children: [
173
- /* @__PURE__ */ jsx(
174
- "input",
175
- {
176
- ref: fileInputRef,
177
- accept,
178
- "aria-label": "Attach files",
179
- className: "hidden",
180
- multiple: true,
181
- onChange: handleFilesChange,
182
- type: "file"
183
- }
184
- ),
185
- /* @__PURE__ */ jsx(
186
- "button",
187
- {
188
- "aria-label": "Attach files",
189
- className: pillButtonClass,
190
- disabled: disabled || isSubmitting || isRunning || !allowAttachments,
191
- onClick: () => fileInputRef.current?.click(),
192
- title: "Attach files",
193
- type: "button",
194
- children: /* @__PURE__ */ jsx(Plus, { className: "size-4" })
195
- }
196
- )
197
- ] }),
198
- /* @__PURE__ */ jsx("div", { style: { gridArea: "textarea" }, className: "min-w-0 w-full", children: /* @__PURE__ */ jsx(
199
- "textarea",
200
- {
201
- ref: setTextareaRef,
202
- "aria-label": "Message",
203
- className: cn(
204
- "w-full resize-none border-0 bg-transparent px-1 text-[15px] leading-relaxed text-foreground shadow-none outline-none placeholder:text-neutral-400 focus-visible:ring-0 scrollbar-thin",
205
- isExpanded ? "min-h-9 max-h-[224px] py-1" : "min-h-9 h-9 max-h-9 py-2"
206
- ),
207
- disabled: disabled || isSubmitting || isRunning,
208
- onChange: (event) => setText(event.target.value),
209
- onKeyDown: handleKeyDown,
210
- placeholder,
211
- rows: isExpanded ? 2 : 1,
212
- value: text
213
- }
214
- ) }),
215
- isExpanded ? /* @__PURE__ */ jsx("div", { style: { gridArea: "toolbar" }, className: "flex min-w-0 items-center gap-1.5", children: inputToolbarContent }) : null,
216
- !isExpanded ? /* @__PURE__ */ jsx("div", { style: { gridArea: "composer" }, className: "flex min-w-0 items-center gap-1.5" }) : null,
217
- !isExpanded ? /* @__PURE__ */ jsx("div", { style: { gridArea: "mic" }, className: "flex shrink-0 items-center justify-end" }) : null,
218
- /* @__PURE__ */ jsx("div", { style: { gridArea: "submit" }, className: "flex shrink-0 items-center justify-end", children: isRunning ? /* @__PURE__ */ jsx(
219
- "button",
220
- {
221
- "aria-label": "Stop generating",
222
- className: submitButtonClass,
223
- disabled: disabled || !onStop,
224
- onClick: onStop,
225
- title: "Stop",
226
- type: "button",
227
- children: /* @__PURE__ */ jsx(Square, { className: "size-3.5 fill-current" })
228
- }
229
- ) : /* @__PURE__ */ jsx(
230
- "button",
231
- {
232
- "aria-label": files.length > 0 ? `Send ${formatFileCount(files)}` : "Send message",
233
- className: cn(submitButtonClass, text.trim().length > 0 && "hover:scale-105"),
234
- disabled: !canSubmit,
235
- onClick: () => void submit(),
236
- title: "Send",
237
- type: "button",
238
- children: /* @__PURE__ */ jsx(ArrowUp, { className: "size-4", strokeWidth: 2.5 })
239
- }
240
- ) })
241
- ]
242
- }
243
- )
244
- }
245
- ),
246
- !isExpanded && inputToolbarContent ? /* @__PURE__ */ jsx("div", { className: "mt-2 flex items-center gap-2 px-1", children: inputToolbarContent }) : null
247
- ] });
248
- }
249
-
250
- // src/ui/agent-chat-actions.tsx
251
- import { Check, Copy, RefreshCcw, ThumbsDown, ThumbsUp } from "lucide-react";
252
-
253
- // src/ui/adapters.ts
254
- import * as React2 from "react";
2
+ AgentChatComposer,
3
+ AgentChatDefaultActions,
4
+ AgentChatMessage,
5
+ AgentChatUserMessageContent,
6
+ AgentResponseTimeline,
7
+ Attachment,
8
+ AttachmentAction,
9
+ AttachmentActions,
10
+ AttachmentContent,
11
+ AttachmentDescription,
12
+ AttachmentGroup,
13
+ AttachmentMedia,
14
+ AttachmentTitle,
15
+ AttachmentTrigger,
16
+ ChatAttachment,
17
+ ChatAttachments,
18
+ ChatMessage,
19
+ ChatMessageAction,
20
+ ChatMessageActions,
21
+ ChatMessageContent,
22
+ DefaultPartRow,
23
+ Message,
24
+ MessageAvatar,
25
+ MessageContent,
26
+ MessageFooter,
27
+ MessageGroup,
28
+ MessageHeader,
29
+ MessageResponse,
30
+ attachmentMediaVariants,
31
+ attachmentVariants,
32
+ cn,
33
+ hitlActionLabel,
34
+ hitlTitle,
35
+ resolvedHitlLabel,
36
+ shouldCollapseUserMessage
37
+ } from "../chunk-R7WFL3YR.js";
38
+ import "../chunk-CHAU7FNW.js";
255
39
 
256
- // src/ui/attachment.tsx
40
+ // src/ui/bubble.tsx
257
41
  import { cva } from "class-variance-authority";
258
42
  import { Slot } from "@radix-ui/react-slot";
259
- import { jsx as jsx2 } from "react/jsx-runtime";
260
- var attachmentVariants = cva(
261
- "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",
262
- {
263
- variants: {
264
- size: {
265
- 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",
266
- 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",
267
- 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"
268
- },
269
- orientation: {
270
- horizontal: "min-w-40 items-center",
271
- vertical: "w-24 flex-col has-data-[slot=attachment-content]:w-30"
272
- }
273
- }
274
- }
275
- );
276
- function Attachment({
277
- className,
278
- state = "done",
279
- size = "default",
280
- orientation = "horizontal",
281
- ...props
282
- }) {
283
- return /* @__PURE__ */ jsx2(
284
- "div",
285
- {
286
- "data-slot": "attachment",
287
- "data-state": state,
288
- "data-size": size,
289
- "data-orientation": orientation,
290
- className: cn(attachmentVariants({ size, orientation }), className),
291
- ...props
292
- }
293
- );
294
- }
295
- var attachmentMediaVariants = cva(
296
- "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",
297
- {
298
- variants: {
299
- variant: {
300
- icon: "",
301
- 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"
302
- }
303
- },
304
- defaultVariants: {
305
- variant: "icon"
306
- }
307
- }
308
- );
309
- function AttachmentMedia({
310
- className,
311
- variant = "icon",
312
- ...props
313
- }) {
314
- return /* @__PURE__ */ jsx2(
315
- "div",
316
- {
317
- "data-slot": "attachment-media",
318
- "data-variant": variant,
319
- className: cn(attachmentMediaVariants({ variant }), className),
320
- ...props
321
- }
322
- );
323
- }
324
- function AttachmentContent({
325
- className,
326
- ...props
327
- }) {
328
- return /* @__PURE__ */ jsx2(
329
- "div",
330
- {
331
- "data-slot": "attachment-content",
332
- className: cn(
333
- "max-w-full min-w-0 flex-1 leading-tight group-data-[orientation=vertical]/attachment:px-1",
334
- className
335
- ),
336
- ...props
337
- }
338
- );
339
- }
340
- function AttachmentTitle({
341
- className,
342
- ...props
343
- }) {
344
- return /* @__PURE__ */ jsx2(
345
- "span",
346
- {
347
- "data-slot": "attachment-title",
348
- className: cn(
349
- "block max-w-full min-w-0 truncate font-medium group-data-[state=processing]/attachment:shimmer group-data-[state=uploading]/attachment:shimmer",
350
- className
351
- ),
352
- ...props
353
- }
354
- );
355
- }
356
- function AttachmentDescription({
357
- className,
358
- ...props
359
- }) {
360
- return /* @__PURE__ */ jsx2(
361
- "span",
362
- {
363
- "data-slot": "attachment-description",
364
- className: cn(
365
- "mt-0.5 block min-w-0 truncate text-xs text-muted-foreground group-data-[state=error]/attachment:text-destructive/80",
366
- "max-w-full",
367
- className
368
- ),
369
- ...props
370
- }
371
- );
372
- }
373
- function AttachmentActions({
374
- className,
375
- ...props
376
- }) {
377
- return /* @__PURE__ */ jsx2(
378
- "div",
379
- {
380
- "data-slot": "attachment-actions",
381
- className: cn(
382
- "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",
383
- className
384
- ),
385
- ...props
386
- }
387
- );
388
- }
389
- function AttachmentAction({
390
- className,
391
- type = "button",
392
- ...props
393
- }) {
394
- return /* @__PURE__ */ jsx2(
395
- "button",
396
- {
397
- "data-slot": "attachment-action",
398
- type,
399
- className: cn(
400
- "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",
401
- className
402
- ),
403
- ...props
404
- }
405
- );
406
- }
407
- function AttachmentTrigger({
408
- className,
409
- asChild = false,
410
- type,
411
- ...props
412
- }) {
413
- const Comp = asChild ? Slot : "button";
414
- return /* @__PURE__ */ jsx2(
415
- Comp,
416
- {
417
- "data-slot": "attachment-trigger",
418
- type: asChild ? void 0 : type ?? "button",
419
- className: cn("absolute inset-0 z-10 outline-none", className),
420
- ...props
421
- }
422
- );
423
- }
424
- function AttachmentGroup({ className, ...props }) {
425
- return /* @__PURE__ */ jsx2(
426
- "div",
427
- {
428
- "data-slot": "attachment-group",
429
- className: cn(
430
- "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",
431
- className
432
- ),
433
- ...props
434
- }
435
- );
436
- }
437
-
438
- // src/ui/message.tsx
439
- import { jsx as jsx3 } from "react/jsx-runtime";
440
- function MessageGroup({ className, ...props }) {
441
- return /* @__PURE__ */ jsx3(
442
- "div",
443
- {
444
- "data-slot": "message-group",
445
- className: cn("flex min-w-0 flex-col gap-2", className),
446
- ...props
447
- }
448
- );
449
- }
450
- function Message({
451
- className,
452
- align = "start",
453
- ...props
454
- }) {
455
- return /* @__PURE__ */ jsx3(
456
- "div",
457
- {
458
- "data-slot": "message",
459
- "data-align": align,
460
- className: cn(
461
- "group/message relative flex w-full min-w-0 gap-2 text-sm data-[align=end]:flex-row-reverse",
462
- className
463
- ),
464
- ...props
465
- }
466
- );
467
- }
468
- function MessageAvatar({ className, ...props }) {
469
- return /* @__PURE__ */ jsx3(
470
- "div",
471
- {
472
- "data-slot": "message-avatar",
473
- className: cn(
474
- "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",
475
- className
476
- ),
477
- ...props
478
- }
479
- );
480
- }
481
- function MessageContent({ className, ...props }) {
482
- return /* @__PURE__ */ jsx3(
483
- "div",
484
- {
485
- "data-slot": "message-content",
486
- className: cn(
487
- "flex w-full min-w-0 flex-col gap-2.5 wrap-break-word group-data-[align=end]/message:*:data-slot:self-end",
488
- className
489
- ),
490
- ...props
491
- }
492
- );
493
- }
494
- function MessageHeader({ className, ...props }) {
495
- return /* @__PURE__ */ jsx3(
496
- "div",
497
- {
498
- "data-slot": "message-header",
499
- className: cn(
500
- "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",
501
- className
502
- ),
503
- ...props
504
- }
505
- );
506
- }
507
- function MessageFooter({ className, ...props }) {
508
- return /* @__PURE__ */ jsx3(
509
- "div",
510
- {
511
- "data-slot": "message-footer",
512
- className: cn(
513
- "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",
514
- className
515
- ),
516
- ...props
517
- }
518
- );
519
- }
520
-
521
- // src/ui/adapters.ts
522
- function ChatMessage(input) {
523
- const { className, from, align, ...props } = input;
524
- const isUser = from === "user";
525
- const isRtl = props.dir === "rtl";
526
- const resolvedAlign = align || "start";
527
- const messageProps = Object.assign({}, props);
528
- messageProps.align = resolvedAlign;
529
- messageProps.className = cn(
530
- "group flex w-full max-w-[80%] flex-col gap-2",
531
- isUser ? cn("is-user justify-end", isRtl ? "mr-auto" : "ml-auto") : "is-assistant",
532
- className
533
- );
534
- return React2.createElement(Message, messageProps);
535
- }
536
- function ChatMessageContent({
537
- className,
538
- ...props
539
- }) {
540
- return React2.createElement("div", {
541
- className: cn(
542
- "is-user:dark flex w-fit max-w-full min-w-0 flex-col gap-2 overflow-hidden text-sm",
543
- "group-[.is-user]:rounded-lg group-[.is-user]:bg-secondary group-[.is-user]:px-4 group-[.is-user]:py-3 group-[.is-user]:text-foreground",
544
- "group-[.is-assistant]:w-full group-[.is-assistant]:max-w-full group-[.is-assistant]:text-foreground",
545
- props.dir === "rtl" ? "group-[.is-user]:mr-auto" : "group-[.is-user]:ml-auto",
546
- className
547
- ),
548
- "data-slot": "message-content",
549
- ...props
550
- });
551
- }
552
- function ChatMessageActions({
553
- className,
554
- ...props
555
- }) {
556
- return React2.createElement("div", {
557
- className: cn("flex items-center gap-1", className),
558
- "data-slot": "message-actions",
559
- ...props
560
- });
561
- }
562
- function ChatMessageAction({
563
- children,
564
- className,
565
- label,
566
- tooltip,
567
- type = "button",
568
- ...props
569
- }) {
570
- const tooltipLabel = typeof tooltip === "string" ? tooltip : void 0;
571
- return React2.createElement(
572
- "button",
573
- {
574
- "aria-label": label || tooltipLabel,
575
- className: cn(
576
- "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",
577
- className
578
- ),
579
- title: tooltipLabel,
580
- type,
581
- ...props
582
- },
583
- children,
584
- React2.createElement(
585
- "span",
586
- { className: "sr-only" },
587
- label || tooltipLabel
588
- )
589
- );
590
- }
591
- function ChatAttachment({
592
- data,
593
- onRemove,
594
- orientation,
595
- ...props
596
- }) {
597
- const mediaType = data.mediaType || data.contentType || String(data.type || "application/octet-stream");
598
- const filename = data.filename || data.name || "Attachment";
599
- const isImage = mediaType.startsWith("image/") && Boolean(data.url);
600
- return React2.createElement(
601
- Attachment,
602
- {
603
- orientation: orientation || (isImage ? "vertical" : "horizontal"),
604
- ...props
605
- },
606
- React2.createElement(
607
- AttachmentMedia,
608
- { variant: isImage ? "image" : "icon" },
609
- isImage ? React2.createElement("img", {
610
- alt: filename,
611
- className: "size-full object-cover",
612
- src: data.url
613
- }) : React2.createElement(
614
- "span",
615
- { "aria-hidden": "true", className: "text-sm" },
616
- filename.slice(0, 1).toUpperCase()
617
- )
618
- ),
619
- React2.createElement(
620
- AttachmentContent,
621
- null,
622
- React2.createElement(AttachmentTitle, null, filename),
623
- React2.createElement(AttachmentDescription, null, mediaType)
624
- ),
625
- onRemove ? React2.createElement(
626
- "button",
627
- {
628
- "aria-label": "Remove attachment",
629
- 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",
630
- onClick: (event) => {
631
- event.stopPropagation();
632
- onRemove();
633
- },
634
- type: "button"
635
- },
636
- React2.createElement("span", { "aria-hidden": "true" }, "x")
637
- ) : null
638
- );
639
- }
640
- function ChatAttachments({
641
- children,
642
- className,
643
- ...props
644
- }) {
645
- if (!children) return null;
646
- return React2.createElement(
647
- AttachmentGroup,
648
- {
649
- className: cn(
650
- props.dir === "rtl" ? "mr-auto" : "ml-auto",
651
- "w-fit flex-wrap overflow-visible",
652
- className
653
- ),
654
- ...props
655
- },
656
- children
657
- );
658
- }
659
-
660
- // src/ui/agent-chat-actions.tsx
661
- import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
662
- function AgentChatDefaultActions({
663
- handlers,
664
- message
665
- }) {
666
- if (!handlers || message.role !== "assistant" || message.isFinal === false) return null;
667
- return /* @__PURE__ */ jsxs2(ChatMessageActions, { children: [
668
- handlers.onRetry ? /* @__PURE__ */ jsx4(
669
- ChatMessageAction,
670
- {
671
- label: "Retry",
672
- onClick: () => void handlers.onRetry?.(message),
673
- tooltip: "Regenerate response",
674
- children: /* @__PURE__ */ jsx4(RefreshCcw, { className: "size-4" })
675
- }
676
- ) : null,
677
- handlers.onLike ? /* @__PURE__ */ jsx4(
678
- ChatMessageAction,
679
- {
680
- label: "Like",
681
- onClick: () => void handlers.onLike?.(message),
682
- tooltip: "Like this response",
683
- children: /* @__PURE__ */ jsx4(ThumbsUp, { className: "size-4", fill: handlers.liked ? "currentColor" : "none" })
684
- }
685
- ) : null,
686
- handlers.onDislike ? /* @__PURE__ */ jsx4(
687
- ChatMessageAction,
688
- {
689
- label: "Dislike",
690
- onClick: () => void handlers.onDislike?.(message),
691
- tooltip: "Dislike this response",
692
- children: /* @__PURE__ */ jsx4(ThumbsDown, { className: "size-4", fill: handlers.disliked ? "currentColor" : "none" })
693
- }
694
- ) : null,
695
- handlers.onCopy ? /* @__PURE__ */ jsx4(
696
- ChatMessageAction,
697
- {
698
- label: "Copy",
699
- onClick: () => handlers.onCopy?.(message.content || "", message.id),
700
- tooltip: handlers.copied ? "Copied!" : "Copy to clipboard",
701
- children: handlers.copied ? /* @__PURE__ */ jsx4(Check, { className: "size-4 text-green-600" }) : /* @__PURE__ */ jsx4(Copy, { className: "size-4" })
702
- }
703
- ) : null
704
- ] });
705
- }
706
-
707
- // src/ui/agent-chat-message.tsx
708
- import * as React5 from "react";
709
- import { ChevronDown as ChevronDown2, ChevronUp } from "lucide-react";
710
-
711
- // src/ui/agent-response-timeline.tsx
712
- import * as React4 from "react";
713
- import { ChevronDown, ChevronRight } from "lucide-react";
714
-
715
- // src/ui/message-response.tsx
716
- import * as React3 from "react";
717
- import { defaultRehypePlugins, Streamdown } from "streamdown";
718
- import { Fragment, jsx as jsx5 } from "react/jsx-runtime";
719
- var BLOCK_MARKDOWN_TAGS = /* @__PURE__ */ new Set([
720
- "blockquote",
721
- "div",
722
- "h1",
723
- "h2",
724
- "h3",
725
- "h4",
726
- "h5",
727
- "h6",
728
- "hr",
729
- "ol",
730
- "pre",
731
- "table",
732
- "ul"
733
- ]);
734
- function isMultilineCodeNode(node) {
735
- return node?.tagName === "code" && node.position?.start?.line !== void 0 && node.position?.end?.line !== void 0 && node.position.start.line !== node.position.end.line;
736
- }
737
- function hasBlockMarkdownChild(child) {
738
- if (!React3.isValidElement(child)) {
739
- return false;
740
- }
741
- if (typeof child.type === "string" && BLOCK_MARKDOWN_TAGS.has(child.type)) {
742
- return true;
743
- }
744
- const props = child.props;
745
- if (BLOCK_MARKDOWN_TAGS.has(props.node?.tagName || "") || isMultilineCodeNode(props.node)) {
746
- return true;
747
- }
748
- return React3.Children.toArray(props.children).some(hasBlockMarkdownChild);
749
- }
750
- function MessageMarkdownParagraph({
751
- children,
752
- ...props
753
- }) {
754
- const domProps = { ...props };
755
- delete domProps.node;
756
- const childArray = React3.Children.toArray(children).filter(
757
- (child) => child !== null && child !== ""
758
- );
759
- const isImageOnly = childArray.length === 1 && React3.isValidElement(childArray[0]) && childArray[0].props.node?.tagName === "img";
760
- if (isImageOnly) {
761
- return /* @__PURE__ */ jsx5(Fragment, { children });
762
- }
763
- if (childArray.some(hasBlockMarkdownChild)) {
764
- return /* @__PURE__ */ jsx5("div", { ...domProps, children });
765
- }
766
- return /* @__PURE__ */ jsx5("p", { ...domProps, children });
767
- }
768
- var MESSAGE_RESPONSE_COMPONENTS = {
769
- p: MessageMarkdownParagraph
770
- };
771
- var streamdownDefaultRehypePlugins = defaultRehypePlugins || {};
772
- var streamdownHardenPlugin = Array.isArray(
773
- streamdownDefaultRehypePlugins.harden
774
- ) ? streamdownDefaultRehypePlugins.harden[0] : streamdownDefaultRehypePlugins.harden;
775
- var MESSAGE_RESPONSE_REHYPE_PLUGINS = [
776
- ...Object.entries(streamdownDefaultRehypePlugins).filter(([key]) => key !== "harden").map(([, plugin]) => plugin),
777
- ...streamdownHardenPlugin ? [
778
- [
779
- streamdownHardenPlugin,
780
- {
781
- allowDataImages: true,
782
- allowedImagePrefixes: ["*"],
783
- allowedLinkPrefixes: ["*"],
784
- allowedProtocols: ["*", "reshet:", "source:"],
785
- defaultOrigin: void 0
786
- }
787
- ]
788
- ] : []
789
- ];
790
- var MessageResponse = React3.memo(
791
- ({
792
- children,
793
- className,
794
- components,
795
- rehypePlugins,
796
- streaming = false,
797
- streamingId,
798
- ...props
799
- }) => {
800
- const text = typeof children === "string" || typeof children === "number" ? String(children) : "";
801
- const streamingText = useStreamingText({
802
- charsPerSecond: 120,
803
- id: streamingId || text,
804
- isStreaming: streaming && text.length > 0,
805
- maxCatchupChars: 32,
806
- text
807
- });
808
- const streamdownComponents = React3.useMemo(
809
- () => ({ ...MESSAGE_RESPONSE_COMPONENTS, ...components }),
810
- [components]
811
- );
812
- return /* @__PURE__ */ jsx5(
813
- Streamdown,
814
- {
815
- className: cn("size-full [&>*:first-child]:mt-0 [&>*:last-child]:mb-0", className),
816
- components: streamdownComponents,
817
- rehypePlugins: rehypePlugins || MESSAGE_RESPONSE_REHYPE_PLUGINS,
818
- ...props,
819
- children: text ? streamingText.displayedText : children
820
- }
821
- );
822
- },
823
- (prevProps, nextProps) => prevProps.children === nextProps.children && prevProps.className === nextProps.className && prevProps.streaming === nextProps.streaming && prevProps.streamingId === nextProps.streamingId && prevProps.components === nextProps.components
824
- );
825
- MessageResponse.displayName = "MessageResponse";
826
-
827
- // src/ui/hitl-presentation.ts
828
- function optionalString(value) {
829
- return typeof value === "string" && value.trim() ? value.trim() : null;
830
- }
831
- function hitlActionLabel(part, action) {
832
- if (action === "approve") return ["tool_review", "app_data_permission"].includes(part.hitlKind) ? "Allow once" : "Approve";
833
- if (action === "reject") return ["tool_review", "app_data_permission"].includes(part.hitlKind) ? "Don\u2019t allow" : "Reject";
834
- if (action === "connect") return "Connect";
835
- return "Skip";
836
- }
837
- function hitlTitle(part) {
838
- const presentation = part.presentation || {};
839
- if (part.hitlKind === "tool_review") return `Run ${optionalString(presentation.tool_name) || "this tool"}?`;
840
- if (part.hitlKind === "mcp_auth") return `Connect ${optionalString(presentation.server_name) || "this service"} to continue`;
841
- if (part.hitlKind === "app_data_permission") return "Allow this data change?";
842
- return part.message;
843
- }
844
- function resolvedHitlLabel(part) {
845
- const outcome = String(part.resolution?.outcome || part.status).trim().toLowerCase();
846
- return {
847
- approved: part.hitlKind === "user_approval" ? "Approved" : "Allowed",
848
- rejected: part.hitlKind === "user_approval" ? "Rejected" : "Not allowed",
849
- connected: "Connected",
850
- skipped: "Skipped",
851
- timeout: "Timed out",
852
- expired: "Timed out",
853
- cancelled: "Cancelled",
854
- invalidated: "No longer available",
855
- policy_denied: "Not allowed"
856
- }[outcome] || outcome.replace(/_/g, " ");
857
- }
858
-
859
- // src/ui/agent-response-timeline.tsx
860
- import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
861
- function optionalString2(value) {
862
- return typeof value === "string" && value.trim() ? value.trim() : null;
863
- }
864
- function presentationValue(part, key) {
865
- const presentation = part.presentation || {};
866
- const snakeKey = key.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
867
- return optionalString2(presentation[key]) || optionalString2(presentation[snakeKey]);
868
- }
869
- function groupKey(part) {
870
- return presentationValue(part, "groupKey");
871
- }
872
- function groupLabel(part) {
873
- return presentationValue(part, "groupLabel") || toolLabel(part);
874
- }
875
- function toolLabel(part) {
876
- return presentationValue(part, "activity") || presentationValue(part, "title") || part.toolName || "Tool activity";
877
- }
878
- function toolDetail(part) {
879
- return presentationValue(part, "detail") || part.errorText || null;
880
- }
881
- function isActiveTool(part, activeToolId) {
882
- return part.id === activeToolId || part.toolCallId === activeToolId || part.state === "input-available" || part.state === "input-streaming";
883
- }
884
- function Shimmer({ children, className }) {
885
- return /* @__PURE__ */ jsx6("span", { className: cn("inline-block animate-pulse", className), children });
886
- }
887
- function CompactTask({
888
- children,
889
- defaultOpen = false,
890
- detail,
891
- error,
892
- label,
893
- loading
894
- }) {
895
- const [open, setOpen] = React4.useState(defaultOpen);
896
- const expandable = Boolean(children || detail);
897
- const content = loading ? /* @__PURE__ */ jsx6(Shimmer, { children: label }) : /* @__PURE__ */ jsx6("span", { children: label });
898
- if (!expandable) {
899
- return /* @__PURE__ */ jsx6("div", { className: cn("flex min-w-0 items-center gap-1.5 text-sm", error ? "text-destructive" : "text-muted-foreground"), children: content });
900
- }
901
- return /* @__PURE__ */ jsxs3("div", { className: "w-full", children: [
902
- /* @__PURE__ */ jsxs3(
903
- "button",
904
- {
905
- "aria-expanded": open,
906
- className: cn(
907
- "group flex min-w-0 items-center gap-1.5 rounded-md px-0 py-0.5 text-left text-sm transition-colors hover:text-foreground",
908
- error ? "text-destructive" : "text-muted-foreground"
909
- ),
910
- onClick: () => setOpen((value) => !value),
911
- type: "button",
912
- children: [
913
- content,
914
- open ? /* @__PURE__ */ jsx6(ChevronDown, { className: "size-3" }) : /* @__PURE__ */ jsx6(ChevronRight, { className: "size-3 opacity-70" })
915
- ]
916
- }
917
- ),
918
- open ? /* @__PURE__ */ jsxs3("div", { className: "mt-1 pl-4 text-sm text-muted-foreground", children: [
919
- detail ? /* @__PURE__ */ jsx6("div", { children: detail }) : null,
920
- children
921
- ] }) : null
922
- ] });
923
- }
924
- function ToolPartRow({
925
- activeToolId,
926
- part
927
- }) {
928
- return /* @__PURE__ */ jsx6("div", { "data-agents24-tool-part": part.type, "data-state": part.state, children: /* @__PURE__ */ jsx6(
929
- CompactTask,
930
- {
931
- defaultOpen: part.state === "output-error",
932
- detail: toolDetail(part),
933
- error: part.state === "output-error",
934
- label: toolLabel(part),
935
- loading: isActiveTool(part, activeToolId)
936
- }
937
- ) });
938
- }
939
- function ToolGroup({
940
- activeToolId,
941
- parts
942
- }) {
943
- const loading = parts.some((part) => isActiveTool(part, activeToolId));
944
- return /* @__PURE__ */ jsx6(CompactTask, { label: groupLabel(parts[0]), loading, children: /* @__PURE__ */ jsx6("div", { className: "space-y-1", children: parts.map((part) => /* @__PURE__ */ jsxs3(
945
- "div",
946
- {
947
- className: cn("text-sm", part.state === "output-error" ? "text-destructive" : "text-muted-foreground"),
948
- children: [
949
- isActiveTool(part, activeToolId) ? /* @__PURE__ */ jsx6(Shimmer, { children: toolLabel(part) }) : toolLabel(part),
950
- toolDetail(part) ? /* @__PURE__ */ jsx6("span", { className: "ml-1 text-xs text-muted-foreground/75", children: toolDetail(part) }) : null
951
- ]
952
- },
953
- part.id
954
- )) }) });
955
- }
956
- function HitlPartRow({
957
- getHitlActionState,
958
- isLoading,
959
- onHitlAction,
960
- part
961
- }) {
962
- const state = getHitlActionState?.(part);
963
- const [comment, setComment] = React4.useState("");
964
- const presentation = part.presentation || {};
965
- const requireComment = presentation.require_comment === true;
966
- const isTerminal = part.status !== "pending";
967
- if (isTerminal) {
968
- return /* @__PURE__ */ jsx6("div", { className: "px-1 py-1 text-sm text-muted-foreground", children: resolvedHitlLabel(part) });
969
- }
970
- return /* @__PURE__ */ jsxs3("div", { className: "space-y-2.5 rounded-lg bg-muted/45 px-3 py-2.5", children: [
971
- /* @__PURE__ */ jsx6("div", { className: "text-sm font-medium text-foreground text-pretty", children: hitlTitle(part) }),
972
- requireComment ? /* @__PURE__ */ jsx6(
973
- "textarea",
974
- {
975
- "aria-label": "Approval comment",
976
- className: "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",
977
- onChange: (event) => setComment(event.target.value),
978
- placeholder: "Comment required",
979
- value: comment
980
- }
981
- ) : null,
982
- state?.error ? /* @__PURE__ */ jsx6("div", { className: "text-sm text-destructive", children: state.error }) : null,
983
- /* @__PURE__ */ jsx6("div", { className: "flex flex-wrap items-center gap-1.5", children: part.allowedActions.map((action) => /* @__PURE__ */ jsx6(
984
- "button",
985
- {
986
- "aria-label": hitlActionLabel(part, action),
987
- className: cn(
988
- "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",
989
- 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"
990
- ),
991
- disabled: !onHitlAction || isLoading || state?.status === "connecting" || state?.status === "resuming" || requireComment && !comment.trim(),
992
- onClick: () => onHitlAction?.(part, action, comment.trim() || void 0),
993
- type: "button",
994
- children: hitlActionLabel(part, action)
995
- },
996
- action
997
- )) })
998
- ] });
999
- }
1000
- function DefaultPartRow({
1001
- activeToolId,
1002
- getHitlActionState,
1003
- isLoading,
1004
- message,
1005
- onHitlAction,
1006
- part
1007
- }) {
1008
- if (part.kind === "text") return /* @__PURE__ */ jsx6(MessageResponse, { children: part.text });
1009
- if (part.kind === "tool") return /* @__PURE__ */ jsx6(ToolPartRow, { activeToolId, part });
1010
- if (part.kind === "reasoning") {
1011
- const label = part.text || part.label || "Thinking";
1012
- return part.status === "running" || part.status === "streaming" ? /* @__PURE__ */ jsx6(Shimmer, { className: "text-sm text-muted-foreground", children: label }) : /* @__PURE__ */ jsx6("div", { className: "text-sm text-muted-foreground", children: label });
1013
- }
1014
- if (part.kind === "ui-blocks") {
1015
- if (part.state === "output-error") {
1016
- return /* @__PURE__ */ jsx6("div", { className: "text-sm text-destructive", children: part.errorText || "Failed to render UI content." });
1017
- }
1018
- return /* @__PURE__ */ jsx6(CompactTask, { label: part.state === "input-streaming" ? "Rendering UI block" : "Rendered UI block", loading: part.state === "input-streaming" });
1019
- }
1020
- if (part.kind === "hitl") {
1021
- return /* @__PURE__ */ jsx6(
1022
- HitlPartRow,
1023
- {
1024
- getHitlActionState,
1025
- isLoading,
1026
- onHitlAction,
1027
- part
1028
- }
1029
- );
1030
- }
1031
- if (part.kind === "error") return /* @__PURE__ */ jsx6("div", { className: "text-sm text-destructive", children: part.errorText });
1032
- if (part.kind === "data") return /* @__PURE__ */ jsx6("div", { "data-agents24-data-part": part.name, className: "hidden", children: message.id });
1033
- return null;
1034
- }
1035
- function AgentResponseTimeline({
1036
- getHitlActionState,
1037
- isLoading = false,
1038
- message,
1039
- onHitlAction,
1040
- renderPart
1041
- }) {
1042
- const parts = message.parts || [];
1043
- const activeToolId = [...parts].reverse().find((part) => part.kind === "tool" && (part.state === "input-available" || part.state === "input-streaming"))?.id;
1044
- const rendered = [];
1045
- let index = 0;
1046
- while (index < parts.length) {
1047
- const part = parts[index];
1048
- if (part.kind === "tool" && groupKey(part)) {
1049
- const key = groupKey(part);
1050
- const group = [];
1051
- while (index < parts.length) {
1052
- const candidate = parts[index];
1053
- if (candidate.kind !== "tool" || groupKey(candidate) !== key) break;
1054
- group.push(candidate);
1055
- index += 1;
1056
- }
1057
- rendered.push(group.length === 1 ? /* @__PURE__ */ jsx6(ToolPartRow, { activeToolId, part: group[0] }, group[0].id) : /* @__PURE__ */ jsx6(ToolGroup, { activeToolId, parts: group }, `tool-group-${key}-${group[0].id}`));
1058
- continue;
1059
- }
1060
- rendered.push(
1061
- /* @__PURE__ */ jsx6(React4.Fragment, { children: renderPart ? renderPart(part, message) : /* @__PURE__ */ jsx6(
1062
- DefaultPartRow,
1063
- {
1064
- activeToolId,
1065
- getHitlActionState,
1066
- isLoading,
1067
- message,
1068
- onHitlAction,
1069
- part
1070
- }
1071
- ) }, part.id)
1072
- );
1073
- index += 1;
1074
- }
1075
- if (rendered.length === 0 && message.content) {
1076
- rendered.push(/* @__PURE__ */ jsx6(MessageResponse, { children: message.content }, `${message.id}-fallback`));
1077
- }
1078
- if (rendered.length === 0 && isLoading) {
1079
- rendered.push(/* @__PURE__ */ jsx6("div", { className: "text-sm text-muted-foreground", children: "Thinking" }, `${message.id}-thinking`));
1080
- }
1081
- return /* @__PURE__ */ jsx6("div", { className: "space-y-3", children: rendered });
1082
- }
1083
-
1084
- // src/ui/agent-chat-message.tsx
1085
- import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
1086
- var DEFAULT_USER_MESSAGE_COLLAPSE_THRESHOLD = 360;
1087
- var DEFAULT_USER_MESSAGE_COLLAPSED_LINES = 4;
1088
- function shouldCollapseUserMessage(content, threshold = DEFAULT_USER_MESSAGE_COLLAPSE_THRESHOLD) {
1089
- return typeof threshold === "number" && threshold > 0 && content.length > threshold;
1090
- }
1091
- function AgentChatUserMessageContent({
1092
- collapsedLines = DEFAULT_USER_MESSAGE_COLLAPSED_LINES,
1093
- collapseThreshold = DEFAULT_USER_MESSAGE_COLLAPSE_THRESHOLD,
1094
- content,
1095
- showLessLabel = "Show less",
1096
- showMoreLabel = "Show more"
1097
- }) {
1098
- const [isExpanded, setIsExpanded] = React5.useState(false);
1099
- const contentId = React5.useId();
1100
- const shouldCollapse = shouldCollapseUserMessage(content, collapseThreshold);
1101
- const Icon = isExpanded ? ChevronUp : ChevronDown2;
1102
- const clampedStyle = shouldCollapse && !isExpanded ? {
1103
- WebkitBoxOrient: "vertical",
1104
- WebkitLineClamp: Math.max(1, collapsedLines),
1105
- display: "-webkit-box",
1106
- overflow: "hidden"
1107
- } : void 0;
1108
- return /* @__PURE__ */ jsxs4("div", { className: "flex min-w-0 flex-col gap-3", children: [
1109
- /* @__PURE__ */ jsx7("div", { id: contentId, className: "min-w-0", style: clampedStyle, children: content }),
1110
- shouldCollapse ? /* @__PURE__ */ jsxs4(
1111
- "button",
1112
- {
1113
- "aria-controls": contentId,
1114
- "aria-expanded": isExpanded,
1115
- className: cn(
1116
- "inline-flex w-fit items-center gap-1 rounded-md text-left text-sm font-medium text-muted-foreground transition-colors",
1117
- "hover:text-foreground focus-visible:border-neutral-300 focus-visible:outline-none focus-visible:ring-0"
1118
- ),
1119
- onClick: () => setIsExpanded((current) => !current),
1120
- type: "button",
1121
- children: [
1122
- isExpanded ? showLessLabel : showMoreLabel,
1123
- /* @__PURE__ */ jsx7(Icon, { "aria-hidden": "true", className: "size-4", strokeWidth: 2 })
1124
- ]
1125
- }
1126
- ) : null
1127
- ] });
1128
- }
1129
- function AgentChatMessage({
1130
- actionHandlers,
1131
- actions,
1132
- actionsClassName,
1133
- className,
1134
- contentClassName,
1135
- dir,
1136
- getHitlActionState,
1137
- message,
1138
- onHitlAction,
1139
- renderAssistantContent,
1140
- renderPart,
1141
- renderUserContent,
1142
- showAssistantAttachments = false,
1143
- streamingMessageId,
1144
- userMessageCollapsedLines,
1145
- userMessageContent,
1146
- userMessageCollapseThreshold,
1147
- userMessageShowLessLabel,
1148
- userMessageShowMoreLabel
1149
- }) {
1150
- const isAssistant = message.role === "assistant";
1151
- const isStreaming = isAssistant && message.id === streamingMessageId;
1152
- const resolvedUserContent = userMessageContent ?? message.content;
1153
- const shouldRenderContent = isAssistant || Boolean(renderUserContent || resolvedUserContent);
1154
- const showAttachments = Boolean(message.attachments?.length) && (message.role === "user" || showAssistantAttachments);
1155
- const renderedActions = actions ?? (actionHandlers ? /* @__PURE__ */ jsx7(AgentChatDefaultActions, { handlers: actionHandlers, message }) : null);
1156
- return /* @__PURE__ */ jsxs4(ChatMessage, { className, dir, from: message.role, children: [
1157
- showAttachments ? /* @__PURE__ */ jsx7(ChatAttachments, { className: "mb-2", dir, children: message.attachments?.map((attachment, index) => /* @__PURE__ */ jsx7(
1158
- ChatAttachment,
1159
- {
1160
- data: attachment,
1161
- dir: "ltr"
1162
- },
1163
- String(attachment.url || attachment.filename || index)
1164
- )) }) : null,
1165
- shouldRenderContent ? /* @__PURE__ */ jsxs4(
1166
- ChatMessageContent,
1167
- {
1168
- className: cn(isAssistant ? "bg-transparent p-0" : void 0, contentClassName),
1169
- dir,
1170
- children: [
1171
- isAssistant ? renderAssistantContent ? renderAssistantContent({ isStreaming, message }) : /* @__PURE__ */ jsx7(
1172
- AgentResponseTimeline,
1173
- {
1174
- getHitlActionState,
1175
- isLoading: isStreaming,
1176
- message,
1177
- onHitlAction,
1178
- renderPart
1179
- }
1180
- ) : null,
1181
- !isAssistant ? renderUserContent ? renderUserContent({ message }) : resolvedUserContent ? /* @__PURE__ */ jsx7(
1182
- AgentChatUserMessageContent,
1183
- {
1184
- collapsedLines: userMessageCollapsedLines,
1185
- collapseThreshold: userMessageCollapseThreshold,
1186
- content: resolvedUserContent,
1187
- showLessLabel: userMessageShowLessLabel,
1188
- showMoreLabel: userMessageShowMoreLabel
1189
- }
1190
- ) : null : null
1191
- ]
1192
- }
1193
- ) : null,
1194
- renderedActions ? /* @__PURE__ */ jsx7("div", { className: cn(dir === "rtl" ? "mt-1 h-9 w-full translate-x-2" : "mt-1 h-9 w-full -translate-x-2", actionsClassName), children: renderedActions }) : null
1195
- ] });
1196
- }
1197
-
1198
- // src/ui/bubble.tsx
1199
- import { cva as cva2 } from "class-variance-authority";
1200
- import { Slot as Slot2 } from "@radix-ui/react-slot";
1201
- import { jsx as jsx8 } from "react/jsx-runtime";
43
+ import { jsx } from "react/jsx-runtime";
1202
44
  function BubbleGroup({ className, ...props }) {
1203
- return /* @__PURE__ */ jsx8(
45
+ return /* @__PURE__ */ jsx(
1204
46
  "div",
1205
47
  {
1206
48
  "data-slot": "bubble-group",
@@ -1209,7 +51,7 @@ function BubbleGroup({ className, ...props }) {
1209
51
  }
1210
52
  );
1211
53
  }
1212
- var bubbleVariants = cva2(
54
+ var bubbleVariants = cva(
1213
55
  "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",
1214
56
  {
1215
57
  variants: {
@@ -1234,7 +76,7 @@ function Bubble({
1234
76
  className,
1235
77
  ...props
1236
78
  }) {
1237
- return /* @__PURE__ */ jsx8(
79
+ return /* @__PURE__ */ jsx(
1238
80
  "div",
1239
81
  {
1240
82
  "data-slot": "bubble",
@@ -1250,8 +92,8 @@ function BubbleContent({
1250
92
  className,
1251
93
  ...props
1252
94
  }) {
1253
- const Comp = asChild ? Slot2 : "div";
1254
- return /* @__PURE__ */ jsx8(
95
+ const Comp = asChild ? Slot : "div";
96
+ return /* @__PURE__ */ jsx(
1255
97
  Comp,
1256
98
  {
1257
99
  "data-slot": "bubble-content",
@@ -1263,7 +105,7 @@ function BubbleContent({
1263
105
  }
1264
106
  );
1265
107
  }
1266
- var bubbleReactionsVariants = cva2(
108
+ var bubbleReactionsVariants = cva(
1267
109
  "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",
1268
110
  {
1269
111
  variants: {
@@ -1288,7 +130,7 @@ function BubbleReactions({
1288
130
  className,
1289
131
  ...props
1290
132
  }) {
1291
- return /* @__PURE__ */ jsx8(
133
+ return /* @__PURE__ */ jsx(
1292
134
  "div",
1293
135
  {
1294
136
  "data-slot": "bubble-reactions",
@@ -1301,10 +143,10 @@ function BubbleReactions({
1301
143
  }
1302
144
 
1303
145
  // src/ui/marker.tsx
1304
- import { cva as cva3 } from "class-variance-authority";
1305
- import { Slot as Slot3 } from "@radix-ui/react-slot";
1306
- import { jsx as jsx9 } from "react/jsx-runtime";
1307
- var markerVariants = cva3(
146
+ import { cva as cva2 } from "class-variance-authority";
147
+ import { Slot as Slot2 } from "@radix-ui/react-slot";
148
+ import { jsx as jsx2 } from "react/jsx-runtime";
149
+ var markerVariants = cva2(
1308
150
  "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",
1309
151
  {
1310
152
  variants: {
@@ -1322,8 +164,8 @@ function Marker({
1322
164
  asChild = false,
1323
165
  ...props
1324
166
  }) {
1325
- const Comp = asChild ? Slot3 : "div";
1326
- return /* @__PURE__ */ jsx9(
167
+ const Comp = asChild ? Slot2 : "div";
168
+ return /* @__PURE__ */ jsx2(
1327
169
  Comp,
1328
170
  {
1329
171
  "data-slot": "marker",
@@ -1334,7 +176,7 @@ function Marker({
1334
176
  );
1335
177
  }
1336
178
  function MarkerIcon({ className, ...props }) {
1337
- return /* @__PURE__ */ jsx9(
179
+ return /* @__PURE__ */ jsx2(
1338
180
  "span",
1339
181
  {
1340
182
  "data-slot": "marker-icon",
@@ -1348,7 +190,7 @@ function MarkerIcon({ className, ...props }) {
1348
190
  );
1349
191
  }
1350
192
  function MarkerContent({ className, ...props }) {
1351
- return /* @__PURE__ */ jsx9(
193
+ return /* @__PURE__ */ jsx2(
1352
194
  "span",
1353
195
  {
1354
196
  "data-slot": "marker-content",
@@ -1362,7 +204,7 @@ function MarkerContent({ className, ...props }) {
1362
204
  }
1363
205
 
1364
206
  // src/ui/mcp-connect-required-card.tsx
1365
- import { jsx as jsx10, jsxs as jsxs5 } from "react/jsx-runtime";
207
+ import { jsx as jsx3, jsxs } from "react/jsx-runtime";
1366
208
  var STATUS_LABELS = {
1367
209
  pending: "Connect",
1368
210
  connecting: "Connecting",
@@ -1386,7 +228,7 @@ function McpConnectRequiredCard({
1386
228
  const isBusy = loading || status === "connecting" || status === "resuming";
1387
229
  const isConnected = status === "connected";
1388
230
  const buttonDisabled = disabled || isBusy || isConnected || !onConnect;
1389
- return /* @__PURE__ */ jsx10(
231
+ return /* @__PURE__ */ jsx3(
1390
232
  "div",
1391
233
  {
1392
234
  "data-agents24-mcp-connect-card": true,
@@ -1396,13 +238,13 @@ function McpConnectRequiredCard({
1396
238
  className
1397
239
  ),
1398
240
  ...props,
1399
- children: /* @__PURE__ */ jsxs5("div", { className: "flex min-w-0 items-start justify-between gap-3", children: [
1400
- /* @__PURE__ */ jsxs5("div", { className: "min-w-0", children: [
1401
- /* @__PURE__ */ jsx10("div", { className: "truncate text-sm font-medium", children: label }),
1402
- /* @__PURE__ */ jsx10("div", { className: "mt-1 text-sm text-muted-foreground", children: message || "Connect your account to continue." }),
1403
- status === "error" && error ? /* @__PURE__ */ jsx10("div", { className: "mt-2 text-sm text-destructive", children: error }) : null
241
+ children: /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-start justify-between gap-3", children: [
242
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
243
+ /* @__PURE__ */ jsx3("div", { className: "truncate text-sm font-medium", children: label }),
244
+ /* @__PURE__ */ jsx3("div", { className: "mt-1 text-sm text-muted-foreground", children: message || "Connect your account to continue." }),
245
+ status === "error" && error ? /* @__PURE__ */ jsx3("div", { className: "mt-2 text-sm text-destructive", children: error }) : null
1404
246
  ] }),
1405
- /* @__PURE__ */ jsx10(
247
+ /* @__PURE__ */ jsx3(
1406
248
  "button",
1407
249
  {
1408
250
  type: "button",