@agents24/chat-react 0.5.29 → 0.5.31

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 (52) hide show
  1. package/README.md +28 -1
  2. package/dist/chunk-INO5DSSJ.js +86 -0
  3. package/dist/chunk-INO5DSSJ.js.map +1 -0
  4. package/dist/generated/delegation-types.d.ts +129 -0
  5. package/dist/registry/agents24-chat.json +267 -0
  6. package/dist/runtime/index.cjs +53 -1
  7. package/dist/runtime/index.cjs.map +1 -1
  8. package/dist/runtime/index.d.ts +2 -0
  9. package/dist/runtime/index.js +62 -78
  10. package/dist/runtime/index.js.map +1 -1
  11. package/dist/runtime/input-contract.d.ts +3 -1
  12. package/dist/runtime/use-delegation-tasks.d.ts +20 -0
  13. package/dist/scaffold/app.d.ts +3 -0
  14. package/dist/scaffold/components/chat/chat-composer.d.ts +5 -3
  15. package/dist/scaffold/components/chat/subagent-tasks.d.ts +3 -0
  16. package/dist/scaffold/components/chat/theme-menu.d.ts +1 -1
  17. package/dist/scaffold/components/theme-provider.d.ts +3 -1
  18. package/dist/scaffold/index.cjs +420 -333
  19. package/dist/scaffold/index.cjs.map +1 -1
  20. package/dist/scaffold/index.d.ts +5 -2
  21. package/dist/scaffold/index.js +415 -332
  22. package/dist/scaffold/index.js.map +1 -1
  23. package/dist/styles.css +14 -0
  24. package/dist/ui/adapters.d.ts +5 -2
  25. package/dist/ui/agent-chat-composer.d.ts +8 -2
  26. package/dist/ui/agent-chat-queue.d.ts +0 -2
  27. package/dist/ui/index.cjs +817 -623
  28. package/dist/ui/index.cjs.map +1 -1
  29. package/dist/ui/index.d.ts +1 -0
  30. package/dist/ui/index.js +776 -622
  31. package/dist/ui/index.js.map +1 -1
  32. package/dist/ui/tool-sessions.d.ts +10 -2
  33. package/dist/ui/use-agent-chat-composer.d.ts +47 -0
  34. package/dist/ui/use-audio-recorder.d.ts +5 -3
  35. package/package.json +11 -6
  36. package/scaffold/host-styles.css +3 -0
  37. package/scaffold/inherit.css +4 -0
  38. package/scaffold/manifest.json +2 -2
  39. package/scaffold/src/app.tsx +5 -0
  40. package/scaffold/src/chat.css +35 -1
  41. package/scaffold/src/components/chat/chat-composer.tsx +31 -97
  42. package/scaffold/src/components/chat/chat-shell.tsx +7 -5
  43. package/scaffold/src/components/chat/external-link-dialog.tsx +6 -5
  44. package/scaffold/src/components/chat/identity-control.tsx +0 -1
  45. package/scaffold/src/components/chat/subagent-tasks.tsx +109 -0
  46. package/scaffold/src/components/chat/theme-menu.tsx +3 -1
  47. package/scaffold/src/components/theme-provider.tsx +28 -23
  48. package/scaffold/src/components/ui/tooltip.tsx +3 -0
  49. package/scaffold/src/host.css +11 -0
  50. package/scaffold/src/index.css +1 -213
  51. package/scaffold/src/index.tsx +9 -2
  52. package/scaffold/src/theme.css +215 -0
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  // scaffold/src/app.tsx
2
4
  import { createBrowserVisibility } from "@agents24/client/browser";
3
5
  import {
@@ -13,7 +15,7 @@ import {
13
15
  } from "@agents24/chat-react/runtime";
14
16
  import { AgentChatQueue, queuedMessageComposerFiles, useLiveCompletionRuns } from "@agents24/chat-react/ui";
15
17
  import { CircleAlert, LoaderCircle as LoaderCircle3, SquarePen as SquarePen2, X } from "lucide-react";
16
- import * as React8 from "react";
18
+ import * as React9 from "react";
17
19
 
18
20
  // scaffold/src/components/chat/chat-composer.tsx
19
21
  import { ArrowUp, ChevronDown, LoaderCircle, Mic, Square } from "lucide-react";
@@ -21,13 +23,8 @@ import * as React2 from "react";
21
23
  import {
22
24
  AgentChatContextStatus,
23
25
  ChatAttachmentRows,
24
- chatComposerAction,
25
- createAgentChatComposerFile,
26
- revokeAgentChatComposerFiles,
27
- useAudioRecorder,
28
- useChatComposerFocus
26
+ useAgentChatComposer
29
27
  } from "@agents24/chat-react/ui";
30
- import { attachmentFileAccept, inputMimeTypes, validateAgentChatSubmission } from "@agents24/chat-react/runtime";
31
28
 
32
29
  // scaffold/src/components/ui/button.tsx
33
30
  import { cva } from "class-variance-authority";
@@ -99,6 +96,21 @@ import { CheckIcon, ChevronRightIcon } from "lucide-react";
99
96
  import * as React from "react";
100
97
  import { jsx as jsx2 } from "react/jsx-runtime";
101
98
  var ThemeContext = React.createContext(null);
99
+ var appearanceEvent = "agents24-chat-appearance-change";
100
+ var subscribeAppearance = (notify) => {
101
+ window.addEventListener("storage", notify);
102
+ window.addEventListener(appearanceEvent, notify);
103
+ return () => {
104
+ window.removeEventListener("storage", notify);
105
+ window.removeEventListener(appearanceEvent, notify);
106
+ };
107
+ };
108
+ var subscribeDarkMode = (notify) => {
109
+ const media = window.matchMedia?.("(prefers-color-scheme: dark)");
110
+ media?.addEventListener("change", notify);
111
+ return () => media?.removeEventListener("change", notify);
112
+ };
113
+ var getDarkMode = () => window.matchMedia?.("(prefers-color-scheme: dark)").matches ?? false;
102
114
  var storageKey = "agents24-chat-appearance";
103
115
  function storedAppearance() {
104
116
  try {
@@ -111,43 +123,33 @@ function storedAppearance() {
111
123
  function ThemeProvider({
112
124
  children,
113
125
  controller,
114
- theme = "soft"
126
+ theme = "soft",
127
+ mode = "isolated"
115
128
  }) {
116
- const [internalAppearance, setInternalAppearance] = React.useState("system");
117
- const [resolvedAppearance, setResolvedAppearance] = React.useState("light");
118
- const [mounted, setMounted] = React.useState(false);
129
+ const [sessionAppearance, setSessionAppearance] = React.useState(null);
130
+ const stored = React.useSyncExternalStore(subscribeAppearance, storedAppearance, () => "system");
131
+ const systemDark = React.useSyncExternalStore(subscribeDarkMode, getDarkMode, () => false);
119
132
  const [portalContainer, setPortalContainer] = React.useState(null);
120
- const appearance = controller?.appearance ?? internalAppearance;
121
- React.useEffect(() => {
122
- if (!controller) setInternalAppearance(storedAppearance());
123
- setMounted(true);
124
- }, [controller]);
125
- React.useEffect(() => {
126
- if (!mounted || typeof window === "undefined" || typeof window.matchMedia !== "function") return;
127
- const media = window.matchMedia("(prefers-color-scheme: dark)");
128
- const apply = () => setResolvedAppearance(
129
- appearance === "system" ? media.matches ? "dark" : "light" : appearance
130
- );
131
- apply();
132
- media.addEventListener("change", apply);
133
- return () => media.removeEventListener("change", apply);
134
- }, [appearance, mounted]);
133
+ const appearance = controller?.appearance ?? sessionAppearance ?? stored;
134
+ const resolvedAppearance = appearance === "system" ? systemDark ? "dark" : "light" : appearance;
135
135
  const change = React.useCallback((next) => {
136
136
  if (controller) {
137
137
  controller.setAppearance(next);
138
138
  return;
139
139
  }
140
+ setSessionAppearance(next);
140
141
  try {
141
142
  window.localStorage.setItem(storageKey, next);
142
143
  } catch {
143
144
  }
144
- setInternalAppearance(next);
145
+ window.dispatchEvent(new Event(appearanceEvent));
145
146
  }, [controller]);
146
- return /* @__PURE__ */ jsx2(ThemeContext.Provider, { value: { appearance, portalContainer, setAppearance: change }, children: /* @__PURE__ */ jsx2(
147
+ return /* @__PURE__ */ jsx2(ThemeContext.Provider, { value: { appearance, portalContainer, setAppearance: change, inherited: mode === "inherit" }, children: /* @__PURE__ */ jsx2(
147
148
  "div",
148
149
  {
149
150
  ref: setPortalContainer,
150
- className: `${resolvedAppearance} size-full`,
151
+ className: mode === "inherit" ? "size-full" : `${resolvedAppearance} size-full`,
152
+ "data-agents24-theme-mode": mode,
151
153
  "data-agents24-theme": theme,
152
154
  "data-agents24-theme-root": "",
153
155
  children
@@ -449,6 +451,7 @@ function TooltipTrigger({
449
451
  asChild: _asChild,
450
452
  ...props
451
453
  }) {
454
+ void _asChild;
452
455
  return /* @__PURE__ */ jsx6(Slot2.Root, { "data-slot": "tooltip-trigger", ...props });
453
456
  }
454
457
  function TooltipContent({
@@ -458,6 +461,8 @@ function TooltipContent({
458
461
  sideOffset: _sideOffset,
459
462
  ...props
460
463
  }) {
464
+ void _align;
465
+ void _sideOffset;
461
466
  return /* @__PURE__ */ jsx6(
462
467
  "span",
463
468
  {
@@ -481,6 +486,8 @@ function TooltipContent({
481
486
  import { Fragment, jsx as jsx7, jsxs as jsxs3 } from "react/jsx-runtime";
482
487
  function ChatComposer({
483
488
  inputContract,
489
+ queue,
490
+ onRecordingStreamChange,
484
491
  disabled,
485
492
  contextWindow,
486
493
  focusKey,
@@ -498,49 +505,35 @@ function ChatComposer({
498
505
  restoredDraft,
499
506
  utilityRow = "inline"
500
507
  }) {
501
- const [text, setText] = React2.useState("");
502
- const [files, setFiles] = React2.useState([]);
503
- const [attachmentError, setAttachmentError] = React2.useState(null);
504
508
  const inputRef = React2.useRef(null);
505
- const textareaRef = React2.useRef(null);
506
- const filesRef = React2.useRef([]);
507
- const inFlightFilesRef = React2.useRef([]);
508
- const submittingRef = React2.useRef(false);
509
- const draftRevisionRef = React2.useRef(0);
510
- const attachmentMimeTypes = inputMimeTypes(inputContract);
511
- const allowAttachments = attachmentMimeTypes.length > 0;
512
- const recordingAllowed = inputContract.modalities.audio.recording_enabled;
513
- React2.useEffect(() => {
514
- filesRef.current = files;
515
- }, [files]);
516
- React2.useEffect(() => () => {
517
- revokeAgentChatComposerFiles(filesRef.current);
518
- revokeAgentChatComposerFiles(inFlightFilesRef.current);
519
- }, []);
520
- const focusComposer = useChatComposerFocus({
509
+ const {
510
+ text,
511
+ setText,
512
+ files,
513
+ removeFile,
514
+ addFiles,
521
515
  textareaRef,
522
- disabled: Boolean(disabled),
516
+ submit,
517
+ attachmentError,
518
+ attachmentMimeTypes,
519
+ allowAttachments,
520
+ recordingAllowed,
521
+ recorder,
522
+ isExpanded,
523
+ validationError,
524
+ action,
525
+ canSubmit
526
+ } = useAgentChatComposer({
527
+ inputContract,
528
+ disabled,
523
529
  focusKey,
530
+ forceExpanded,
531
+ isCancelling,
532
+ isRunning,
533
+ onSubmit,
524
534
  restoredDraft,
525
- onRestore: (draft) => {
526
- draftRevisionRef.current += 1;
527
- revokeAgentChatComposerFiles(filesRef.current);
528
- setText(draft.text);
529
- setFiles(draft.files);
530
- }
535
+ onRecordingStreamChange
531
536
  });
532
- const addRecording = React2.useCallback((file) => {
533
- draftRevisionRef.current += 1;
534
- setFiles((current) => [...current, createAgentChatComposerFile(file)]);
535
- }, []);
536
- const recorder = useAudioRecorder(addRecording);
537
- const isExpanded = forceExpanded || text.includes("\n") || text.length > 62;
538
- const validationError = validateAgentChatSubmission(
539
- inputContract,
540
- text,
541
- files.map((file) => ({ data: file.source || new Uint8Array(), mediaType: file.mediaType }))
542
- );
543
- const action = chatComposerAction({ hasDraft: Boolean(text.trim() || files.length), isRunning, isCancelling });
544
537
  const selectedAgent = agents.find((item) => item.id === agentId);
545
538
  const selectedModel = models.find((item) => item.id === modelId);
546
539
  const modelSelector = models.length > 0 ? /* @__PURE__ */ jsx7("div", { className: "w-fit max-w-40", children: /* @__PURE__ */ jsxs3(Select, { disabled: disabled || isRunning, onValueChange: onModelChange, value: modelId || void 0, children: [
@@ -580,66 +573,35 @@ function ChatComposer({
580
573
  ] }) : modelSelector;
581
574
  const hasContextWindow = Number.isFinite(Number(contextWindow?.max_tokens)) && Number(contextWindow?.max_tokens) > 0;
582
575
  const showUtilityRow = utilityRow === "below";
583
- const submit = async () => {
584
- if (disabled || isCancelling || submittingRef.current || validationError) return;
585
- const value = text;
586
- const selectedFiles = files;
587
- const clearedRevision = draftRevisionRef.current + 1;
588
- draftRevisionRef.current = clearedRevision;
589
- submittingRef.current = true;
590
- inFlightFilesRef.current = selectedFiles;
591
- setText("");
592
- setFiles([]);
593
- focusComposer();
594
- try {
595
- await onSubmit(value, selectedFiles);
596
- revokeAgentChatComposerFiles(selectedFiles);
597
- inFlightFilesRef.current = [];
598
- } catch {
599
- if (draftRevisionRef.current === clearedRevision) {
600
- draftRevisionRef.current += 1;
601
- setText(value);
602
- setFiles(selectedFiles);
603
- inFlightFilesRef.current = [];
604
- } else {
605
- revokeAgentChatComposerFiles(selectedFiles);
606
- inFlightFilesRef.current = [];
607
- }
608
- } finally {
609
- submittingRef.current = false;
610
- }
611
- };
612
576
  return /* @__PURE__ */ jsxs3(
613
577
  "div",
614
578
  {
615
579
  className: "relative z-10 w-full pb-[max(1rem,env(safe-area-inset-bottom))]",
616
580
  "data-agents24-chat-composer": "",
617
581
  children: [
618
- allowAttachments && files.length > 0 && /* @__PURE__ */ jsx7("div", { className: "mb-2 min-w-0", children: /* @__PURE__ */ jsx7(
619
- ChatAttachmentRows,
620
- {
621
- attachments: files,
622
- onRemove: (attachment) => {
623
- if (!attachment.id) return;
624
- draftRevisionRef.current += 1;
625
- setFiles((current) => {
626
- const removed = current.find((file) => file.id === attachment.id);
627
- if (removed) revokeAgentChatComposerFiles([removed]);
628
- return current.filter((file) => file.id !== attachment.id);
629
- });
630
- }
631
- }
632
- ) }),
582
+ queue,
633
583
  attachmentError || recorder.error ? /* @__PURE__ */ jsx7("p", { className: "mb-2 text-xs text-destructive", role: "alert", children: attachmentError || recorder.error }) : null,
634
584
  /* @__PURE__ */ jsxs3(
635
585
  "div",
636
586
  {
637
587
  "data-expanded": isExpanded || void 0,
588
+ "data-attachments": files.length > 0 || void 0,
638
589
  className: cn(
639
590
  "agents24-composer-grid grid min-w-0 items-center gap-2 rounded-xl border bg-background shadow-sm",
640
591
  isExpanded ? "p-2" : "p-1.5"
641
592
  ),
642
593
  children: [
594
+ allowAttachments && files.length > 0 && /* @__PURE__ */ jsx7("div", { className: "agents24-composer-attachments min-w-0", children: /* @__PURE__ */ jsx7(
595
+ ChatAttachmentRows,
596
+ {
597
+ layout: "composer",
598
+ attachments: files,
599
+ onRemove: (attachment) => {
600
+ if (!attachment.id) return;
601
+ removeFile(attachment.id);
602
+ }
603
+ }
604
+ ) }),
643
605
  allowAttachments ? /* @__PURE__ */ jsxs3(Fragment, { children: [
644
606
  /* @__PURE__ */ jsx7(
645
607
  "input",
@@ -648,11 +610,9 @@ function ChatComposer({
648
610
  className: "sr-only",
649
611
  type: "file",
650
612
  multiple: true,
651
- accept: attachmentFileAccept(attachmentMimeTypes),
613
+ accept: attachmentMimeTypes.join(","),
652
614
  onChange: (event) => {
653
- const selected = Array.from(event.target.files ?? []);
654
- setFiles((current) => [...current, ...selected.map(createAgentChatComposerFile)]);
655
- setAttachmentError(null);
615
+ addFiles(Array.from(event.target.files ?? []));
656
616
  event.currentTarget.value = "";
657
617
  }
658
618
  }
@@ -673,7 +633,6 @@ function ChatComposer({
673
633
  isExpanded ? "py-2" : "h-9 py-2"
674
634
  ),
675
635
  onChange: (event) => {
676
- draftRevisionRef.current += 1;
677
636
  setText(event.target.value);
678
637
  },
679
638
  onKeyDown: (event) => {
@@ -713,7 +672,7 @@ function ChatComposer({
713
672
  ) : validationError ? /* @__PURE__ */ jsxs3(Tooltip, { children: [
714
673
  /* @__PURE__ */ jsx7(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx7("span", { tabIndex: 0, "aria-label": validationError, children: /* @__PURE__ */ jsx7(Button, { size: "icon", "aria-label": isRunning ? "Queue message" : "Send message", disabled: true, onClick: () => void submit(), children: /* @__PURE__ */ jsx7(ArrowUp, {}) }) }) }),
715
674
  /* @__PURE__ */ jsx7(TooltipContent, { children: validationError })
716
- ] }) : /* @__PURE__ */ jsx7(Button, { size: "icon", "aria-label": isRunning ? "Queue message" : "Send message", disabled, onClick: () => void submit(), children: /* @__PURE__ */ jsx7(ArrowUp, {}) })
675
+ ] }) : /* @__PURE__ */ jsx7(Button, { size: "icon", "aria-label": isRunning ? "Queue message" : "Send message", disabled: !canSubmit, onClick: () => void submit(), children: /* @__PURE__ */ jsx7(ArrowUp, {}) })
717
676
  ] })
718
677
  ]
719
678
  }
@@ -925,19 +884,20 @@ function DialogDescription({
925
884
  import { jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
926
885
  function ExternalLinkDialog({ isOpen, onClose, onConfirm, url }) {
927
886
  const [copied, setCopied] = React4.useState(false);
928
- React4.useEffect(() => {
929
- if (!isOpen) setCopied(false);
930
- }, [isOpen]);
887
+ const close = () => {
888
+ setCopied(false);
889
+ onClose();
890
+ };
931
891
  const copyUrl = async () => {
932
892
  await navigator.clipboard.writeText(url);
933
893
  setCopied(true);
934
894
  };
935
895
  const openUrl = () => {
936
896
  onConfirm();
937
- onClose();
897
+ close();
938
898
  };
939
899
  return /* @__PURE__ */ jsx10(Dialog, { open: isOpen, onOpenChange: (open) => {
940
- if (!open) onClose();
900
+ if (!open) close();
941
901
  }, children: /* @__PURE__ */ jsxs6(DialogContent, { className: "max-h-[calc(100dvh-2rem)] min-w-0 overflow-hidden sm:max-w-md", children: [
942
902
  /* @__PURE__ */ jsxs6(DialogHeader, { children: [
943
903
  /* @__PURE__ */ jsx10(DialogTitle, { children: "Open external link?" }),
@@ -1199,23 +1159,147 @@ function ChatMessage({
1199
1159
  ] }) });
1200
1160
  }
1201
1161
 
1202
- // scaffold/src/components/chat/theme-menu.tsx
1203
- import { Laptop, Moon, Sun } from "lucide-react";
1162
+ // scaffold/src/components/chat/subagent-tasks.tsx
1204
1163
  import * as React5 from "react";
1164
+ import { useAgentClient, partsFromResponseBlocks } from "@agents24/react";
1165
+ import { AgentResponseTimeline as AgentResponseTimeline2 } from "@agents24/chat-react/ui";
1166
+ import { useDelegationTasks } from "@agents24/chat-react/runtime";
1205
1167
  import { jsx as jsx15, jsxs as jsxs9 } from "react/jsx-runtime";
1168
+ function SubagentTasks({ threadId }) {
1169
+ const client = useAgentClient();
1170
+ const source = React5.useMemo(() => ({ events: (input) => client.threads.delegations.events(input) }), [client]);
1171
+ const { tasks, error, refresh } = useDelegationTasks({ threadId, source });
1172
+ const [selection, setSelection] = React5.useState({ threadId: null, handle: null });
1173
+ const selected = selection.threadId === threadId ? selection.handle : null;
1174
+ if (!threadId || !tasks.length && !error) return null;
1175
+ return /* @__PURE__ */ jsx15("section", { "aria-label": "Subagent tasks", className: "mx-auto w-full max-w-3xl px-4 py-2 text-sm", children: /* @__PURE__ */ jsxs9("details", { children: [
1176
+ /* @__PURE__ */ jsxs9("summary", { className: "cursor-pointer font-medium", children: [
1177
+ "Tasks (",
1178
+ tasks.length,
1179
+ ")"
1180
+ ] }),
1181
+ error ? /* @__PURE__ */ jsx15("p", { role: "alert", children: "Task updates are unavailable. Retrying\u2026" }) : null,
1182
+ /* @__PURE__ */ jsx15("ul", { className: "max-h-56 overflow-y-auto", children: tasks.map((task) => /* @__PURE__ */ jsx15("li", { style: { paddingInlineStart: Math.max(0, task.depth - 1) * 12 }, children: /* @__PURE__ */ jsxs9(
1183
+ "button",
1184
+ {
1185
+ type: "button",
1186
+ className: "flex w-full items-center justify-between gap-3 rounded-md px-2 py-2 text-start hover:bg-muted",
1187
+ onClick: () => setSelection({ threadId, handle: selected === task.handle ? null : task.handle }),
1188
+ "aria-expanded": selected === task.handle,
1189
+ children: [
1190
+ /* @__PURE__ */ jsxs9("span", { className: "min-w-0 truncate", children: [
1191
+ task.name,
1192
+ task.background ? " \xB7 Background" : ""
1193
+ ] }),
1194
+ /* @__PURE__ */ jsx15("span", { className: "shrink-0 text-xs text-muted-foreground", children: task.status.replaceAll("_", " ") })
1195
+ ]
1196
+ }
1197
+ ) }, task.handle)) }),
1198
+ tasks.filter((task) => task.handle === selected).map((task) => /* @__PURE__ */ jsx15(TaskConversation, { threadId, task, onChanged: refresh }, `${threadId}:${task.handle}`))
1199
+ ] }) });
1200
+ }
1201
+ function TaskConversation({ threadId, task, onChanged }) {
1202
+ const client = useAgentClient();
1203
+ const [history, setHistory] = React5.useState(null);
1204
+ const [earlier, setEarlier] = React5.useState([]);
1205
+ const historyInitialized = React5.useRef(false);
1206
+ const [before, setBefore] = React5.useState(null);
1207
+ const [error, setError] = React5.useState(null);
1208
+ const [busy, setBusy] = React5.useState(false);
1209
+ const operation = React5.useRef(null);
1210
+ React5.useEffect(() => {
1211
+ const controller = new AbortController();
1212
+ let timer;
1213
+ const refresh = async () => {
1214
+ try {
1215
+ const page = await client.threads.delegations.history({ threadId, handle: task.handle, signal: controller.signal });
1216
+ if (controller.signal.aborted) return;
1217
+ setHistory(page);
1218
+ if (!historyInitialized.current) {
1219
+ historyInitialized.current = true;
1220
+ setBefore(page.paging.next_before_turn_index);
1221
+ }
1222
+ } catch {
1223
+ if (!controller.signal.aborted) setError("Task history could not be loaded.");
1224
+ } finally {
1225
+ if (!controller.signal.aborted) timer = setTimeout(refresh, 2e3);
1226
+ }
1227
+ };
1228
+ void refresh();
1229
+ return () => {
1230
+ controller.abort();
1231
+ clearTimeout(timer);
1232
+ };
1233
+ }, [client, threadId, task.handle]);
1234
+ const loadEarlier = async () => {
1235
+ if (before === null) return;
1236
+ setBusy(true);
1237
+ try {
1238
+ const page = await client.threads.delegations.history({ threadId, handle: task.handle, before });
1239
+ setEarlier((current) => [...page.turns, ...current]);
1240
+ setBefore(page.paging.next_before_turn_index);
1241
+ } catch {
1242
+ setError("Earlier task history could not be loaded.");
1243
+ } finally {
1244
+ setBusy(false);
1245
+ }
1246
+ };
1247
+ const act = async (action) => {
1248
+ setBusy(true);
1249
+ setError(null);
1250
+ if (operation.current?.action !== action) operation.current = { action, id: crypto.randomUUID() };
1251
+ try {
1252
+ await client.threads.delegations.act({
1253
+ threadId,
1254
+ handle: task.handle,
1255
+ action: { action, operation_id: operation.current.id }
1256
+ });
1257
+ operation.current = null;
1258
+ onChanged();
1259
+ } catch {
1260
+ setError("The task action could not be completed. Try again.");
1261
+ } finally {
1262
+ setBusy(false);
1263
+ }
1264
+ };
1265
+ const turns = Array.from(new Map([...earlier, ...history?.turns || []].map((turn) => [turn.id, turn])).values()).sort((a, b) => a.turn_index - b.turn_index);
1266
+ return /* @__PURE__ */ jsxs9("div", { className: "space-y-3 rounded-md bg-muted/30 p-3", children: [
1267
+ /* @__PURE__ */ jsx15("h3", { className: "font-medium", children: task.name }),
1268
+ error ? /* @__PURE__ */ jsx15("p", { role: "alert", children: error }) : null,
1269
+ /* @__PURE__ */ jsxs9("div", { className: "flex flex-wrap gap-2", children: [
1270
+ task.capabilities.interrupt ? /* @__PURE__ */ jsx15(Button, { variant: "secondary", size: "sm", disabled: busy, onClick: () => void act("interrupt"), children: "Interrupt" }) : null,
1271
+ task.capabilities.cancel ? /* @__PURE__ */ jsx15(Button, { variant: "secondary", size: "sm", disabled: busy, onClick: () => void act("cancel"), children: "Cancel task" }) : null,
1272
+ before !== null ? /* @__PURE__ */ jsx15(Button, { variant: "ghost", size: "sm", disabled: busy, onClick: () => void loadEarlier(), children: "Earlier exchanges" }) : null
1273
+ ] }),
1274
+ /* @__PURE__ */ jsx15("div", { className: "max-h-[55dvh] space-y-4 overflow-y-auto break-words", children: turns.map((turn) => /* @__PURE__ */ jsxs9("article", { className: "space-y-2", children: [
1275
+ turn.assignment_instruction ? /* @__PURE__ */ jsx15("p", { className: "whitespace-pre-wrap text-muted-foreground", children: turn.assignment_instruction }) : null,
1276
+ turn.user_input_text ? /* @__PURE__ */ jsx15("p", { className: "whitespace-pre-wrap", children: turn.user_input_text }) : null,
1277
+ turn.exchanges?.map((exchange) => /* @__PURE__ */ jsxs9("div", { className: "whitespace-pre-wrap break-words text-sm", children: [
1278
+ /* @__PURE__ */ jsx15("p", { className: "text-xs font-medium", children: exchange.sender === "parent" ? "Parent Agent" : "Subagent update" }),
1279
+ exchange.text
1280
+ ] }, exchange.id)),
1281
+ /* @__PURE__ */ jsx15(AgentResponseTimeline2, { message: { id: turn.id, role: "assistant", createdAt: turn.created_at, content: turn.assistant_output_text || "", parts: partsFromResponseBlocks(turn.response_blocks) } })
1282
+ ] }, turn.id)) })
1283
+ ] });
1284
+ }
1285
+
1286
+ // scaffold/src/components/chat/theme-menu.tsx
1287
+ import { Laptop, Moon, Sun } from "lucide-react";
1288
+ import * as React6 from "react";
1289
+ import { jsx as jsx16, jsxs as jsxs10 } from "react/jsx-runtime";
1206
1290
  var options = [
1207
1291
  { value: "system", label: "System", icon: Laptop },
1208
1292
  { value: "light", label: "Light", icon: Sun },
1209
1293
  { value: "dark", label: "Dark", icon: Moon }
1210
1294
  ];
1211
1295
  function ThemeMenu() {
1212
- const { appearance, setAppearance } = useTheme();
1213
- const [open, setOpen] = React5.useState(false);
1214
- const rootRef = React5.useRef(null);
1215
- const firstItemRef = React5.useRef(null);
1216
- const menuId = React5.useId();
1296
+ const { appearance, setAppearance, inherited } = useTheme();
1297
+ const [open, setOpen] = React6.useState(false);
1298
+ const rootRef = React6.useRef(null);
1299
+ const firstItemRef = React6.useRef(null);
1300
+ const menuId = React6.useId();
1217
1301
  const ActiveIcon = options.find((item) => item.value === appearance)?.icon ?? Laptop;
1218
- React5.useEffect(() => {
1302
+ React6.useEffect(() => {
1219
1303
  if (!open) return;
1220
1304
  firstItemRef.current?.focus();
1221
1305
  const closeOnOutsidePointer = (event) => {
@@ -1231,9 +1315,10 @@ function ThemeMenu() {
1231
1315
  document.removeEventListener("keydown", closeOnEscape);
1232
1316
  };
1233
1317
  }, [open]);
1318
+ if (inherited) return null;
1234
1319
  const label = appearance === "system" ? "System" : appearance === "light" ? "Light" : "Dark";
1235
- return /* @__PURE__ */ jsxs9("div", { ref: rootRef, className: "relative", children: [
1236
- /* @__PURE__ */ jsx15(
1320
+ return /* @__PURE__ */ jsxs10("div", { ref: rootRef, className: "relative", children: [
1321
+ /* @__PURE__ */ jsx16(
1237
1322
  Button,
1238
1323
  {
1239
1324
  "aria-controls": menuId,
@@ -1243,16 +1328,16 @@ function ThemeMenu() {
1243
1328
  onClick: () => setOpen((value) => !value),
1244
1329
  size: "icon-sm",
1245
1330
  variant: "ghost",
1246
- children: /* @__PURE__ */ jsx15(ActiveIcon, {})
1331
+ children: /* @__PURE__ */ jsx16(ActiveIcon, {})
1247
1332
  }
1248
1333
  ),
1249
- open ? /* @__PURE__ */ jsx15(
1334
+ open ? /* @__PURE__ */ jsx16(
1250
1335
  "div",
1251
1336
  {
1252
1337
  id: menuId,
1253
1338
  className: "absolute end-0 top-full z-50 mt-1 flex min-w-44 flex-col rounded-xl border bg-popover p-1 text-popover-foreground shadow-md",
1254
1339
  role: "menu",
1255
- children: options.map(({ value, label: optionLabel, icon: Icon }, index) => /* @__PURE__ */ jsxs9(
1340
+ children: options.map(({ value, label: optionLabel, icon: Icon }, index) => /* @__PURE__ */ jsxs10(
1256
1341
  Button,
1257
1342
  {
1258
1343
  ref: index === 0 ? firstItemRef : void 0,
@@ -1266,7 +1351,7 @@ function ThemeMenu() {
1266
1351
  size: "sm",
1267
1352
  variant: "ghost",
1268
1353
  children: [
1269
- /* @__PURE__ */ jsx15(Icon, {}),
1354
+ /* @__PURE__ */ jsx16(Icon, {}),
1270
1355
  " ",
1271
1356
  optionLabel
1272
1357
  ]
@@ -1286,16 +1371,16 @@ import { LoaderCircle as LoaderCircle2, SquarePen, Trash2 } from "lucide-react";
1286
1371
  import { LogIn, LogOut, UserRound } from "lucide-react";
1287
1372
 
1288
1373
  // scaffold/src/components/ui/sidebar.tsx
1289
- import * as React7 from "react";
1374
+ import * as React8 from "react";
1290
1375
  import { cva as cva3 } from "class-variance-authority";
1291
1376
  import { Slot as Slot4 } from "radix-ui";
1292
1377
 
1293
1378
  // scaffold/src/hooks/use-mobile.ts
1294
- import * as React6 from "react";
1379
+ import * as React7 from "react";
1295
1380
  var MOBILE_BREAKPOINT = 768;
1296
1381
  function useIsMobile() {
1297
- const [isMobile, setIsMobile] = React6.useState(void 0);
1298
- React6.useEffect(() => {
1382
+ const [isMobile, setIsMobile] = React7.useState(void 0);
1383
+ React7.useEffect(() => {
1299
1384
  const onChange = () => {
1300
1385
  setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
1301
1386
  };
@@ -1313,31 +1398,31 @@ function useIsMobile() {
1313
1398
  }
1314
1399
 
1315
1400
  // scaffold/src/components/ui/input.tsx
1316
- import { jsx as jsx16 } from "react/jsx-runtime";
1401
+ import { jsx as jsx17 } from "react/jsx-runtime";
1317
1402
 
1318
1403
  // scaffold/src/components/ui/separator.tsx
1319
1404
  import { Separator as SeparatorPrimitive } from "radix-ui";
1320
- import { jsx as jsx17 } from "react/jsx-runtime";
1405
+ import { jsx as jsx18 } from "react/jsx-runtime";
1321
1406
 
1322
1407
  // scaffold/src/components/ui/sheet.tsx
1323
1408
  import { Dialog as SheetPrimitive } from "radix-ui";
1324
1409
  import { XIcon as XIcon2 } from "lucide-react";
1325
- import { jsx as jsx18, jsxs as jsxs10 } from "react/jsx-runtime";
1410
+ import { jsx as jsx19, jsxs as jsxs11 } from "react/jsx-runtime";
1326
1411
  function Sheet({ ...props }) {
1327
- return /* @__PURE__ */ jsx18(SheetPrimitive.Root, { "data-slot": "sheet", ...props });
1412
+ return /* @__PURE__ */ jsx19(SheetPrimitive.Root, { "data-slot": "sheet", ...props });
1328
1413
  }
1329
1414
  function SheetPortal({
1330
1415
  container,
1331
1416
  ...props
1332
1417
  }) {
1333
1418
  const themeContainer = useThemePortalContainer();
1334
- return /* @__PURE__ */ jsx18(SheetPrimitive.Portal, { container: container || themeContainer || void 0, "data-slot": "sheet-portal", ...props });
1419
+ return /* @__PURE__ */ jsx19(SheetPrimitive.Portal, { container: container || themeContainer || void 0, "data-slot": "sheet-portal", ...props });
1335
1420
  }
1336
1421
  function SheetOverlay({
1337
1422
  className,
1338
1423
  ...props
1339
1424
  }) {
1340
- return /* @__PURE__ */ jsx18(
1425
+ return /* @__PURE__ */ jsx19(
1341
1426
  SheetPrimitive.Overlay,
1342
1427
  {
1343
1428
  "data-slot": "sheet-overlay",
@@ -1356,9 +1441,9 @@ function SheetContent({
1356
1441
  showCloseButton = true,
1357
1442
  ...props
1358
1443
  }) {
1359
- return /* @__PURE__ */ jsxs10(SheetPortal, { children: [
1360
- /* @__PURE__ */ jsx18(SheetOverlay, {}),
1361
- /* @__PURE__ */ jsxs10(
1444
+ return /* @__PURE__ */ jsxs11(SheetPortal, { children: [
1445
+ /* @__PURE__ */ jsx19(SheetOverlay, {}),
1446
+ /* @__PURE__ */ jsxs11(
1362
1447
  SheetPrimitive.Content,
1363
1448
  {
1364
1449
  "data-slot": "sheet-content",
@@ -1370,18 +1455,18 @@ function SheetContent({
1370
1455
  ...props,
1371
1456
  children: [
1372
1457
  children,
1373
- showCloseButton && /* @__PURE__ */ jsx18(SheetPrimitive.Close, { "data-slot": "sheet-close", asChild: true, children: /* @__PURE__ */ jsxs10(
1458
+ showCloseButton && /* @__PURE__ */ jsx19(SheetPrimitive.Close, { "data-slot": "sheet-close", asChild: true, children: /* @__PURE__ */ jsxs11(
1374
1459
  Button,
1375
1460
  {
1376
1461
  variant: "ghost",
1377
1462
  className: "absolute top-4 right-4 bg-secondary",
1378
1463
  size: "icon-sm",
1379
1464
  children: [
1380
- /* @__PURE__ */ jsx18(
1465
+ /* @__PURE__ */ jsx19(
1381
1466
  XIcon2,
1382
1467
  {}
1383
1468
  ),
1384
- /* @__PURE__ */ jsx18("span", { className: "sr-only", children: "Close" })
1469
+ /* @__PURE__ */ jsx19("span", { className: "sr-only", children: "Close" })
1385
1470
  ]
1386
1471
  }
1387
1472
  ) })
@@ -1391,7 +1476,7 @@ function SheetContent({
1391
1476
  ] });
1392
1477
  }
1393
1478
  function SheetHeader({ className, ...props }) {
1394
- return /* @__PURE__ */ jsx18(
1479
+ return /* @__PURE__ */ jsx19(
1395
1480
  "div",
1396
1481
  {
1397
1482
  "data-slot": "sheet-header",
@@ -1404,7 +1489,7 @@ function SheetTitle({
1404
1489
  className,
1405
1490
  ...props
1406
1491
  }) {
1407
- return /* @__PURE__ */ jsx18(
1492
+ return /* @__PURE__ */ jsx19(
1408
1493
  SheetPrimitive.Title,
1409
1494
  {
1410
1495
  "data-slot": "sheet-title",
@@ -1420,7 +1505,7 @@ function SheetDescription({
1420
1505
  className,
1421
1506
  ...props
1422
1507
  }) {
1423
- return /* @__PURE__ */ jsx18(
1508
+ return /* @__PURE__ */ jsx19(
1424
1509
  SheetPrimitive.Description,
1425
1510
  {
1426
1511
  "data-slot": "sheet-description",
@@ -1431,17 +1516,17 @@ function SheetDescription({
1431
1516
  }
1432
1517
 
1433
1518
  // scaffold/src/components/ui/skeleton.tsx
1434
- import { jsx as jsx19 } from "react/jsx-runtime";
1519
+ import { jsx as jsx20 } from "react/jsx-runtime";
1435
1520
 
1436
1521
  // scaffold/src/components/ui/sidebar.tsx
1437
1522
  import { PanelLeftIcon } from "lucide-react";
1438
- import { jsx as jsx20, jsxs as jsxs11 } from "react/jsx-runtime";
1523
+ import { jsx as jsx21, jsxs as jsxs12 } from "react/jsx-runtime";
1439
1524
  var SIDEBAR_COOKIE_NAME = "sidebar_state";
1440
1525
  var SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;
1441
1526
  var SIDEBAR_KEYBOARD_SHORTCUT = "b";
1442
- var SidebarContext = React7.createContext(null);
1527
+ var SidebarContext = React8.createContext(null);
1443
1528
  function useSidebar() {
1444
- const context = React7.useContext(SidebarContext);
1529
+ const context = React8.useContext(SidebarContext);
1445
1530
  if (!context) {
1446
1531
  throw new Error("useSidebar must be used within a SidebarProvider.");
1447
1532
  }
@@ -1456,10 +1541,10 @@ function SidebarProvider({
1456
1541
  ...props
1457
1542
  }) {
1458
1543
  const isMobile = useIsMobile();
1459
- const [openMobile, setOpenMobile] = React7.useState(false);
1460
- const [_open, _setOpen] = React7.useState(defaultOpen);
1544
+ const [openMobile, setOpenMobile] = React8.useState(false);
1545
+ const [_open, _setOpen] = React8.useState(defaultOpen);
1461
1546
  const open = openProp ?? _open;
1462
- const setOpen = React7.useCallback(
1547
+ const setOpen = React8.useCallback(
1463
1548
  (value) => {
1464
1549
  const openState = typeof value === "function" ? value(open) : value;
1465
1550
  if (setOpenProp) {
@@ -1471,10 +1556,10 @@ function SidebarProvider({
1471
1556
  },
1472
1557
  [setOpenProp, open]
1473
1558
  );
1474
- const toggleSidebar = React7.useCallback(() => {
1559
+ const toggleSidebar = React8.useCallback(() => {
1475
1560
  return isMobile ? setOpenMobile((open2) => !open2) : setOpen((open2) => !open2);
1476
1561
  }, [isMobile, setOpen, setOpenMobile]);
1477
- React7.useEffect(() => {
1562
+ React8.useEffect(() => {
1478
1563
  const handleKeyDown = (event) => {
1479
1564
  if (event.key === SIDEBAR_KEYBOARD_SHORTCUT && (event.metaKey || event.ctrlKey)) {
1480
1565
  event.preventDefault();
@@ -1485,7 +1570,7 @@ function SidebarProvider({
1485
1570
  return () => window.removeEventListener("keydown", handleKeyDown);
1486
1571
  }, [toggleSidebar]);
1487
1572
  const state = open ? "expanded" : "collapsed";
1488
- const contextValue = React7.useMemo(
1573
+ const contextValue = React8.useMemo(
1489
1574
  () => ({
1490
1575
  state,
1491
1576
  open,
@@ -1497,7 +1582,7 @@ function SidebarProvider({
1497
1582
  }),
1498
1583
  [state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar]
1499
1584
  );
1500
- return /* @__PURE__ */ jsx20(SidebarContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx20(
1585
+ return /* @__PURE__ */ jsx21(SidebarContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx21(
1501
1586
  "div",
1502
1587
  {
1503
1588
  "data-slot": "sidebar-wrapper",
@@ -1521,7 +1606,7 @@ function Sidebar({
1521
1606
  }) {
1522
1607
  const { isMobile, state, openMobile, setOpenMobile } = useSidebar();
1523
1608
  if (collapsible === "none") {
1524
- return /* @__PURE__ */ jsx20(
1609
+ return /* @__PURE__ */ jsx21(
1525
1610
  "div",
1526
1611
  {
1527
1612
  "data-slot": "sidebar",
@@ -1535,7 +1620,7 @@ function Sidebar({
1535
1620
  );
1536
1621
  }
1537
1622
  if (isMobile) {
1538
- return /* @__PURE__ */ jsx20(Sheet, { open: openMobile, onOpenChange: setOpenMobile, ...props, children: /* @__PURE__ */ jsxs11(
1623
+ return /* @__PURE__ */ jsx21(Sheet, { open: openMobile, onOpenChange: setOpenMobile, ...props, children: /* @__PURE__ */ jsxs12(
1539
1624
  SheetContent,
1540
1625
  {
1541
1626
  dir,
@@ -1545,16 +1630,16 @@ function Sidebar({
1545
1630
  className: "w-(--sidebar-width) bg-sidebar p-0 text-sidebar-foreground [--sidebar-width:18rem] [&>button]:hidden",
1546
1631
  side,
1547
1632
  children: [
1548
- /* @__PURE__ */ jsxs11(SheetHeader, { className: "sr-only", children: [
1549
- /* @__PURE__ */ jsx20(SheetTitle, { children: "Sidebar" }),
1550
- /* @__PURE__ */ jsx20(SheetDescription, { children: "Displays the mobile sidebar." })
1633
+ /* @__PURE__ */ jsxs12(SheetHeader, { className: "sr-only", children: [
1634
+ /* @__PURE__ */ jsx21(SheetTitle, { children: "Sidebar" }),
1635
+ /* @__PURE__ */ jsx21(SheetDescription, { children: "Displays the mobile sidebar." })
1551
1636
  ] }),
1552
- /* @__PURE__ */ jsx20("div", { className: "flex h-full w-full flex-col", children })
1637
+ /* @__PURE__ */ jsx21("div", { className: "flex h-full w-full flex-col", children })
1553
1638
  ]
1554
1639
  }
1555
1640
  ) });
1556
1641
  }
1557
- return /* @__PURE__ */ jsxs11(
1642
+ return /* @__PURE__ */ jsxs12(
1558
1643
  "div",
1559
1644
  {
1560
1645
  className: "group peer hidden text-sidebar-foreground md:block",
@@ -1564,7 +1649,7 @@ function Sidebar({
1564
1649
  "data-side": side,
1565
1650
  "data-slot": "sidebar",
1566
1651
  children: [
1567
- /* @__PURE__ */ jsx20(
1652
+ /* @__PURE__ */ jsx21(
1568
1653
  "div",
1569
1654
  {
1570
1655
  "data-slot": "sidebar-gap",
@@ -1576,7 +1661,7 @@ function Sidebar({
1576
1661
  )
1577
1662
  }
1578
1663
  ),
1579
- /* @__PURE__ */ jsx20(
1664
+ /* @__PURE__ */ jsx21(
1580
1665
  "div",
1581
1666
  {
1582
1667
  "data-slot": "sidebar-container",
@@ -1588,7 +1673,7 @@ function Sidebar({
1588
1673
  className
1589
1674
  ),
1590
1675
  ...props,
1591
- children: /* @__PURE__ */ jsx20(
1676
+ children: /* @__PURE__ */ jsx21(
1592
1677
  "div",
1593
1678
  {
1594
1679
  "data-sidebar": "sidebar",
@@ -1609,7 +1694,7 @@ function SidebarTrigger({
1609
1694
  ...props
1610
1695
  }) {
1611
1696
  const { toggleSidebar } = useSidebar();
1612
- return /* @__PURE__ */ jsxs11(
1697
+ return /* @__PURE__ */ jsxs12(
1613
1698
  Button,
1614
1699
  {
1615
1700
  "data-sidebar": "trigger",
@@ -1623,15 +1708,15 @@ function SidebarTrigger({
1623
1708
  },
1624
1709
  ...props,
1625
1710
  children: [
1626
- /* @__PURE__ */ jsx20(PanelLeftIcon, {}),
1627
- /* @__PURE__ */ jsx20("span", { className: "sr-only", children: "Toggle Sidebar" })
1711
+ /* @__PURE__ */ jsx21(PanelLeftIcon, {}),
1712
+ /* @__PURE__ */ jsx21("span", { className: "sr-only", children: "Toggle Sidebar" })
1628
1713
  ]
1629
1714
  }
1630
1715
  );
1631
1716
  }
1632
1717
  function SidebarRail({ className, ...props }) {
1633
1718
  const { toggleSidebar } = useSidebar();
1634
- return /* @__PURE__ */ jsx20(
1719
+ return /* @__PURE__ */ jsx21(
1635
1720
  "button",
1636
1721
  {
1637
1722
  "data-sidebar": "rail",
@@ -1654,7 +1739,7 @@ function SidebarRail({ className, ...props }) {
1654
1739
  );
1655
1740
  }
1656
1741
  function SidebarInset({ className, ...props }) {
1657
- return /* @__PURE__ */ jsx20(
1742
+ return /* @__PURE__ */ jsx21(
1658
1743
  "main",
1659
1744
  {
1660
1745
  "data-slot": "sidebar-inset",
@@ -1667,7 +1752,7 @@ function SidebarInset({ className, ...props }) {
1667
1752
  );
1668
1753
  }
1669
1754
  function SidebarHeader({ className, ...props }) {
1670
- return /* @__PURE__ */ jsx20(
1755
+ return /* @__PURE__ */ jsx21(
1671
1756
  "div",
1672
1757
  {
1673
1758
  "data-slot": "sidebar-header",
@@ -1681,7 +1766,7 @@ function SidebarHeader({ className, ...props }) {
1681
1766
  );
1682
1767
  }
1683
1768
  function SidebarFooter({ className, ...props }) {
1684
- return /* @__PURE__ */ jsx20(
1769
+ return /* @__PURE__ */ jsx21(
1685
1770
  "div",
1686
1771
  {
1687
1772
  "data-slot": "sidebar-footer",
@@ -1692,7 +1777,7 @@ function SidebarFooter({ className, ...props }) {
1692
1777
  );
1693
1778
  }
1694
1779
  function SidebarContent({ className, ...props }) {
1695
- return /* @__PURE__ */ jsx20(
1780
+ return /* @__PURE__ */ jsx21(
1696
1781
  "div",
1697
1782
  {
1698
1783
  "data-slot": "sidebar-content",
@@ -1706,7 +1791,7 @@ function SidebarContent({ className, ...props }) {
1706
1791
  );
1707
1792
  }
1708
1793
  function SidebarGroup({ className, ...props }) {
1709
- return /* @__PURE__ */ jsx20(
1794
+ return /* @__PURE__ */ jsx21(
1710
1795
  "div",
1711
1796
  {
1712
1797
  "data-slot": "sidebar-group",
@@ -1720,7 +1805,7 @@ function SidebarGroupContent({
1720
1805
  className,
1721
1806
  ...props
1722
1807
  }) {
1723
- return /* @__PURE__ */ jsx20(
1808
+ return /* @__PURE__ */ jsx21(
1724
1809
  "div",
1725
1810
  {
1726
1811
  "data-slot": "sidebar-group-content",
@@ -1731,7 +1816,7 @@ function SidebarGroupContent({
1731
1816
  );
1732
1817
  }
1733
1818
  function SidebarMenu({ className, ...props }) {
1734
- return /* @__PURE__ */ jsx20(
1819
+ return /* @__PURE__ */ jsx21(
1735
1820
  "ul",
1736
1821
  {
1737
1822
  "data-slot": "sidebar-menu",
@@ -1742,7 +1827,7 @@ function SidebarMenu({ className, ...props }) {
1742
1827
  );
1743
1828
  }
1744
1829
  function SidebarMenuItem({ className, ...props }) {
1745
- return /* @__PURE__ */ jsx20(
1830
+ return /* @__PURE__ */ jsx21(
1746
1831
  "li",
1747
1832
  {
1748
1833
  "data-slot": "sidebar-menu-item",
@@ -1783,7 +1868,7 @@ function SidebarMenuButton({
1783
1868
  }) {
1784
1869
  const Comp = asChild ? Slot4.Root : "button";
1785
1870
  const { isMobile, state } = useSidebar();
1786
- const button = /* @__PURE__ */ jsx20(
1871
+ const button = /* @__PURE__ */ jsx21(
1787
1872
  Comp,
1788
1873
  {
1789
1874
  "data-slot": "sidebar-menu-button",
@@ -1802,9 +1887,9 @@ function SidebarMenuButton({
1802
1887
  children: tooltip
1803
1888
  };
1804
1889
  }
1805
- return /* @__PURE__ */ jsxs11(Tooltip, { children: [
1806
- /* @__PURE__ */ jsx20(TooltipTrigger, { asChild: true, children: button }),
1807
- /* @__PURE__ */ jsx20(
1890
+ return /* @__PURE__ */ jsxs12(Tooltip, { children: [
1891
+ /* @__PURE__ */ jsx21(TooltipTrigger, { asChild: true, children: button }),
1892
+ /* @__PURE__ */ jsx21(
1808
1893
  TooltipContent,
1809
1894
  {
1810
1895
  side: "right",
@@ -1822,7 +1907,7 @@ function SidebarMenuAction({
1822
1907
  ...props
1823
1908
  }) {
1824
1909
  const Comp = asChild ? Slot4.Root : "button";
1825
- return /* @__PURE__ */ jsx20(
1910
+ return /* @__PURE__ */ jsx21(
1826
1911
  Comp,
1827
1912
  {
1828
1913
  "data-slot": "sidebar-menu-action",
@@ -1838,7 +1923,7 @@ function SidebarMenuAction({
1838
1923
  }
1839
1924
 
1840
1925
  // scaffold/src/components/chat/identity-control.tsx
1841
- import { jsx as jsx21, jsxs as jsxs12 } from "react/jsx-runtime";
1926
+ import { jsx as jsx22, jsxs as jsxs13 } from "react/jsx-runtime";
1842
1927
  function identityLabel(identity) {
1843
1928
  if (identity.displayName) return identity.displayName;
1844
1929
  if (identity.state === "authenticated") return "Signed in";
@@ -1848,44 +1933,43 @@ function identityLabel(identity) {
1848
1933
  return "Guest";
1849
1934
  }
1850
1935
  function IdentityAvatar({ identity }) {
1851
- const label = identityLabel(identity);
1852
1936
  if (identity.avatarUrl) {
1853
- return /* @__PURE__ */ jsx21("img", { alt: "", className: "size-6 rounded-full object-cover", src: identity.avatarUrl });
1937
+ return /* @__PURE__ */ jsx22("img", { alt: "", className: "size-6 rounded-full object-cover", src: identity.avatarUrl });
1854
1938
  }
1855
- return /* @__PURE__ */ jsx21("span", { className: "flex size-6 items-center justify-center rounded-full bg-sidebar-accent text-[10px] font-semibold", "aria-hidden": "true", children: identity.displayName ? identity.displayName.slice(0, 1).toUpperCase() : /* @__PURE__ */ jsx21(UserRound, { className: "size-3.5" }) });
1939
+ return /* @__PURE__ */ jsx22("span", { className: "flex size-6 items-center justify-center rounded-full bg-sidebar-accent text-[10px] font-semibold", "aria-hidden": "true", children: identity.displayName ? identity.displayName.slice(0, 1).toUpperCase() : /* @__PURE__ */ jsx22(UserRound, { className: "size-3.5" }) });
1856
1940
  }
1857
1941
  function IdentityControl({ identity }) {
1858
1942
  const label = identityLabel(identity);
1859
1943
  const canSignOut = identity.state === "authenticated" && Boolean(identity.onSignOut);
1860
1944
  const canSignIn = (identity.state === "guest" || identity.state === "signed_out" || identity.state === "error") && Boolean(identity.onSignIn);
1861
- return /* @__PURE__ */ jsx21(SidebarMenu, { className: "w-full min-w-0", children: /* @__PURE__ */ jsx21(SidebarMenuItem, { className: "w-full min-w-0", children: /* @__PURE__ */ jsxs12(DropdownMenu, { children: [
1862
- /* @__PURE__ */ jsx21(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs12(
1945
+ return /* @__PURE__ */ jsx22(SidebarMenu, { className: "w-full min-w-0", children: /* @__PURE__ */ jsx22(SidebarMenuItem, { className: "w-full min-w-0", children: /* @__PURE__ */ jsxs13(DropdownMenu, { children: [
1946
+ /* @__PURE__ */ jsx22(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs13(
1863
1947
  SidebarMenuButton,
1864
1948
  {
1865
1949
  size: "lg",
1866
1950
  "aria-label": `Account: ${label}`,
1867
1951
  className: "w-full min-w-0 justify-start",
1868
1952
  children: [
1869
- /* @__PURE__ */ jsx21(IdentityAvatar, { identity }),
1870
- /* @__PURE__ */ jsxs12("span", { className: "min-w-0 flex-1 overflow-hidden text-start", children: [
1871
- /* @__PURE__ */ jsx21("span", { className: "block truncate text-sm font-medium", children: label }),
1872
- identity.secondaryText ? /* @__PURE__ */ jsx21("span", { className: "block truncate text-xs text-muted-foreground", children: identity.secondaryText }) : null
1953
+ /* @__PURE__ */ jsx22(IdentityAvatar, { identity }),
1954
+ /* @__PURE__ */ jsxs13("span", { className: "min-w-0 flex-1 overflow-hidden text-start", children: [
1955
+ /* @__PURE__ */ jsx22("span", { className: "block truncate text-sm font-medium", children: label }),
1956
+ identity.secondaryText ? /* @__PURE__ */ jsx22("span", { className: "block truncate text-xs text-muted-foreground", children: identity.secondaryText }) : null
1873
1957
  ] })
1874
1958
  ]
1875
1959
  }
1876
1960
  ) }),
1877
- /* @__PURE__ */ jsxs12(DropdownMenuContent, { align: "start", side: "top", sideOffset: 8, children: [
1878
- /* @__PURE__ */ jsxs12(DropdownMenuLabel, { children: [
1879
- /* @__PURE__ */ jsx21("span", { className: "block truncate", children: label }),
1880
- identity.secondaryText ? /* @__PURE__ */ jsx21("span", { className: "mt-0.5 block truncate text-xs font-normal text-muted-foreground", children: identity.secondaryText }) : null
1961
+ /* @__PURE__ */ jsxs13(DropdownMenuContent, { align: "start", side: "top", sideOffset: 8, children: [
1962
+ /* @__PURE__ */ jsxs13(DropdownMenuLabel, { children: [
1963
+ /* @__PURE__ */ jsx22("span", { className: "block truncate", children: label }),
1964
+ identity.secondaryText ? /* @__PURE__ */ jsx22("span", { className: "mt-0.5 block truncate text-xs font-normal text-muted-foreground", children: identity.secondaryText }) : null
1881
1965
  ] }),
1882
- canSignIn || canSignOut ? /* @__PURE__ */ jsx21(DropdownMenuSeparator, {}) : null,
1883
- canSignIn ? /* @__PURE__ */ jsxs12(DropdownMenuItem, { onSelect: () => identity.onSignIn?.(), children: [
1884
- /* @__PURE__ */ jsx21(LogIn, {}),
1966
+ canSignIn || canSignOut ? /* @__PURE__ */ jsx22(DropdownMenuSeparator, {}) : null,
1967
+ canSignIn ? /* @__PURE__ */ jsxs13(DropdownMenuItem, { onSelect: () => identity.onSignIn?.(), children: [
1968
+ /* @__PURE__ */ jsx22(LogIn, {}),
1885
1969
  "Sign in"
1886
1970
  ] }) : null,
1887
- canSignOut ? /* @__PURE__ */ jsxs12(DropdownMenuItem, { onSelect: () => identity.onSignOut?.(), children: [
1888
- /* @__PURE__ */ jsx21(LogOut, {}),
1971
+ canSignOut ? /* @__PURE__ */ jsxs13(DropdownMenuItem, { onSelect: () => identity.onSignOut?.(), children: [
1972
+ /* @__PURE__ */ jsx22(LogOut, {}),
1889
1973
  "Sign out"
1890
1974
  ] }) : null
1891
1975
  ] })
@@ -1893,25 +1977,25 @@ function IdentityControl({ identity }) {
1893
1977
  }
1894
1978
 
1895
1979
  // scaffold/src/components/chat/thread-sidebar.tsx
1896
- import { jsx as jsx22, jsxs as jsxs13 } from "react/jsx-runtime";
1980
+ import { jsx as jsx23, jsxs as jsxs14 } from "react/jsx-runtime";
1897
1981
  function ThreadStatus({ thread }) {
1898
1982
  const activity = getThreadActivity(thread);
1899
1983
  if (!activity.isSpinning) return null;
1900
1984
  const label = activity.status === "cancelling" ? "Cancelling" : activity.status === "queued" ? "Queued" : "Running";
1901
- return /* @__PURE__ */ jsx22(
1985
+ return /* @__PURE__ */ jsx23(
1902
1986
  "span",
1903
1987
  {
1904
1988
  className: "pointer-events-none absolute end-1 top-1/2 hidden size-5 -translate-y-1/2 items-center justify-center text-muted-foreground transition-opacity group-hover/menu-item:opacity-0 md:flex",
1905
1989
  role: "status",
1906
1990
  "aria-label": label,
1907
1991
  title: label,
1908
- children: /* @__PURE__ */ jsx22(LoaderCircle2, { className: "size-3.5 animate-spin" })
1992
+ children: /* @__PURE__ */ jsx23(LoaderCircle2, { className: "size-3.5 animate-spin" })
1909
1993
  }
1910
1994
  );
1911
1995
  }
1912
1996
  function ThreadSidebar({ activeThreadId, hasMore, identity, isLoading, isLoadingMore, onDelete, onLoadMore, onNew, onOpen, threads }) {
1913
1997
  const { setOpenMobile } = useSidebar();
1914
- return /* @__PURE__ */ jsxs13(
1998
+ return /* @__PURE__ */ jsxs14(
1915
1999
  Sidebar,
1916
2000
  {
1917
2001
  "aria-label": "Conversations",
@@ -1919,19 +2003,19 @@ function ThreadSidebar({ activeThreadId, hasMore, identity, isLoading, isLoading
1919
2003
  "data-agents24-chat-sidebar": "",
1920
2004
  role: "navigation",
1921
2005
  children: [
1922
- /* @__PURE__ */ jsxs13(SidebarHeader, { className: "gap-1 p-2", children: [
1923
- /* @__PURE__ */ jsx22("div", { className: "h-8 shrink-0", "aria-hidden": "true" }),
1924
- /* @__PURE__ */ jsxs13(Button, { className: "w-full justify-start", variant: "ghost", onClick: () => {
2006
+ /* @__PURE__ */ jsxs14(SidebarHeader, { className: "gap-1 p-2", children: [
2007
+ /* @__PURE__ */ jsx23("div", { className: "h-8 shrink-0", "aria-hidden": "true" }),
2008
+ /* @__PURE__ */ jsxs14(Button, { className: "w-full justify-start", variant: "ghost", onClick: () => {
1925
2009
  onNew();
1926
2010
  setOpenMobile(false);
1927
2011
  }, children: [
1928
- /* @__PURE__ */ jsx22(SquarePen, {}),
2012
+ /* @__PURE__ */ jsx23(SquarePen, {}),
1929
2013
  " New chat"
1930
2014
  ] })
1931
2015
  ] }),
1932
- /* @__PURE__ */ jsx22(SidebarContent, { children: /* @__PURE__ */ jsx22(SidebarGroup, { children: /* @__PURE__ */ jsxs13(SidebarGroupContent, { children: [
1933
- /* @__PURE__ */ jsx22(SidebarMenu, { children: threads.map((thread) => /* @__PURE__ */ jsxs13(SidebarMenuItem, { children: [
1934
- /* @__PURE__ */ jsx22(
2016
+ /* @__PURE__ */ jsx23(SidebarContent, { children: /* @__PURE__ */ jsx23(SidebarGroup, { children: /* @__PURE__ */ jsxs14(SidebarGroupContent, { children: [
2017
+ /* @__PURE__ */ jsx23(SidebarMenu, { children: threads.map((thread) => /* @__PURE__ */ jsxs14(SidebarMenuItem, { children: [
2018
+ /* @__PURE__ */ jsx23(
1935
2019
  SidebarMenuButton,
1936
2020
  {
1937
2021
  className: "pr-8 pl-3",
@@ -1941,11 +2025,11 @@ function ThreadSidebar({ activeThreadId, hasMore, identity, isLoading, isLoading
1941
2025
  void onOpen(thread.id).catch(() => void 0);
1942
2026
  setOpenMobile(false);
1943
2027
  },
1944
- children: /* @__PURE__ */ jsx22("span", { className: "min-w-0 truncate", children: String(thread.title || "New conversation") })
2028
+ children: /* @__PURE__ */ jsx23("span", { className: "min-w-0 truncate", children: String(thread.title || "New conversation") })
1945
2029
  }
1946
2030
  ),
1947
- /* @__PURE__ */ jsx22(ThreadStatus, { thread }),
1948
- /* @__PURE__ */ jsx22(
2031
+ /* @__PURE__ */ jsx23(ThreadStatus, { thread }),
2032
+ /* @__PURE__ */ jsx23(
1949
2033
  SidebarMenuAction,
1950
2034
  {
1951
2035
  showOnHover: true,
@@ -1955,12 +2039,12 @@ function ThreadSidebar({ activeThreadId, hasMore, identity, isLoading, isLoading
1955
2039
  event.stopPropagation();
1956
2040
  if (window.confirm("Delete this conversation?")) void onDelete(thread.id).catch(() => void 0);
1957
2041
  },
1958
- children: /* @__PURE__ */ jsx22(Trash2, {})
2042
+ children: /* @__PURE__ */ jsx23(Trash2, {})
1959
2043
  }
1960
2044
  )
1961
2045
  ] }, thread.id)) }),
1962
- !isLoading && threads.length === 0 && /* @__PURE__ */ jsx22("p", { className: "px-2 py-8 text-center text-xs text-muted-foreground", children: "No conversations" }),
1963
- hasMore && /* @__PURE__ */ jsxs13(
2046
+ !isLoading && threads.length === 0 && /* @__PURE__ */ jsx23("p", { className: "px-2 py-8 text-center text-xs text-muted-foreground", children: "No conversations" }),
2047
+ hasMore && /* @__PURE__ */ jsxs14(
1964
2048
  Button,
1965
2049
  {
1966
2050
  className: "mt-2 w-full",
@@ -1969,14 +2053,14 @@ function ThreadSidebar({ activeThreadId, hasMore, identity, isLoading, isLoading
1969
2053
  variant: "ghost",
1970
2054
  onClick: () => void onLoadMore().catch(() => void 0),
1971
2055
  children: [
1972
- isLoadingMore && /* @__PURE__ */ jsx22(LoaderCircle2, { className: "animate-spin" }),
2056
+ isLoadingMore && /* @__PURE__ */ jsx23(LoaderCircle2, { className: "animate-spin" }),
1973
2057
  "Load more"
1974
2058
  ]
1975
2059
  }
1976
2060
  )
1977
2061
  ] }) }) }),
1978
- /* @__PURE__ */ jsx22(SidebarFooter, { className: "w-full min-w-0 p-2", children: /* @__PURE__ */ jsx22(IdentityControl, { identity }) }),
1979
- /* @__PURE__ */ jsx22(SidebarRail, {})
2062
+ /* @__PURE__ */ jsx23(SidebarFooter, { className: "w-full min-w-0 p-2", children: /* @__PURE__ */ jsx23(IdentityControl, { identity }) }),
2063
+ /* @__PURE__ */ jsx23(SidebarRail, {})
1980
2064
  ]
1981
2065
  }
1982
2066
  );
@@ -1984,7 +2068,7 @@ function ThreadSidebar({ activeThreadId, hasMore, identity, isLoading, isLoading
1984
2068
 
1985
2069
  // scaffold/src/components/chat/turn-outline.tsx
1986
2070
  import { LatestThreadScroller } from "@agents24/chat-react";
1987
- import { jsx as jsx23 } from "react/jsx-runtime";
2071
+ import { jsx as jsx24 } from "react/jsx-runtime";
1988
2072
  function turnsFromMessages(messages) {
1989
2073
  return messages.reduce((turns, message) => {
1990
2074
  if (message.role === "user") {
@@ -2003,7 +2087,7 @@ function turnsFromMessages(messages) {
2003
2087
  }, []);
2004
2088
  }
2005
2089
  function TurnOutline({ messages }) {
2006
- return /* @__PURE__ */ jsx23(
2090
+ return /* @__PURE__ */ jsx24(
2007
2091
  LatestThreadScroller.Outline,
2008
2092
  {
2009
2093
  items: turnsFromMessages(messages),
@@ -2030,7 +2114,7 @@ import {
2030
2114
  LatestThreadScroller as LatestThreadScroller2,
2031
2115
  latestThreadAnchorId
2032
2116
  } from "@agents24/chat-react";
2033
- import { Fragment as Fragment3, jsx as jsx24, jsxs as jsxs14 } from "react/jsx-runtime";
2117
+ import { Fragment as Fragment3, jsx as jsx25, jsxs as jsxs15 } from "react/jsx-runtime";
2034
2118
  var LOADING_INPUT_CONTRACT = {
2035
2119
  version: "agents24.runtime-inputs.v2",
2036
2120
  modalities: {
@@ -2043,31 +2127,31 @@ var LOADING_INPUT_CONTRACT = {
2043
2127
  retention: { mode: "thread_lifetime" }
2044
2128
  };
2045
2129
  function PersistentSidebarTrigger() {
2046
- return /* @__PURE__ */ jsx24("div", { className: "pointer-events-none fixed start-3 top-0 z-[60] flex h-12 items-center", children: /* @__PURE__ */ jsx24(SidebarTrigger, { className: "pointer-events-auto [&_svg]:size-[18px]" }) });
2130
+ return /* @__PURE__ */ jsx25("div", { className: "pointer-events-none fixed start-3 top-0 z-[60] flex h-12 items-center", children: /* @__PURE__ */ jsx25(SidebarTrigger, { className: "pointer-events-auto [&_svg]:size-[18px]" }) });
2047
2131
  }
2048
2132
  function CollapsedHeaderActions({ onNew }) {
2049
2133
  const { isMobile, openMobile, state } = useSidebar();
2050
2134
  const sidebarVisible = isMobile ? openMobile : state === "expanded";
2051
2135
  if (sidebarVisible) return null;
2052
- return /* @__PURE__ */ jsxs14(Fragment3, { children: [
2053
- /* @__PURE__ */ jsx24("div", { className: "h-8 w-17 shrink-0", "aria-hidden": "true" }),
2054
- /* @__PURE__ */ jsx24("div", { className: "pointer-events-none fixed start-12 top-0 z-[60] flex h-12 items-center", children: /* @__PURE__ */ jsx24(Button, { className: "pointer-events-auto", variant: "ghost", size: "icon-sm", "aria-label": "New chat", onClick: onNew, children: /* @__PURE__ */ jsx24(SquarePen2, {}) }) })
2136
+ return /* @__PURE__ */ jsxs15(Fragment3, { children: [
2137
+ /* @__PURE__ */ jsx25("div", { className: "h-8 w-17 shrink-0", "aria-hidden": "true" }),
2138
+ /* @__PURE__ */ jsx25("div", { className: "pointer-events-none fixed start-12 top-0 z-[60] flex h-12 items-center", children: /* @__PURE__ */ jsx25(Button, { className: "pointer-events-auto", variant: "ghost", size: "icon-sm", "aria-label": "New chat", onClick: onNew, children: /* @__PURE__ */ jsx25(SquarePen2, {}) }) })
2055
2139
  ] });
2056
2140
  }
2057
2141
  function OperationErrorNotice({ runtime }) {
2058
2142
  const error = runtime.operationError || runtime.backgroundError;
2059
2143
  if (!error) return null;
2060
- return /* @__PURE__ */ jsx24("div", { className: "pointer-events-none absolute inset-x-0 top-3 z-40 flex justify-center px-4", children: /* @__PURE__ */ jsxs14("div", { className: "pointer-events-auto flex max-w-lg items-start gap-2 rounded-lg border border-destructive/20 bg-background px-3 py-2 text-sm shadow-md", role: "alert", children: [
2061
- /* @__PURE__ */ jsx24(CircleAlert, { className: "mt-0.5 size-4 shrink-0 text-destructive" }),
2062
- /* @__PURE__ */ jsx24("span", { className: "min-w-0 flex-1 wrap-break-word", children: agentChatRuntimeErrorMessage(error) || "The chat could not complete that action." }),
2063
- /* @__PURE__ */ jsx24(
2144
+ return /* @__PURE__ */ jsx25("div", { className: "pointer-events-none absolute inset-x-0 top-3 z-40 flex justify-center px-4", children: /* @__PURE__ */ jsxs15("div", { className: "pointer-events-auto flex max-w-lg items-start gap-2 rounded-lg border border-destructive/20 bg-background px-3 py-2 text-sm shadow-md", role: "alert", children: [
2145
+ /* @__PURE__ */ jsx25(CircleAlert, { className: "mt-0.5 size-4 shrink-0 text-destructive" }),
2146
+ /* @__PURE__ */ jsx25("span", { className: "min-w-0 flex-1 wrap-break-word", children: agentChatRuntimeErrorMessage(error) || "The chat could not complete that action." }),
2147
+ /* @__PURE__ */ jsx25(
2064
2148
  Button,
2065
2149
  {
2066
2150
  "aria-label": "Dismiss error",
2067
2151
  onClick: runtime.operationError ? runtime.clearOperationError : runtime.controller.clearBackgroundError,
2068
2152
  size: "icon-xs",
2069
2153
  variant: "ghost",
2070
- children: /* @__PURE__ */ jsx24(X, {})
2154
+ children: /* @__PURE__ */ jsx25(X, {})
2071
2155
  }
2072
2156
  )
2073
2157
  ] }) });
@@ -2078,6 +2162,7 @@ function ChatShell({
2078
2162
  runtime,
2079
2163
  sidebar
2080
2164
  }) {
2165
+ const Composer = components?.Composer || ChatComposer;
2081
2166
  const { controller } = runtime;
2082
2167
  const isUploading = controller.runState === "uploading";
2083
2168
  const isRunning = controller.runState === "streaming" || controller.runState === "reconnecting" || controller.runState === "paused" || controller.runState === "cancelling";
@@ -2087,11 +2172,11 @@ function ChatShell({
2087
2172
  const hasBlockingError = Boolean(runtime.fatalError && controller.messages.length === 0);
2088
2173
  const isEmptyState = controller.activeThreadId === null && controller.messages.length === 0 && !hasBlockingError;
2089
2174
  const latestAnchorId = latestThreadAnchorId(controller.messages);
2090
- const composerOverlayRef = React8.useRef(null);
2091
- const [composerOverlayHeight, setComposerOverlayHeight] = React8.useState(192);
2092
- const [restoredDraft, setRestoredDraft] = React8.useState(null);
2093
- const draftRevisionRef = React8.useRef(0);
2094
- const retractForEdit = React8.useCallback(async (message) => {
2175
+ const composerOverlayRef = React9.useRef(null);
2176
+ const [composerOverlayHeight, setComposerOverlayHeight] = React9.useState(192);
2177
+ const [restoredDraft, setRestoredDraft] = React9.useState(null);
2178
+ const draftRevisionRef = React9.useRef(0);
2179
+ const retractForEdit = React9.useCallback(async (message) => {
2095
2180
  const draft = await controller.retractPendingMessage(message);
2096
2181
  setRestoredDraft({
2097
2182
  revision: ++draftRevisionRef.current,
@@ -2099,7 +2184,7 @@ function ChatShell({
2099
2184
  files: queuedMessageComposerFiles(draft)
2100
2185
  });
2101
2186
  }, [controller]);
2102
- React8.useLayoutEffect(() => {
2187
+ React9.useLayoutEffect(() => {
2103
2188
  const overlay = composerOverlayRef.current;
2104
2189
  if (!overlay || isEmptyState) return;
2105
2190
  const updateHeight = () => {
@@ -2110,15 +2195,15 @@ function ChatShell({
2110
2195
  resizeObserver.observe(overlay);
2111
2196
  return () => resizeObserver.disconnect();
2112
2197
  }, [isEmptyState]);
2113
- return /* @__PURE__ */ jsxs14(
2198
+ return /* @__PURE__ */ jsxs15(
2114
2199
  SidebarProvider,
2115
2200
  {
2116
2201
  className: "h-svh min-h-0 overflow-hidden",
2117
2202
  "data-agents24-chat": "",
2118
2203
  "data-agents24-chat-layout": "",
2119
2204
  children: [
2120
- /* @__PURE__ */ jsx24(PersistentSidebarTrigger, {}),
2121
- sidebar || /* @__PURE__ */ jsx24(
2205
+ /* @__PURE__ */ jsx25(PersistentSidebarTrigger, {}),
2206
+ sidebar || /* @__PURE__ */ jsx25(
2122
2207
  ThreadSidebar,
2123
2208
  {
2124
2209
  activeThreadId: controller.activeThreadId,
@@ -2133,57 +2218,58 @@ function ChatShell({
2133
2218
  onOpen: runtime.openThread
2134
2219
  }
2135
2220
  ),
2136
- /* @__PURE__ */ jsxs14(SidebarInset, { className: "h-svh min-h-0 overflow-hidden bg-background", children: [
2137
- /* @__PURE__ */ jsxs14(
2221
+ /* @__PURE__ */ jsxs15(SidebarInset, { className: "h-svh min-h-0 overflow-hidden bg-background", children: [
2222
+ /* @__PURE__ */ jsxs15(
2138
2223
  "header",
2139
2224
  {
2140
2225
  className: "flex h-12 shrink-0 items-center gap-2 border-b border-border/50 px-3 sm:px-4",
2141
2226
  "data-agents24-chat-header": "",
2142
2227
  children: [
2143
- /* @__PURE__ */ jsx24(CollapsedHeaderActions, { onNew: runtime.newThread }),
2144
- /* @__PURE__ */ jsx24("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsx24("h1", { className: "truncate text-sm font-medium", children: String(activeTitle || "New chat") }) }),
2145
- /* @__PURE__ */ jsx24(ThemeMenu, {})
2228
+ /* @__PURE__ */ jsx25(CollapsedHeaderActions, { onNew: runtime.newThread }),
2229
+ /* @__PURE__ */ jsx25("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsx25("h1", { className: "truncate text-sm font-medium", children: String(activeTitle || "New chat") }) }),
2230
+ /* @__PURE__ */ jsx25(ThemeMenu, {})
2146
2231
  ]
2147
2232
  }
2148
2233
  ),
2149
- /* @__PURE__ */ jsxs14(
2234
+ /* @__PURE__ */ jsxs15(
2150
2235
  "section",
2151
2236
  {
2152
2237
  "aria-label": "Conversation",
2153
2238
  className: "relative flex min-h-0 flex-1 flex-col overflow-hidden",
2154
2239
  "data-agents24-chat-transcript": "",
2155
2240
  children: [
2156
- /* @__PURE__ */ jsx24(OperationErrorNotice, { runtime }),
2157
- /* @__PURE__ */ jsxs14(LatestThreadScroller2.Provider, { autoScroll: true, defaultScrollPosition: "last-anchor", scrollPreviousItemPeek: 64, children: [
2158
- !isEmptyState && /* @__PURE__ */ jsxs14(LatestThreadScroller2.Root, { className: "relative min-h-0 flex-1", children: [
2159
- /* @__PURE__ */ jsx24(LatestThreadScroller2.Viewport, { preserveScrollOnPrepend: true, children: /* @__PURE__ */ jsxs14(
2241
+ /* @__PURE__ */ jsx25(OperationErrorNotice, { runtime }),
2242
+ /* @__PURE__ */ jsx25(SubagentTasks, { threadId: controller.activeThreadId }),
2243
+ /* @__PURE__ */ jsxs15(LatestThreadScroller2.Provider, { autoScroll: true, defaultScrollPosition: "last-anchor", scrollPreviousItemPeek: 64, children: [
2244
+ !isEmptyState && /* @__PURE__ */ jsxs15(LatestThreadScroller2.Root, { className: "relative min-h-0 flex-1", children: [
2245
+ /* @__PURE__ */ jsx25(LatestThreadScroller2.Viewport, { preserveScrollOnPrepend: true, children: /* @__PURE__ */ jsxs15(
2160
2246
  LatestThreadScroller2.Content,
2161
2247
  {
2162
2248
  className: "mx-auto w-full max-w-3xl gap-3 px-4 pt-8 pb-0 sm:px-6 sm:pt-12",
2163
2249
  style: { paddingBottom: composerOverlayHeight + 72 },
2164
2250
  children: [
2165
- controller.hasOlderTurns && /* @__PURE__ */ jsxs14(Button, { className: "self-center", variant: "ghost", size: "sm", disabled: controller.isLoadingOlder, onClick: () => {
2251
+ controller.hasOlderTurns && /* @__PURE__ */ jsxs15(Button, { className: "self-center", variant: "ghost", size: "sm", disabled: controller.isLoadingOlder, onClick: () => {
2166
2252
  void controller.loadOlder().catch(() => void 0);
2167
2253
  }, children: [
2168
- controller.isLoadingOlder && /* @__PURE__ */ jsx24(LoaderCircle3, { className: "animate-spin" }),
2254
+ controller.isLoadingOlder && /* @__PURE__ */ jsx25(LoaderCircle3, { className: "animate-spin" }),
2169
2255
  " Load earlier messages"
2170
2256
  ] }),
2171
- runtime.isBootstrapping && /* @__PURE__ */ jsx24("div", { "aria-label": "Loading conversation", className: "flex flex-1 items-center justify-center py-24 text-muted-foreground", role: "status", children: /* @__PURE__ */ jsx24(LoaderCircle3, { className: "size-4 animate-spin" }) }),
2172
- hasBlockingError && /* @__PURE__ */ jsxs14("div", { className: "flex flex-1 flex-col items-center justify-center gap-3 py-24 text-center", children: [
2173
- /* @__PURE__ */ jsx24(CircleAlert, { className: "size-5 text-destructive" }),
2174
- /* @__PURE__ */ jsxs14("div", { children: [
2175
- /* @__PURE__ */ jsx24("h2", { className: "font-medium", children: "Couldn\u2019t connect" }),
2176
- /* @__PURE__ */ jsx24("p", { className: "mt-1 max-w-md text-sm text-muted-foreground", children: runtime.errorMessage })
2257
+ runtime.isBootstrapping && /* @__PURE__ */ jsx25("div", { "aria-label": "Loading conversation", className: "flex flex-1 items-center justify-center py-24 text-muted-foreground", role: "status", children: /* @__PURE__ */ jsx25(LoaderCircle3, { className: "size-4 animate-spin" }) }),
2258
+ hasBlockingError && /* @__PURE__ */ jsxs15("div", { className: "flex flex-1 flex-col items-center justify-center gap-3 py-24 text-center", children: [
2259
+ /* @__PURE__ */ jsx25(CircleAlert, { className: "size-5 text-destructive" }),
2260
+ /* @__PURE__ */ jsxs15("div", { children: [
2261
+ /* @__PURE__ */ jsx25("h2", { className: "font-medium", children: "Couldn\u2019t connect" }),
2262
+ /* @__PURE__ */ jsx25("p", { className: "mt-1 max-w-md text-sm text-muted-foreground", children: runtime.errorMessage })
2177
2263
  ] }),
2178
- /* @__PURE__ */ jsx24(Button, { variant: "outline", onClick: () => void runtime.retryBootstrap(), children: "Try again" })
2264
+ /* @__PURE__ */ jsx25(Button, { variant: "outline", onClick: () => void runtime.retryBootstrap(), children: "Try again" })
2179
2265
  ] }),
2180
- controller.messages.map((message) => /* @__PURE__ */ jsx24(
2266
+ controller.messages.map((message) => /* @__PURE__ */ jsx25(
2181
2267
  LatestThreadScroller2.Item,
2182
2268
  {
2183
2269
  messageId: message.id,
2184
2270
  scrollAnchor: isRunning && message.id === latestAnchorId,
2185
2271
  className: "overflow-visible [content-visibility:visible]",
2186
- children: /* @__PURE__ */ jsx24(
2272
+ children: /* @__PURE__ */ jsx25(
2187
2273
  ChatMessage,
2188
2274
  {
2189
2275
  animateCompletion: wasLiveRun(message.runId),
@@ -2209,9 +2295,9 @@ function ChatShell({
2209
2295
  ]
2210
2296
  }
2211
2297
  ) }),
2212
- /* @__PURE__ */ jsx24(TurnOutline, { messages: controller.messages })
2298
+ /* @__PURE__ */ jsx25(TurnOutline, { messages: controller.messages })
2213
2299
  ] }),
2214
- /* @__PURE__ */ jsxs14(
2300
+ /* @__PURE__ */ jsxs15(
2215
2301
  "div",
2216
2302
  {
2217
2303
  ref: composerOverlayRef,
@@ -2220,56 +2306,52 @@ function ChatShell({
2220
2306
  background: "linear-gradient(to bottom, transparent 0%, color-mix(in oklch, var(--background) 85%, transparent) 45%, var(--background) 65%, var(--background) 100%)"
2221
2307
  },
2222
2308
  children: [
2223
- isEmptyState ? /* @__PURE__ */ jsx24("div", { className: "mx-auto mb-8 w-full max-w-3xl px-4 text-center sm:px-6", children: /* @__PURE__ */ jsx24("h2", { className: "text-3xl font-semibold tracking-tight sm:text-4xl", children: "Ready when you are." }) }) : /* @__PURE__ */ jsx24("div", { className: "pointer-events-auto absolute inset-x-0 top-0 flex -translate-y-full justify-center pb-3", children: /* @__PURE__ */ jsx24(LatestThreadScroller2.Button, { className: "!relative !inset-auto !translate-x-0 rtl:!translate-x-0" }) }),
2224
- /* @__PURE__ */ jsxs14("div", { className: "pointer-events-auto mx-auto w-full max-w-3xl px-4 sm:px-6", children: [
2225
- /* @__PURE__ */ jsx24(
2226
- AgentChatQueue,
2227
- {
2228
- pending: controller.pendingMessages,
2229
- queue: controller.messageQueue,
2230
- canSteer: controller.runState === "streaming",
2231
- onSteer: controller.steerPendingMessage,
2232
- onRemove: controller.removePendingMessage,
2233
- onEdit: retractForEdit,
2234
- onResume: controller.resumeQueue
2235
- }
2236
- ),
2237
- /* @__PURE__ */ jsx24(
2238
- ChatComposer,
2239
- {
2240
- restoredDraft,
2241
- inputContract: runtime.inputContract || LOADING_INPUT_CONTRACT,
2242
- contextWindow: runtime.contextWindow,
2243
- disabled: runtime.isBootstrapping || runtime.isChangingAgent || isUploading || hasBlockingError,
2244
- focusKey: controller.activeThreadId,
2245
- forceExpanded: isEmptyState,
2246
- isCancelling,
2247
- isRunning,
2248
- agentId: runtime.agentId,
2249
- agents: runtime.agents,
2250
- onAgentChange: (agentId) => {
2251
- void runtime.changeAgent(agentId);
2252
- },
2253
- onStop: () => {
2254
- void controller.cancelRun().catch(() => void 0);
2255
- },
2256
- modelId: runtime.modelId,
2257
- models: runtime.models,
2258
- onModelChange: runtime.changeModel,
2259
- onSubmit: (text, files) => runtime.submit({
2260
- text,
2261
- files: files.filter((file) => file.source).map((file) => ({
2262
- data: file.source,
2263
- mediaType: file.mediaType,
2264
- name: file.filename
2265
- })),
2266
- attachmentIds: files.flatMap((file) => file.persistedAttachmentId ? [file.persistedAttachmentId] : []),
2267
- attachments: files
2268
- }),
2269
- utilityRow: isEmptyState ? "inline" : "below"
2270
- }
2271
- )
2272
- ] })
2309
+ isEmptyState ? /* @__PURE__ */ jsx25("div", { className: "mx-auto mb-8 w-full max-w-3xl px-4 text-center sm:px-6", children: /* @__PURE__ */ jsx25("h2", { className: "text-3xl font-semibold tracking-tight sm:text-4xl", children: "Ready when you are." }) }) : /* @__PURE__ */ jsx25("div", { className: "pointer-events-auto absolute inset-x-0 top-0 flex -translate-y-full justify-center pb-3", children: /* @__PURE__ */ jsx25(LatestThreadScroller2.Button, { className: "!relative !inset-auto !translate-x-0 rtl:!translate-x-0" }) }),
2310
+ /* @__PURE__ */ jsx25("div", { className: "pointer-events-auto mx-auto w-full max-w-3xl px-4 sm:px-6", children: /* @__PURE__ */ jsx25(
2311
+ Composer,
2312
+ {
2313
+ queue: /* @__PURE__ */ jsx25(
2314
+ AgentChatQueue,
2315
+ {
2316
+ pending: controller.pendingMessages,
2317
+ queue: controller.messageQueue,
2318
+ onRemove: controller.removePendingMessage,
2319
+ onEdit: retractForEdit,
2320
+ onResume: controller.resumeQueue
2321
+ }
2322
+ ),
2323
+ restoredDraft,
2324
+ inputContract: runtime.inputContract || LOADING_INPUT_CONTRACT,
2325
+ contextWindow: runtime.contextWindow,
2326
+ disabled: runtime.isBootstrapping || runtime.isChangingAgent || isUploading || hasBlockingError,
2327
+ focusKey: controller.activeThreadId,
2328
+ forceExpanded: isEmptyState,
2329
+ isCancelling,
2330
+ isRunning,
2331
+ agentId: runtime.agentId,
2332
+ agents: runtime.agents,
2333
+ onAgentChange: (agentId) => {
2334
+ void runtime.changeAgent(agentId);
2335
+ },
2336
+ onStop: () => {
2337
+ void controller.cancelRun().catch(() => void 0);
2338
+ },
2339
+ modelId: runtime.modelId,
2340
+ models: runtime.models,
2341
+ onModelChange: runtime.changeModel,
2342
+ onSubmit: (text, files) => runtime.submit({
2343
+ text,
2344
+ files: files.filter((file) => file.source).map((file) => ({
2345
+ data: file.source,
2346
+ mediaType: file.mediaType,
2347
+ name: file.filename
2348
+ })),
2349
+ attachmentIds: files.flatMap((file) => file.persistedAttachmentId ? [file.persistedAttachmentId] : []),
2350
+ attachments: files
2351
+ }),
2352
+ utilityRow: isEmptyState ? "inline" : "below"
2353
+ }
2354
+ ) })
2273
2355
  ]
2274
2356
  }
2275
2357
  )
@@ -2287,7 +2369,7 @@ function ChatShell({
2287
2369
  var GUEST_CHAT_IDENTITY = { state: "guest" };
2288
2370
 
2289
2371
  // scaffold/src/app.tsx
2290
- import { jsx as jsx25 } from "react/jsx-runtime";
2372
+ import { jsx as jsx26 } from "react/jsx-runtime";
2291
2373
  var createBrowserAbortController = () => new AbortController();
2292
2374
  var browserVisibility = createBrowserVisibility();
2293
2375
  function RuntimeChat({
@@ -2299,19 +2381,20 @@ function RuntimeChat({
2299
2381
  ...props,
2300
2382
  mcpRedirectUri: props.mcpRedirectUri || (typeof window === "undefined" ? void 0 : window.location.href)
2301
2383
  });
2302
- return /* @__PURE__ */ jsx25(ChatShell, { components, identity, runtime, sidebar: props.sidebar });
2384
+ return /* @__PURE__ */ jsx26(ChatShell, { components, identity, runtime, sidebar: props.sidebar });
2303
2385
  }
2304
2386
  function App(props) {
2305
2387
  const identity = props.identity || GUEST_CHAT_IDENTITY;
2306
- return /* @__PURE__ */ jsx25(Agents24Provider, { client: props.client, createAbortController: createBrowserAbortController, visibility: browserVisibility, children: /* @__PURE__ */ jsx25(RuntimeChat, { ...props }, identity.state) });
2388
+ return /* @__PURE__ */ jsx26(Agents24Provider, { client: props.client, createAbortController: createBrowserAbortController, visibility: browserVisibility, children: /* @__PURE__ */ jsx26(RuntimeChat, { ...props }, identity.state) });
2307
2389
  }
2308
2390
 
2309
2391
  // scaffold/src/index.tsx
2310
- import { jsx as jsx26 } from "react/jsx-runtime";
2311
- function Agents24ChatScaffold({ appearanceController, theme, ...props }) {
2312
- return /* @__PURE__ */ jsx26(ThemeProvider, { controller: appearanceController, theme, children: /* @__PURE__ */ jsx26(TooltipProvider, { children: /* @__PURE__ */ jsx26(App, { ...props }) }) });
2392
+ import { jsx as jsx27 } from "react/jsx-runtime";
2393
+ function Agents24ChatScaffold({ appearanceController, theme, themeMode, ...props }) {
2394
+ return /* @__PURE__ */ jsx27(ThemeProvider, { controller: appearanceController, theme, mode: themeMode, children: /* @__PURE__ */ jsx27(TooltipProvider, { children: /* @__PURE__ */ jsx27(App, { ...props }) }) });
2313
2395
  }
2314
2396
  export {
2397
+ ChatComposer as Agents24ChatComposer,
2315
2398
  Agents24ChatScaffold,
2316
2399
  App as Agents24ChatScaffoldApp,
2317
2400
  ThemeProvider as Agents24ChatThemeProvider,