@agents24/chat-react 0.5.28 → 0.5.30

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 (37) hide show
  1. package/README.md +23 -1
  2. package/dist/chunk-INO5DSSJ.js +86 -0
  3. package/dist/chunk-INO5DSSJ.js.map +1 -0
  4. package/dist/registry/agents24-chat.json +249 -0
  5. package/dist/runtime/index.cjs +10 -14
  6. package/dist/runtime/index.cjs.map +1 -1
  7. package/dist/runtime/index.js +12 -85
  8. package/dist/runtime/index.js.map +1 -1
  9. package/dist/runtime/input-contract.d.ts +6 -3
  10. package/dist/scaffold/app.d.ts +2 -0
  11. package/dist/scaffold/components/chat/chat-composer.d.ts +2 -3
  12. package/dist/scaffold/components/chat/theme-menu.d.ts +1 -1
  13. package/dist/scaffold/components/theme-provider.d.ts +2 -1
  14. package/dist/scaffold/index.cjs +44 -106
  15. package/dist/scaffold/index.cjs.map +1 -1
  16. package/dist/scaffold/index.d.ts +2 -0
  17. package/dist/scaffold/index.js +41 -107
  18. package/dist/scaffold/index.js.map +1 -1
  19. package/dist/ui/agent-chat-composer.d.ts +7 -2
  20. package/dist/ui/composer-attachments.d.ts +1 -1
  21. package/dist/ui/index.cjs +688 -539
  22. package/dist/ui/index.cjs.map +1 -1
  23. package/dist/ui/index.d.ts +1 -0
  24. package/dist/ui/index.js +650 -540
  25. package/dist/ui/index.js.map +1 -1
  26. package/dist/ui/use-agent-chat-composer.d.ts +48 -0
  27. package/dist/ui/use-audio-recorder.d.ts +1 -1
  28. package/package.json +10 -6
  29. package/scaffold/inherit.css +4 -0
  30. package/scaffold/src/app.tsx +2 -0
  31. package/scaffold/src/chat.css +24 -0
  32. package/scaffold/src/components/chat/chat-composer.tsx +14 -105
  33. package/scaffold/src/components/chat/chat-shell.tsx +2 -1
  34. package/scaffold/src/components/chat/theme-menu.tsx +3 -1
  35. package/scaffold/src/components/theme-provider.tsx +10 -8
  36. package/scaffold/src/index.css +8 -8
  37. package/scaffold/src/index.tsx +5 -0
package/dist/ui/index.js CHANGED
@@ -2,10 +2,16 @@ import {
2
2
  getActiveStreamingTextPartId,
3
3
  useStreamingText
4
4
  } from "../chunk-5PWUQTLK.js";
5
+ import {
6
+ attachmentFileAccept,
7
+ inputMimeTypes,
8
+ normalizeAttachmentMimeType,
9
+ validateAgentChatSubmission
10
+ } from "../chunk-INO5DSSJ.js";
5
11
 
6
12
  // src/ui/agent-chat-composer.tsx
7
- import * as React3 from "react";
8
- import { ArrowUp, Loader2, Plus, Square } from "lucide-react";
13
+ import * as React6 from "react";
14
+ import { ArrowUp, Loader2, Mic, Plus, Square } from "lucide-react";
9
15
 
10
16
  // src/ui/adapters.ts
11
17
  import * as React from "react";
@@ -718,7 +724,11 @@ function ChatAttachmentRows({
718
724
  );
719
725
  }
720
726
 
727
+ // src/ui/use-agent-chat-composer.ts
728
+ import * as React4 from "react";
729
+
721
730
  // src/ui/composer-attachments.ts
731
+ import { normalizeAttachmentMimeType as normalizeAttachmentMimeType2 } from "@agents24/client";
722
732
  var createId = () => typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : `file-${Date.now()}-${Math.random().toString(36).slice(2)}`;
723
733
  function createAgentChatComposerFile(file) {
724
734
  return {
@@ -726,7 +736,7 @@ function createAgentChatComposerFile(file) {
726
736
  type: "file",
727
737
  url: URL.createObjectURL(file),
728
738
  filename: file.name || "attachment",
729
- mediaType: file.type || "application/octet-stream",
739
+ mediaType: normalizeAttachmentMimeType2(file.type, file.name),
730
740
  source: file
731
741
  };
732
742
  }
@@ -778,8 +788,300 @@ function useChatComposerFocus({ textareaRef, disabled, focusKey, restoredDraft,
778
788
  return focus;
779
789
  }
780
790
 
781
- // src/ui/agent-chat-composer.tsx
791
+ // src/ui/use-audio-recorder.ts
792
+ import * as React3 from "react";
793
+ var RECORDING_CANDIDATES = ["audio/webm;codecs=opus", "audio/webm", "audio/mp4"];
794
+ function recordingExtension(mimeType) {
795
+ return mimeType.startsWith("audio/mp4") ? "m4a" : "webm";
796
+ }
797
+ function useAudioRecorder(onRecorded, options = {}) {
798
+ const mountedRef = React3.useRef(true);
799
+ const startingRef = React3.useRef(false);
800
+ const recorderRef = React3.useRef(null);
801
+ const streamRef = React3.useRef(null);
802
+ const onRecordedRef = React3.useRef(onRecorded);
803
+ const onStreamChangeRef = React3.useRef(options.onStreamChange);
804
+ onRecordedRef.current = onRecorded;
805
+ onStreamChangeRef.current = options.onStreamChange;
806
+ const [isRecording, setIsRecording] = React3.useState(false);
807
+ const [error, setError] = React3.useState(null);
808
+ const stop = React3.useCallback(() => {
809
+ if (recorderRef.current && recorderRef.current.state !== "inactive") recorderRef.current.stop();
810
+ }, []);
811
+ const start = React3.useCallback(async () => {
812
+ if (startingRef.current || recorderRef.current) return;
813
+ startingRef.current = true;
814
+ setError(null);
815
+ if (!navigator.mediaDevices?.getUserMedia || typeof MediaRecorder === "undefined") {
816
+ setError("Audio recording is not supported in this browser.");
817
+ startingRef.current = false;
818
+ return;
819
+ }
820
+ try {
821
+ const mimeType = RECORDING_CANDIDATES.find((candidate) => MediaRecorder.isTypeSupported(candidate));
822
+ if (!mimeType) {
823
+ setError("Audio recording is not supported in this browser.");
824
+ return;
825
+ }
826
+ const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
827
+ if (!mountedRef.current) {
828
+ stream.getTracks().forEach((track) => track.stop());
829
+ return;
830
+ }
831
+ streamRef.current = stream;
832
+ onStreamChangeRef.current?.(stream);
833
+ const recorder = new MediaRecorder(stream, { mimeType });
834
+ const chunks = [];
835
+ recorder.ondataavailable = (event) => {
836
+ if (event.data.size) chunks.push(event.data);
837
+ };
838
+ recorder.onstop = () => {
839
+ const actualType = recorder.mimeType || "audio/webm";
840
+ if (mountedRef.current) onRecordedRef.current(new File([new Blob(chunks, { type: actualType })], `recording-${Date.now()}.${recordingExtension(actualType)}`, { type: actualType }));
841
+ stream.getTracks().forEach((track) => track.stop());
842
+ onStreamChangeRef.current?.(null);
843
+ recorderRef.current = null;
844
+ streamRef.current = null;
845
+ if (mountedRef.current) setIsRecording(false);
846
+ };
847
+ recorderRef.current = recorder;
848
+ streamRef.current = stream;
849
+ recorder.start();
850
+ setIsRecording(true);
851
+ } catch {
852
+ streamRef.current?.getTracks().forEach((track) => track.stop());
853
+ onStreamChangeRef.current?.(null);
854
+ streamRef.current = null;
855
+ recorderRef.current = null;
856
+ if (mountedRef.current) setError("Microphone access is unavailable.");
857
+ } finally {
858
+ startingRef.current = false;
859
+ }
860
+ }, []);
861
+ React3.useEffect(() => {
862
+ mountedRef.current = true;
863
+ return () => {
864
+ mountedRef.current = false;
865
+ if (recorderRef.current && recorderRef.current.state !== "inactive") recorderRef.current.stop();
866
+ streamRef.current?.getTracks().forEach((track) => track.stop());
867
+ onStreamChangeRef.current?.(null);
868
+ };
869
+ }, []);
870
+ return { error, isRecording, start, stop };
871
+ }
872
+
873
+ // src/ui/use-agent-chat-composer.ts
874
+ function useAgentChatComposer({
875
+ disabled = false,
876
+ isRunning = false,
877
+ isCancelling = false,
878
+ inputContract,
879
+ recordingEnabled,
880
+ focusKey,
881
+ restoredDraft,
882
+ textareaRef,
883
+ onSubmit
884
+ }) {
885
+ const [text, setDraftText] = React4.useState("");
886
+ const [files, setDraftFiles] = React4.useState([]);
887
+ const [isSubmitting, setIsSubmitting] = React4.useState(false);
888
+ const [attachmentError, setAttachmentError] = React4.useState(null);
889
+ const draftRef = React4.useRef({ text, files, revision: 0 });
890
+ const inFlightRef = React4.useRef([]);
891
+ const submittingRef = React4.useRef(false);
892
+ const mountedRef = React4.useRef(true);
893
+ const updateDraft = (nextText, nextFiles) => {
894
+ draftRef.current = { text: nextText, files: nextFiles, revision: draftRef.current.revision + 1 };
895
+ setDraftText(nextText);
896
+ setDraftFiles(nextFiles);
897
+ };
898
+ React4.useEffect(() => {
899
+ mountedRef.current = true;
900
+ return () => {
901
+ mountedRef.current = false;
902
+ revokeAgentChatComposerFiles(draftRef.current.files);
903
+ revokeAgentChatComposerFiles(inFlightRef.current);
904
+ };
905
+ }, []);
906
+ const focus = useChatComposerFocus({ textareaRef, disabled, focusKey, restoredDraft, onRestore: (draft) => {
907
+ revokeAgentChatComposerFiles(draftRef.current.files.filter((file) => !draft.files.includes(file)));
908
+ updateDraft(draft.text, draft.files);
909
+ } });
910
+ const accept = inputContract ? inputMimeTypes(inputContract) : null;
911
+ const addFiles = (selected) => {
912
+ const accepted = accept ? selected.filter((file) => accept.includes(normalizeAttachmentMimeType(file.type, file.name))) : selected;
913
+ setAttachmentError(accepted.length === selected.length ? null : "One or more selected file types are not supported by this Agent.");
914
+ updateDraft(draftRef.current.text, [...draftRef.current.files, ...accepted.map(createAgentChatComposerFile)]);
915
+ };
916
+ const recordedRef = React4.useRef(addFiles);
917
+ recordedRef.current = addFiles;
918
+ const recorder = useAudioRecorder((file) => {
919
+ if (mountedRef.current) recordedRef.current([file]);
920
+ });
921
+ const recordingAllowed = inputContract ? inputContract.modalities.audio.enabled && inputContract.modalities.audio.recording_enabled && recordingEnabled !== false : recordingEnabled === true;
922
+ const validationError = inputContract ? validateAgentChatSubmission(
923
+ inputContract,
924
+ text,
925
+ files.map((file) => ({ data: file.source || new Uint8Array(), mediaType: file.mediaType, name: file.filename, byteSize: file.source ? void 0 : file.byteSize }))
926
+ ) : null;
927
+ const canSubmit = !disabled && !isCancelling && !isSubmitting && !recorder.isRecording && !validationError && Boolean(text.trim() || files.length);
928
+ const submit = async () => {
929
+ if (!canSubmit || submittingRef.current) return;
930
+ const submitted = draftRef.current;
931
+ submittingRef.current = true;
932
+ inFlightRef.current = submitted.files;
933
+ setIsSubmitting(true);
934
+ updateDraft("", []);
935
+ const clearedRevision = draftRef.current.revision;
936
+ focus();
937
+ try {
938
+ await onSubmit({
939
+ text: submitted.text,
940
+ files: submitted.files,
941
+ attachmentIds: submitted.files.flatMap((file) => file.persistedAttachmentId ? [file.persistedAttachmentId] : [])
942
+ });
943
+ revokeAgentChatComposerFiles(submitted.files);
944
+ } catch {
945
+ if (mountedRef.current && draftRef.current.revision === clearedRevision) updateDraft(submitted.text, submitted.files);
946
+ else revokeAgentChatComposerFiles(submitted.files);
947
+ } finally {
948
+ inFlightRef.current = [];
949
+ submittingRef.current = false;
950
+ if (mountedRef.current) setIsSubmitting(false);
951
+ }
952
+ };
953
+ return {
954
+ text,
955
+ files,
956
+ isSubmitting,
957
+ canSubmit,
958
+ validationError,
959
+ attachmentError,
960
+ recordingAllowed,
961
+ recorder,
962
+ accept,
963
+ fileAccept: accept ? attachmentFileAccept(accept) : void 0,
964
+ allowAttachments: accept ? accept.length > 0 : true,
965
+ action: chatComposerAction({ hasDraft: Boolean(text.trim() || files.length), isRunning, isCancelling }),
966
+ setText: (value) => updateDraft(value, draftRef.current.files),
967
+ addFiles,
968
+ removeFile: (id) => {
969
+ revokeAgentChatComposerFiles(draftRef.current.files.filter((file) => file.id === id));
970
+ updateDraft(draftRef.current.text, draftRef.current.files.filter((file) => file.id !== id));
971
+ },
972
+ submit,
973
+ onKeyDown: (event) => {
974
+ if (event.key === "Enter" && !event.shiftKey && !event.nativeEvent.isComposing) {
975
+ event.preventDefault();
976
+ void submit();
977
+ }
978
+ }
979
+ };
980
+ }
981
+
982
+ // src/ui/agent-chat-context-status.tsx
983
+ import * as React5 from "react";
782
984
  import { jsx as jsx3, jsxs } from "react/jsx-runtime";
985
+ var COMPACT_TOKENS = new Intl.NumberFormat("en-US", { notation: "compact" });
986
+ var PERCENT = new Intl.NumberFormat("en-US", {
987
+ style: "percent",
988
+ maximumFractionDigits: 1
989
+ });
990
+ var EXACT_SOURCES = /* @__PURE__ */ new Set(["exact", "provider_usage"]);
991
+ function contextStatusMetrics(contextWindow) {
992
+ const maximum = Number(contextWindow?.max_tokens || 0);
993
+ const input = Math.max(0, Number(contextWindow?.input_tokens || 0));
994
+ const admissionInput = Math.max(0, Number(contextWindow?.admission_input_tokens ?? input));
995
+ const usageRatio = Math.max(0, Math.min(1, Number(
996
+ contextWindow?.usage_ratio ?? input / maximum
997
+ )));
998
+ const admissionRatio = Math.max(0, Math.min(1, Number(
999
+ contextWindow?.admission_ratio ?? admissionInput / maximum
1000
+ )));
1001
+ return { maximum, input, admissionInput, usageRatio, admissionRatio };
1002
+ }
1003
+ function AgentChatContextStatus({
1004
+ className,
1005
+ contextWindow
1006
+ }) {
1007
+ const tooltipId = React5.useId();
1008
+ const [open, setOpen] = React5.useState(false);
1009
+ const { maximum, input, admissionInput, usageRatio, admissionRatio } = contextStatusMetrics(contextWindow);
1010
+ const percentage = Math.round(usageRatio * 100);
1011
+ const exact = EXACT_SOURCES.has(contextWindow?.source || "unknown");
1012
+ if (!Number.isFinite(maximum) || maximum <= 0) return null;
1013
+ return /* @__PURE__ */ jsxs(
1014
+ "div",
1015
+ {
1016
+ className: cn("a24-context-status", className),
1017
+ onBlurCapture: (event) => {
1018
+ if (!event.currentTarget.contains(event.relatedTarget)) setOpen(false);
1019
+ },
1020
+ onFocusCapture: () => setOpen(true),
1021
+ onMouseEnter: () => setOpen(true),
1022
+ onMouseLeave: () => setOpen(false),
1023
+ children: [
1024
+ /* @__PURE__ */ jsxs(
1025
+ "button",
1026
+ {
1027
+ "aria-describedby": open ? tooltipId : void 0,
1028
+ "aria-label": `Context window ${percentage}% used`,
1029
+ className: "a24-context-status__trigger",
1030
+ onKeyDown: (event) => {
1031
+ if (event.key === "Escape") setOpen(false);
1032
+ },
1033
+ type: "button",
1034
+ children: [
1035
+ /* @__PURE__ */ jsxs("svg", { "aria-hidden": "true", className: "a24-context-status__ring", viewBox: "0 0 24 24", children: [
1036
+ /* @__PURE__ */ jsx3("circle", { className: "a24-context-status__track", cx: "12", cy: "12", r: "10" }),
1037
+ /* @__PURE__ */ jsx3(
1038
+ "circle",
1039
+ {
1040
+ className: "a24-context-status__value",
1041
+ cx: "12",
1042
+ cy: "12",
1043
+ pathLength: "100",
1044
+ r: "10",
1045
+ strokeDasharray: `${percentage} 100`
1046
+ }
1047
+ )
1048
+ ] }),
1049
+ /* @__PURE__ */ jsxs("span", { children: [
1050
+ percentage,
1051
+ "%"
1052
+ ] })
1053
+ ]
1054
+ }
1055
+ ),
1056
+ open ? /* @__PURE__ */ jsxs("div", { className: "a24-context-status__popover", id: tooltipId, role: "tooltip", children: [
1057
+ /* @__PURE__ */ jsx3("span", { className: "a24-context-status__sr-only", children: "Context window" }),
1058
+ /* @__PURE__ */ jsxs("strong", { children: [
1059
+ PERCENT.format(usageRatio),
1060
+ " context used"
1061
+ ] }),
1062
+ /* @__PURE__ */ jsxs("span", { children: [
1063
+ exact ? "Input" : "Estimated input",
1064
+ ": ",
1065
+ COMPACT_TOKENS.format(input),
1066
+ " / ",
1067
+ COMPACT_TOKENS.format(maximum),
1068
+ " tokens"
1069
+ ] }),
1070
+ admissionInput !== input ? /* @__PURE__ */ jsxs("span", { children: [
1071
+ "Safety bound: ",
1072
+ COMPACT_TOKENS.format(admissionInput),
1073
+ " tokens (",
1074
+ PERCENT.format(admissionRatio),
1075
+ ")"
1076
+ ] }) : null
1077
+ ] }) : null
1078
+ ]
1079
+ }
1080
+ );
1081
+ }
1082
+
1083
+ // src/ui/agent-chat-composer.tsx
1084
+ import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
783
1085
  function formatFileCount(files) {
784
1086
  if (files.length === 1) return files[0].filename;
785
1087
  return `${files.length} files`;
@@ -787,6 +1089,9 @@ function formatFileCount(files) {
787
1089
  var pillButtonClass = "inline-flex size-8 shrink-0 items-center justify-center gap-2 rounded-lg border-0 bg-muted text-sm text-muted-foreground shadow-none transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-0 disabled:pointer-events-none disabled:opacity-50";
788
1090
  var submitButtonClass = "inline-flex size-9 shrink-0 items-center justify-center gap-2 rounded-lg border-0 bg-primary text-sm text-primary-foreground shadow-none transition-[background-color,color,opacity,transform] duration-200 hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-0 disabled:pointer-events-none disabled:opacity-50";
789
1091
  function AgentChatComposer({
1092
+ inputContract,
1093
+ recordingEnabled,
1094
+ contextWindow,
790
1095
  accept,
791
1096
  allowAttachments = true,
792
1097
  attachmentLayout = "flow",
@@ -804,16 +1109,22 @@ function AgentChatComposer({
804
1109
  restoredDraft,
805
1110
  textareaRef
806
1111
  }) {
807
- const fileInputRef = React3.useRef(null);
808
- const filesRef = React3.useRef([]);
809
- const inFlightFilesRef = React3.useRef([]);
810
- const localTextareaRef = React3.useRef(null);
811
- const draftRevisionRef = React3.useRef(0);
812
- const [text, setText] = React3.useState("");
813
- const [files, setFiles] = React3.useState([]);
814
- const [isSubmitting, setIsSubmitting] = React3.useState(false);
1112
+ const fileInputRef = React6.useRef(null);
1113
+ const localTextareaRef = React6.useRef(null);
1114
+ const composer = useAgentChatComposer({
1115
+ inputContract,
1116
+ recordingEnabled,
1117
+ disabled,
1118
+ isRunning,
1119
+ isCancelling,
1120
+ focusKey,
1121
+ restoredDraft,
1122
+ textareaRef: localTextareaRef,
1123
+ onSubmit: (input) => onSubmit({ ...input, text: input.text.trim() })
1124
+ });
1125
+ const { text, files, setText, isSubmitting, canSubmit, action, submit } = composer;
815
1126
  const isExpanded = forceExpanded || text.includes("\n") || text.length > 62;
816
- const setTextareaRef = React3.useCallback(
1127
+ const setTextareaRef = React6.useCallback(
817
1128
  (node) => {
818
1129
  localTextareaRef.current = node;
819
1130
  if (typeof textareaRef === "function") {
@@ -824,27 +1135,10 @@ function AgentChatComposer({
824
1135
  },
825
1136
  [textareaRef]
826
1137
  );
827
- React3.useEffect(() => {
828
- filesRef.current = files;
1138
+ React6.useEffect(() => {
829
1139
  onAttachmentCountChange?.(files.length);
830
1140
  }, [files, onAttachmentCountChange]);
831
- React3.useEffect(() => () => {
832
- revokeAgentChatComposerFiles(filesRef.current);
833
- revokeAgentChatComposerFiles(inFlightFilesRef.current);
834
- }, []);
835
- const focusComposer = useChatComposerFocus({
836
- textareaRef: localTextareaRef,
837
- disabled,
838
- focusKey,
839
- restoredDraft,
840
- onRestore: (draft) => {
841
- draftRevisionRef.current += 1;
842
- revokeAgentChatComposerFiles(filesRef.current);
843
- setText(draft.text);
844
- setFiles(draft.files);
845
- }
846
- });
847
- React3.useLayoutEffect(() => {
1141
+ React6.useLayoutEffect(() => {
848
1142
  const textarea = localTextareaRef.current;
849
1143
  if (!textarea) return;
850
1144
  if (isExpanded) {
@@ -857,65 +1151,12 @@ function AgentChatComposer({
857
1151
  textarea.style.height = "";
858
1152
  textarea.style.overflowY = "hidden";
859
1153
  }, [isExpanded, text]);
860
- const canSubmit = !disabled && !isCancelling && !isSubmitting && (text.trim().length > 0 || files.length > 0);
861
- const action = chatComposerAction({ hasDraft: text.trim().length > 0 || files.length > 0, isRunning, isCancelling });
862
1154
  const handleFilesChange = (event) => {
863
- const selected = Array.from(event.target.files || []).map(createAgentChatComposerFile);
864
- if (selected.length > 0) {
865
- draftRevisionRef.current += 1;
866
- setFiles((current) => [...current, ...selected]);
867
- }
1155
+ composer.addFiles(Array.from(event.target.files || []));
868
1156
  event.currentTarget.value = "";
869
1157
  };
870
- const removeFile = (fileId) => {
871
- draftRevisionRef.current += 1;
872
- setFiles((current) => {
873
- const target = current.find((file) => file.id === fileId);
874
- if (target) revokeAgentChatComposerFiles([target]);
875
- return current.filter((file) => file.id !== fileId);
876
- });
877
- };
878
- const submit = async () => {
879
- if (!canSubmit) return;
880
- const submittedText = text.trim();
881
- const submittedFiles = files;
882
- const clearedRevision = draftRevisionRef.current + 1;
883
- draftRevisionRef.current = clearedRevision;
884
- inFlightFilesRef.current = submittedFiles;
885
- setIsSubmitting(true);
886
- setText("");
887
- setFiles([]);
888
- focusComposer();
889
- try {
890
- await onSubmit({
891
- text: submittedText,
892
- files: submittedFiles,
893
- attachmentIds: submittedFiles.flatMap((file) => file.persistedAttachmentId ? [file.persistedAttachmentId] : [])
894
- });
895
- revokeAgentChatComposerFiles(submittedFiles);
896
- inFlightFilesRef.current = [];
897
- } catch {
898
- if (draftRevisionRef.current === clearedRevision) {
899
- draftRevisionRef.current += 1;
900
- setText(submittedText);
901
- setFiles(submittedFiles);
902
- inFlightFilesRef.current = [];
903
- } else {
904
- revokeAgentChatComposerFiles(submittedFiles);
905
- inFlightFilesRef.current = [];
906
- }
907
- } finally {
908
- setIsSubmitting(false);
909
- }
910
- };
911
- const handleKeyDown = (event) => {
912
- if (event.key === "Enter" && !event.shiftKey && !event.nativeEvent.isComposing) {
913
- event.preventDefault();
914
- void submit();
915
- }
916
- };
917
- return /* @__PURE__ */ jsxs("div", { className: cn("a24-composer relative mx-auto w-full max-w-3xl overflow-visible", className), "data-agents24-chat-composer": "", children: [
918
- files.length > 0 ? /* @__PURE__ */ jsx3(
1158
+ return /* @__PURE__ */ jsxs2("div", { className: cn("a24-composer relative mx-auto w-full max-w-3xl overflow-visible", className), "data-agents24-chat-composer": "", children: [
1159
+ files.length > 0 ? /* @__PURE__ */ jsx4(
919
1160
  "div",
920
1161
  {
921
1162
  "data-testid": "agent-chat-composer-attachments",
@@ -924,16 +1165,17 @@ function AgentChatComposer({
924
1165
  "a24-composer__attachments min-w-0 py-1",
925
1166
  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"
926
1167
  ),
927
- children: /* @__PURE__ */ jsx3(
1168
+ children: /* @__PURE__ */ jsx4(
928
1169
  ChatAttachmentRows,
929
1170
  {
930
1171
  attachments: files,
931
- onRemove: (attachment) => attachment.id && removeFile(attachment.id)
1172
+ onRemove: (attachment) => attachment.id && composer.removeFile(attachment.id)
932
1173
  }
933
1174
  )
934
1175
  }
935
1176
  ) : null,
936
- /* @__PURE__ */ jsx3(
1177
+ composer.attachmentError || composer.recorder.error ? /* @__PURE__ */ jsx4("p", { role: "alert", children: composer.attachmentError || composer.recorder.error }) : null,
1178
+ /* @__PURE__ */ jsx4(
937
1179
  "div",
938
1180
  {
939
1181
  "data-expanded": isExpanded ? "" : void 0,
@@ -941,7 +1183,7 @@ function AgentChatComposer({
941
1183
  "a24-composer__surface relative w-full border border-input bg-background shadow-sm",
942
1184
  isExpanded ? "rounded-xl p-2 pb-1.5" : "rounded-xl px-2 py-[3px]"
943
1185
  ),
944
- children: /* @__PURE__ */ jsxs(
1186
+ children: /* @__PURE__ */ jsxs2(
945
1187
  "div",
946
1188
  {
947
1189
  className: "a24-composer__grid w-full",
@@ -953,12 +1195,12 @@ function AgentChatComposer({
953
1195
  gap: isExpanded ? "3px 10px" : "8px"
954
1196
  },
955
1197
  children: [
956
- /* @__PURE__ */ jsxs("div", { style: { gridArea: "plus" }, className: "a24-composer__attach-slot flex shrink-0 items-center justify-center", children: [
957
- /* @__PURE__ */ jsx3(
1198
+ /* @__PURE__ */ jsxs2("div", { style: { gridArea: "plus" }, className: "a24-composer__attach-slot flex shrink-0 items-center justify-center", children: [
1199
+ /* @__PURE__ */ jsx4(
958
1200
  "input",
959
1201
  {
960
1202
  ref: fileInputRef,
961
- accept,
1203
+ accept: composer.fileAccept ?? accept,
962
1204
  "aria-label": "Attach files",
963
1205
  className: "hidden",
964
1206
  multiple: true,
@@ -967,20 +1209,20 @@ function AgentChatComposer({
967
1209
  type: "file"
968
1210
  }
969
1211
  ),
970
- /* @__PURE__ */ jsx3(
1212
+ /* @__PURE__ */ jsx4(
971
1213
  "button",
972
1214
  {
973
1215
  "aria-label": "Attach files",
974
1216
  className: cn("a24-composer__attach", pillButtonClass),
975
- disabled: disabled || isSubmitting || !allowAttachments,
1217
+ disabled: disabled || isSubmitting || !allowAttachments || !composer.allowAttachments,
976
1218
  onClick: () => fileInputRef.current?.click(),
977
1219
  title: "Attach files",
978
1220
  type: "button",
979
- children: /* @__PURE__ */ jsx3(Plus, { className: "size-4" })
1221
+ children: /* @__PURE__ */ jsx4(Plus, { className: "size-4" })
980
1222
  }
981
1223
  )
982
1224
  ] }),
983
- /* @__PURE__ */ jsx3("div", { style: { gridArea: "textarea" }, className: "a24-composer__textarea-slot min-w-0 w-full", children: /* @__PURE__ */ jsx3(
1225
+ /* @__PURE__ */ jsx4("div", { style: { gridArea: "textarea" }, className: "a24-composer__textarea-slot min-w-0 w-full", children: /* @__PURE__ */ jsx4(
984
1226
  "textarea",
985
1227
  {
986
1228
  ref: setTextareaRef,
@@ -992,20 +1234,50 @@ function AgentChatComposer({
992
1234
  ),
993
1235
  disabled,
994
1236
  name: "message",
995
- onChange: (event) => {
996
- draftRevisionRef.current += 1;
997
- setText(event.target.value);
998
- },
999
- onKeyDown: handleKeyDown,
1237
+ onChange: (event) => setText(event.target.value),
1238
+ onKeyDown: composer.onKeyDown,
1000
1239
  placeholder,
1001
1240
  rows: isExpanded ? 2 : 1,
1002
1241
  value: text
1003
1242
  }
1004
1243
  ) }),
1005
- isExpanded ? /* @__PURE__ */ jsx3("div", { style: { gridArea: "toolbar" }, className: "a24-composer__toolbar flex min-w-0 items-center gap-1.5", children: inputToolbarContent }) : null,
1006
- !isExpanded ? /* @__PURE__ */ jsx3("div", { style: { gridArea: "composer" }, className: "flex min-w-0 items-center gap-1.5" }) : null,
1007
- !isExpanded ? /* @__PURE__ */ jsx3("div", { style: { gridArea: "mic" }, className: "flex shrink-0 items-center justify-end" }) : null,
1008
- /* @__PURE__ */ jsx3("div", { style: { gridArea: "submit" }, className: "a24-composer__submit-slot flex shrink-0 items-center justify-end gap-1", children: /* @__PURE__ */ jsx3(
1244
+ isExpanded ? /* @__PURE__ */ jsxs2("div", { style: { gridArea: "toolbar" }, className: "a24-composer__toolbar flex min-w-0 items-center gap-1.5", children: [
1245
+ inputToolbarContent,
1246
+ /* @__PURE__ */ jsx4(AgentChatContextStatus, { contextWindow }),
1247
+ composer.recordingAllowed ? /* @__PURE__ */ jsx4(
1248
+ "button",
1249
+ {
1250
+ type: "button",
1251
+ className: pillButtonClass,
1252
+ "aria-label": composer.recorder.isRecording ? "Stop recording" : "Record audio",
1253
+ disabled: disabled || isRunning || isSubmitting,
1254
+ onClick: () => {
1255
+ if (composer.recorder.isRecording) composer.recorder.stop();
1256
+ else void composer.recorder.start();
1257
+ },
1258
+ children: composer.recorder.isRecording ? /* @__PURE__ */ jsx4(Square, {}) : /* @__PURE__ */ jsx4(Mic, {})
1259
+ }
1260
+ ) : null
1261
+ ] }) : null,
1262
+ !isExpanded ? /* @__PURE__ */ jsx4("div", { style: { gridArea: "composer" }, className: "flex min-w-0 items-center gap-1.5" }) : null,
1263
+ !isExpanded ? /* @__PURE__ */ jsxs2("div", { style: { gridArea: "mic" }, className: "flex shrink-0 items-center justify-end", children: [
1264
+ /* @__PURE__ */ jsx4(AgentChatContextStatus, { contextWindow }),
1265
+ composer.recordingAllowed ? /* @__PURE__ */ jsx4(
1266
+ "button",
1267
+ {
1268
+ type: "button",
1269
+ className: pillButtonClass,
1270
+ "aria-label": composer.recorder.isRecording ? "Stop recording" : "Record audio",
1271
+ disabled: disabled || isRunning || isSubmitting,
1272
+ onClick: () => {
1273
+ if (composer.recorder.isRecording) composer.recorder.stop();
1274
+ else void composer.recorder.start();
1275
+ },
1276
+ children: composer.recorder.isRecording ? /* @__PURE__ */ jsx4(Square, {}) : /* @__PURE__ */ jsx4(Mic, {})
1277
+ }
1278
+ ) : null
1279
+ ] }) : null,
1280
+ /* @__PURE__ */ jsx4("div", { style: { gridArea: "submit" }, className: "a24-composer__submit-slot flex shrink-0 items-center justify-end gap-1", children: /* @__PURE__ */ jsx4(
1009
1281
  "button",
1010
1282
  {
1011
1283
  "aria-label": isCancelling ? "Cancelling response" : action === "stop" ? "Stop generating" : files.length > 0 ? `Send ${formatFileCount(files)}` : isRunning ? "Queue message" : "Send message",
@@ -1015,9 +1287,9 @@ function AgentChatComposer({
1015
1287
  if (action === "stop") onStop?.();
1016
1288
  else void submit();
1017
1289
  },
1018
- title: action === "stop" ? "Stop" : isRunning ? "Queue message" : "Send",
1290
+ title: composer.validationError || (action === "stop" ? "Stop" : isRunning ? "Queue message" : "Send"),
1019
1291
  type: "button",
1020
- children: isCancelling ? /* @__PURE__ */ jsx3(Loader2, { className: "size-3.5 animate-spin" }) : action === "stop" ? /* @__PURE__ */ jsx3(Square, { className: "size-3.5 fill-current" }) : /* @__PURE__ */ jsx3(ArrowUp, { className: "size-4", strokeWidth: 2.5 })
1292
+ children: isCancelling ? /* @__PURE__ */ jsx4(Loader2, { className: "size-3.5 animate-spin" }) : action === "stop" ? /* @__PURE__ */ jsx4(Square, { className: "size-3.5 fill-current" }) : /* @__PURE__ */ jsx4(ArrowUp, { className: "size-4", strokeWidth: 2.5 })
1021
1293
  }
1022
1294
  ) })
1023
1295
  ]
@@ -1025,17 +1297,17 @@ function AgentChatComposer({
1025
1297
  )
1026
1298
  }
1027
1299
  ),
1028
- !isExpanded && inputToolbarContent ? /* @__PURE__ */ jsx3("div", { className: "a24-composer__toolbar a24-composer__toolbar--external mt-2 flex items-center gap-2 px-1", children: inputToolbarContent }) : null
1300
+ !isExpanded && inputToolbarContent ? /* @__PURE__ */ jsx4("div", { className: "a24-composer__toolbar a24-composer__toolbar--external mt-2 flex items-center gap-2 px-1", children: inputToolbarContent }) : null
1029
1301
  ] });
1030
1302
  }
1031
1303
 
1032
1304
  // src/ui/agent-chat-queue.tsx
1033
- import * as React4 from "react";
1305
+ import * as React7 from "react";
1034
1306
  import { CornerDownLeft, Ellipsis, Trash2 } from "lucide-react";
1035
- import { Fragment, jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
1307
+ import { Fragment, jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
1036
1308
  function QueueRow({ message, onRemove, onEdit }) {
1037
- const [busy, setBusy] = React4.useState(false);
1038
- const [error, setError] = React4.useState(null);
1309
+ const [busy, setBusy] = React7.useState(false);
1310
+ const [error, setError] = React7.useState(null);
1039
1311
  const editable = message.revision > 0 && (message.status === "queued" || message.status === "blocked");
1040
1312
  const act = async (operation) => {
1041
1313
  setBusy(true);
@@ -1048,14 +1320,14 @@ function QueueRow({ message, onRemove, onEdit }) {
1048
1320
  setBusy(false);
1049
1321
  }
1050
1322
  };
1051
- return /* @__PURE__ */ jsxs2("div", { "data-message-id": message.id, className: "a24-queue__row min-w-0 border-b border-border/40 last:border-b-0", children: [
1052
- /* @__PURE__ */ jsxs2("div", { className: "a24-queue__row-content flex min-h-9 items-center gap-2 px-3 text-sm", children: [
1053
- /* @__PURE__ */ jsx4(CornerDownLeft, { "aria-hidden": true, className: "size-4 shrink-0 text-muted-foreground" }),
1054
- /* @__PURE__ */ jsx4("span", { className: "a24-queue__text min-w-0 flex-1 truncate text-foreground", title: message.text, children: message.text || `${message.attachment_ids.length} attachment${message.attachment_ids.length === 1 ? "" : "s"}` }),
1055
- message.status === "blocked" ? /* @__PURE__ */ jsx4("span", { className: "shrink-0 text-xs text-amber-700", children: "Needs attention" }) : null,
1056
- ["steer_pending", "delivering"].includes(message.status) ? /* @__PURE__ */ jsx4("span", { className: "shrink-0 text-xs text-muted-foreground", children: "Steering\u2026" }) : null,
1057
- editable ? /* @__PURE__ */ jsxs2(Fragment, { children: [
1058
- /* @__PURE__ */ jsx4(
1323
+ return /* @__PURE__ */ jsxs3("div", { "data-message-id": message.id, className: "a24-queue__row min-w-0 border-b border-border/40 last:border-b-0", children: [
1324
+ /* @__PURE__ */ jsxs3("div", { className: "a24-queue__row-content flex min-h-9 items-center gap-2 px-3 text-sm", children: [
1325
+ /* @__PURE__ */ jsx5(CornerDownLeft, { "aria-hidden": true, className: "size-4 shrink-0 text-muted-foreground" }),
1326
+ /* @__PURE__ */ jsx5("span", { className: "a24-queue__text min-w-0 flex-1 truncate text-foreground", title: message.text, children: message.text || `${message.attachment_ids.length} attachment${message.attachment_ids.length === 1 ? "" : "s"}` }),
1327
+ message.status === "blocked" ? /* @__PURE__ */ jsx5("span", { className: "shrink-0 text-xs text-amber-700", children: "Needs attention" }) : null,
1328
+ ["steer_pending", "delivering"].includes(message.status) ? /* @__PURE__ */ jsx5("span", { className: "shrink-0 text-xs text-muted-foreground", children: "Steering\u2026" }) : null,
1329
+ editable ? /* @__PURE__ */ jsxs3(Fragment, { children: [
1330
+ /* @__PURE__ */ jsx5(
1059
1331
  "button",
1060
1332
  {
1061
1333
  type: "button",
@@ -1063,12 +1335,12 @@ function QueueRow({ message, onRemove, onEdit }) {
1063
1335
  disabled: busy,
1064
1336
  onClick: () => void act(() => onRemove(message)),
1065
1337
  className: "a24-queue__icon flex size-8 shrink-0 items-center justify-center text-muted-foreground hover:text-foreground disabled:opacity-50",
1066
- children: /* @__PURE__ */ jsx4(Trash2, { "aria-hidden": true, className: "size-4" })
1338
+ children: /* @__PURE__ */ jsx5(Trash2, { "aria-hidden": true, className: "size-4" })
1067
1339
  }
1068
1340
  ),
1069
- /* @__PURE__ */ jsxs2("details", { className: "a24-queue__menu relative group/queue-menu", children: [
1070
- /* @__PURE__ */ jsx4("summary", { "aria-label": "Queued message options", className: "a24-queue__icon flex size-8 cursor-pointer list-none items-center justify-center text-muted-foreground hover:text-foreground [&::-webkit-details-marker]:hidden", children: /* @__PURE__ */ jsx4(Ellipsis, { "aria-hidden": true, className: "size-4" }) }),
1071
- /* @__PURE__ */ jsx4("div", { className: "a24-queue__menu-content mt-1 min-w-28 rounded-md border bg-background p-1", children: /* @__PURE__ */ jsx4(
1341
+ /* @__PURE__ */ jsxs3("details", { className: "a24-queue__menu relative group/queue-menu", children: [
1342
+ /* @__PURE__ */ jsx5("summary", { "aria-label": "Queued message options", className: "a24-queue__icon flex size-8 cursor-pointer list-none items-center justify-center text-muted-foreground hover:text-foreground [&::-webkit-details-marker]:hidden", children: /* @__PURE__ */ jsx5(Ellipsis, { "aria-hidden": true, className: "size-4" }) }),
1343
+ /* @__PURE__ */ jsx5("div", { className: "a24-queue__menu-content mt-1 min-w-28 rounded-md border bg-background p-1", children: /* @__PURE__ */ jsx5(
1072
1344
  "button",
1073
1345
  {
1074
1346
  type: "button",
@@ -1081,29 +1353,29 @@ function QueueRow({ message, onRemove, onEdit }) {
1081
1353
  ] })
1082
1354
  ] }) : null
1083
1355
  ] }),
1084
- error ? /* @__PURE__ */ jsx4("p", { role: "alert", className: "px-9 pb-2 text-xs text-destructive", children: error }) : null
1356
+ error ? /* @__PURE__ */ jsx5("p", { role: "alert", className: "px-9 pb-2 text-xs text-destructive", children: error }) : null
1085
1357
  ] });
1086
1358
  }
1087
1359
  function AgentChatQueue({ pending, queue, onRemove, onEdit, onResume, className }) {
1088
- const [resumeError, setResumeError] = React4.useState(null);
1360
+ const [resumeError, setResumeError] = React7.useState(null);
1089
1361
  if (!pending.length && !queue?.paused) return null;
1090
- return /* @__PURE__ */ jsxs2("section", { "aria-label": "Message queue", className: cn("a24-queue relative z-0 -mb-2 overflow-visible rounded-t-xl border border-b-0 border-border/50 bg-background pb-2", className), children: [
1091
- queue?.paused ? /* @__PURE__ */ jsxs2("div", { className: "flex min-h-9 items-center justify-between gap-2 px-3 text-xs text-muted-foreground", children: [
1092
- /* @__PURE__ */ jsx4("span", { children: "Queue paused" }),
1093
- /* @__PURE__ */ jsx4("button", { type: "button", onClick: () => {
1362
+ return /* @__PURE__ */ jsxs3("section", { "aria-label": "Message queue", className: cn("a24-queue relative z-0 -mb-2 overflow-visible rounded-t-xl border border-b-0 border-border/50 bg-background pb-2", className), children: [
1363
+ queue?.paused ? /* @__PURE__ */ jsxs3("div", { className: "flex min-h-9 items-center justify-between gap-2 px-3 text-xs text-muted-foreground", children: [
1364
+ /* @__PURE__ */ jsx5("span", { children: "Queue paused" }),
1365
+ /* @__PURE__ */ jsx5("button", { type: "button", onClick: () => {
1094
1366
  void onResume().then(() => setResumeError(null)).catch((cause) => setResumeError(cause instanceof Error ? cause.message : "Could not resume queue"));
1095
1367
  }, className: "font-medium text-foreground hover:underline", children: "Resume queue" })
1096
1368
  ] }) : null,
1097
- resumeError ? /* @__PURE__ */ jsx4("p", { role: "alert", className: "px-3 pb-2 text-xs text-destructive", children: resumeError }) : null,
1098
- /* @__PURE__ */ jsx4("div", { className: "max-h-40 overflow-y-auto", children: pending.map((message) => /* @__PURE__ */ jsx4(QueueRow, { message, onRemove, onEdit }, message.client_submission_id || message.id)) })
1369
+ resumeError ? /* @__PURE__ */ jsx5("p", { role: "alert", className: "px-3 pb-2 text-xs text-destructive", children: resumeError }) : null,
1370
+ /* @__PURE__ */ jsx5("div", { className: "max-h-40 overflow-y-auto", children: pending.map((message) => /* @__PURE__ */ jsx5(QueueRow, { message, onRemove, onEdit }, message.client_submission_id || message.id)) })
1099
1371
  ] });
1100
1372
  }
1101
1373
 
1102
1374
  // src/ui/agent-chat-actions.tsx
1103
- import * as React5 from "react";
1375
+ import * as React8 from "react";
1104
1376
  import { Check, Copy, RefreshCcw, ThumbsDown, ThumbsUp } from "lucide-react";
1105
1377
  import { Popover } from "radix-ui";
1106
- import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
1378
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
1107
1379
  var FEEDBACK_REASONS = [
1108
1380
  ["incorrect", "Incorrect"],
1109
1381
  ["irrelevant", "Irrelevant"],
@@ -1114,8 +1386,8 @@ var FEEDBACK_REASONS = [
1114
1386
  ];
1115
1387
  var enteredCompletionRuns = /* @__PURE__ */ new Set();
1116
1388
  function useLiveCompletionRuns(runId, isRunning) {
1117
- const liveRuns = React5.useRef(/* @__PURE__ */ new Set());
1118
- React5.useEffect(() => {
1389
+ const liveRuns = React8.useRef(/* @__PURE__ */ new Set());
1390
+ React8.useEffect(() => {
1119
1391
  if (!runId || !isRunning) return;
1120
1392
  liveRuns.current.add(runId);
1121
1393
  if (liveRuns.current.size > 256) liveRuns.current.delete(liveRuns.current.values().next().value);
@@ -1123,10 +1395,10 @@ function useLiveCompletionRuns(runId, isRunning) {
1123
1395
  return (candidate) => Boolean(candidate && liveRuns.current.has(candidate));
1124
1396
  }
1125
1397
  function useCompletionActionEntrance(isComplete, runId, wasLive = false) {
1126
- const sawIncomplete = React5.useRef(!isComplete);
1127
- const entered = React5.useRef(false);
1398
+ const sawIncomplete = React8.useRef(!isComplete);
1399
+ const entered = React8.useRef(false);
1128
1400
  const shouldEnter = isComplete && !entered.current && (runId ? wasLive && !enteredCompletionRuns.has(runId) : sawIncomplete.current);
1129
- React5.useEffect(() => {
1401
+ React8.useEffect(() => {
1130
1402
  if (!isComplete) sawIncomplete.current = true;
1131
1403
  else if (shouldEnter) {
1132
1404
  entered.current = true;
@@ -1154,17 +1426,17 @@ function AgentChatDefaultActions({
1154
1426
  handlers,
1155
1427
  message
1156
1428
  }) {
1157
- const [feedbackOpen, setFeedbackOpen] = React5.useState(false);
1158
- const [feedbackTrigger, setFeedbackTrigger] = React5.useState(null);
1159
- const [reason, setReason] = React5.useState(handlers?.feedback?.reason ?? null);
1160
- const [comment, setComment] = React5.useState(handlers?.feedback?.comment ?? "");
1161
- const [optimisticCopied, setOptimisticCopied] = React5.useState(false);
1162
- const copyResetTimer = React5.useRef(null);
1163
- React5.useEffect(() => {
1429
+ const [feedbackOpen, setFeedbackOpen] = React8.useState(false);
1430
+ const [feedbackTrigger, setFeedbackTrigger] = React8.useState(null);
1431
+ const [reason, setReason] = React8.useState(handlers?.feedback?.reason ?? null);
1432
+ const [comment, setComment] = React8.useState(handlers?.feedback?.comment ?? "");
1433
+ const [optimisticCopied, setOptimisticCopied] = React8.useState(false);
1434
+ const copyResetTimer = React8.useRef(null);
1435
+ React8.useEffect(() => {
1164
1436
  setReason(handlers?.feedback?.reason ?? null);
1165
1437
  setComment(handlers?.feedback?.comment ?? "");
1166
1438
  }, [handlers?.feedback?.comment, handlers?.feedback?.reason]);
1167
- React5.useEffect(() => () => {
1439
+ React8.useEffect(() => () => {
1168
1440
  if (copyResetTimer.current !== null) window.clearTimeout(copyResetTimer.current);
1169
1441
  }, []);
1170
1442
  const copied = handlers?.copied ?? optimisticCopied;
@@ -1181,32 +1453,32 @@ function AgentChatDefaultActions({
1181
1453
  if (!handlers || message.isFinal === false) return null;
1182
1454
  if (message.role === "user") {
1183
1455
  const sentAt = formatSentAt(message.createdAt);
1184
- return /* @__PURE__ */ jsxs3(ChatMessageActions, { className: "a24-user-message-actions", children: [
1185
- sentAt ? /* @__PURE__ */ jsx5("time", { dateTime: sentAt.dateTime, children: sentAt.label }) : null,
1186
- handlers.onCopy ? /* @__PURE__ */ jsx5(
1456
+ return /* @__PURE__ */ jsxs4(ChatMessageActions, { className: "a24-user-message-actions", children: [
1457
+ sentAt ? /* @__PURE__ */ jsx6("time", { dateTime: sentAt.dateTime, children: sentAt.label }) : null,
1458
+ handlers.onCopy ? /* @__PURE__ */ jsx6(
1187
1459
  ChatMessageAction,
1188
1460
  {
1189
1461
  label: "Copy",
1190
1462
  onClick: copyMessage,
1191
1463
  tooltip: copied ? "Copied!" : "Copy to clipboard",
1192
1464
  className: "a24-user-message-copy",
1193
- children: copied ? /* @__PURE__ */ jsx5(Check, { className: "a24-user-message-copy__check text-green-600" }) : /* @__PURE__ */ jsx5(Copy, { className: "a24-user-message-copy__icon" })
1465
+ children: copied ? /* @__PURE__ */ jsx6(Check, { className: "a24-user-message-copy__check text-green-600" }) : /* @__PURE__ */ jsx6(Copy, { className: "a24-user-message-copy__icon" })
1194
1466
  }
1195
1467
  ) : null
1196
1468
  ] });
1197
1469
  }
1198
1470
  const canWriteFeedback = Boolean(message.runId && handlers.onSetFeedback);
1199
- return /* @__PURE__ */ jsxs3(ChatMessageActions, { className: animateCompletion ? "a24-chat-completion-actions" : void 0, children: [
1200
- handlers.onRetry ? /* @__PURE__ */ jsx5(
1471
+ return /* @__PURE__ */ jsxs4(ChatMessageActions, { className: animateCompletion ? "a24-chat-completion-actions" : void 0, children: [
1472
+ handlers.onRetry ? /* @__PURE__ */ jsx6(
1201
1473
  ChatMessageAction,
1202
1474
  {
1203
1475
  label: "Retry",
1204
1476
  onClick: () => void handlers.onRetry?.(message),
1205
1477
  tooltip: "Regenerate response",
1206
- children: /* @__PURE__ */ jsx5(RefreshCcw, { className: "size-4" })
1478
+ children: /* @__PURE__ */ jsx6(RefreshCcw, { className: "size-4" })
1207
1479
  }
1208
1480
  ) : null,
1209
- canWriteFeedback ? /* @__PURE__ */ jsx5(
1481
+ canWriteFeedback ? /* @__PURE__ */ jsx6(
1210
1482
  ChatMessageAction,
1211
1483
  {
1212
1484
  label: "Like",
@@ -1214,11 +1486,11 @@ function AgentChatDefaultActions({
1214
1486
  rating: handlers.feedback?.rating === "like" ? null : "like"
1215
1487
  }),
1216
1488
  tooltip: "Like this response",
1217
- children: /* @__PURE__ */ jsx5(ThumbsUp, { className: "size-4", fill: handlers.feedback?.rating === "like" ? "currentColor" : "none" })
1489
+ children: /* @__PURE__ */ jsx6(ThumbsUp, { className: "size-4", fill: handlers.feedback?.rating === "like" ? "currentColor" : "none" })
1218
1490
  }
1219
1491
  ) : null,
1220
- canWriteFeedback ? /* @__PURE__ */ jsxs3(Popover.Root, { onOpenChange: setFeedbackOpen, open: feedbackOpen, children: [
1221
- /* @__PURE__ */ jsx5(Popover.Trigger, { asChild: true, children: /* @__PURE__ */ jsx5(
1492
+ canWriteFeedback ? /* @__PURE__ */ jsxs4(Popover.Root, { onOpenChange: setFeedbackOpen, open: feedbackOpen, children: [
1493
+ /* @__PURE__ */ jsx6(Popover.Trigger, { asChild: true, children: /* @__PURE__ */ jsx6(
1222
1494
  ChatMessageAction,
1223
1495
  {
1224
1496
  label: "Dislike",
@@ -1230,14 +1502,14 @@ function AgentChatDefaultActions({
1230
1502
  },
1231
1503
  ref: setFeedbackTrigger,
1232
1504
  tooltip: "Dislike this response",
1233
- children: /* @__PURE__ */ jsx5(ThumbsDown, { className: "size-4", fill: handlers.feedback?.rating === "dislike" ? "currentColor" : "none" })
1505
+ children: /* @__PURE__ */ jsx6(ThumbsDown, { className: "size-4", fill: handlers.feedback?.rating === "dislike" ? "currentColor" : "none" })
1234
1506
  }
1235
1507
  ) }),
1236
- /* @__PURE__ */ jsx5(
1508
+ /* @__PURE__ */ jsx6(
1237
1509
  Popover.Portal,
1238
1510
  {
1239
1511
  container: feedbackTrigger?.closest("[data-agents24-theme-root]") ?? void 0,
1240
- children: /* @__PURE__ */ jsxs3(
1512
+ children: /* @__PURE__ */ jsxs4(
1241
1513
  Popover.Content,
1242
1514
  {
1243
1515
  align: "end",
@@ -1247,8 +1519,8 @@ function AgentChatDefaultActions({
1247
1519
  side: "top",
1248
1520
  sideOffset: 8,
1249
1521
  children: [
1250
- /* @__PURE__ */ jsx5("p", { className: "text-sm font-medium", children: "What could be better?" }),
1251
- /* @__PURE__ */ jsx5("div", { className: "mt-3 flex flex-wrap gap-1.5", children: FEEDBACK_REASONS.map(([value, label]) => /* @__PURE__ */ jsx5(
1522
+ /* @__PURE__ */ jsx6("p", { className: "text-sm font-medium", children: "What could be better?" }),
1523
+ /* @__PURE__ */ jsx6("div", { className: "mt-3 flex flex-wrap gap-1.5", children: FEEDBACK_REASONS.map(([value, label]) => /* @__PURE__ */ jsx6(
1252
1524
  "button",
1253
1525
  {
1254
1526
  "aria-pressed": reason === value,
@@ -1259,7 +1531,7 @@ function AgentChatDefaultActions({
1259
1531
  },
1260
1532
  value
1261
1533
  )) }),
1262
- /* @__PURE__ */ jsx5(
1534
+ /* @__PURE__ */ jsx6(
1263
1535
  "textarea",
1264
1536
  {
1265
1537
  "aria-label": "Optional feedback comment",
@@ -1270,8 +1542,8 @@ function AgentChatDefaultActions({
1270
1542
  value: comment
1271
1543
  }
1272
1544
  ),
1273
- /* @__PURE__ */ jsxs3("div", { className: "mt-3 flex items-center justify-between gap-2", children: [
1274
- /* @__PURE__ */ jsx5(
1545
+ /* @__PURE__ */ jsxs4("div", { className: "mt-3 flex items-center justify-between gap-2", children: [
1546
+ /* @__PURE__ */ jsx6(
1275
1547
  "button",
1276
1548
  {
1277
1549
  className: "rounded-md px-3 py-2 text-sm font-medium hover:bg-muted",
@@ -1282,7 +1554,7 @@ function AgentChatDefaultActions({
1282
1554
  children: "Remove feedback"
1283
1555
  }
1284
1556
  ),
1285
- /* @__PURE__ */ jsx5(
1557
+ /* @__PURE__ */ jsx6(
1286
1558
  "button",
1287
1559
  {
1288
1560
  className: "rounded-md bg-primary px-3 py-2 text-sm font-medium text-primary-foreground hover:opacity-90",
@@ -1301,27 +1573,27 @@ function AgentChatDefaultActions({
1301
1573
  )
1302
1574
  ] }) : null,
1303
1575
  additionalActions,
1304
- handlers.onCopy ? /* @__PURE__ */ jsx5(
1576
+ handlers.onCopy ? /* @__PURE__ */ jsx6(
1305
1577
  ChatMessageAction,
1306
1578
  {
1307
1579
  label: "Copy",
1308
1580
  onClick: copyMessage,
1309
1581
  tooltip: copied ? "Copied!" : "Copy to clipboard",
1310
- children: copied ? /* @__PURE__ */ jsx5(Check, { className: "size-4 text-green-600" }) : /* @__PURE__ */ jsx5(Copy, { className: "size-4" })
1582
+ children: copied ? /* @__PURE__ */ jsx6(Check, { className: "size-4 text-green-600" }) : /* @__PURE__ */ jsx6(Copy, { className: "size-4" })
1311
1583
  }
1312
1584
  ) : null
1313
1585
  ] });
1314
1586
  }
1315
1587
 
1316
1588
  // src/ui/agent-chat-message.tsx
1317
- import * as React10 from "react";
1589
+ import * as React13 from "react";
1318
1590
  import { ChevronDown as ChevronDown3, ChevronUp } from "lucide-react";
1319
1591
 
1320
1592
  // src/ui/agent-response-timeline.tsx
1321
- import * as React9 from "react";
1593
+ import * as React12 from "react";
1322
1594
 
1323
1595
  // src/ui/ask-user-interaction.tsx
1324
- import * as React6 from "react";
1596
+ import * as React9 from "react";
1325
1597
  import {
1326
1598
  ChevronDown,
1327
1599
  ChevronLeft,
@@ -1383,7 +1655,7 @@ function askUserAnswersFromDrafts(questions, drafts) {
1383
1655
  }
1384
1656
 
1385
1657
  // src/ui/ask-user-interaction.tsx
1386
- import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
1658
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
1387
1659
  function resolvedAnswerText(question, answer) {
1388
1660
  if (!answer) return ["No response recorded"];
1389
1661
  const labels = answer.selectedOptionIds.flatMap((optionId) => {
@@ -1397,7 +1669,7 @@ function ResolvedQuestions({
1397
1669
  buttonRef,
1398
1670
  part
1399
1671
  }) {
1400
- const [open, setOpen] = React6.useState(false);
1672
+ const [open, setOpen] = React9.useState(false);
1401
1673
  const count = part.questions.length;
1402
1674
  const answers = new Map(
1403
1675
  (part.resolution?.answers || []).map((answer) => [
@@ -1405,8 +1677,8 @@ function ResolvedQuestions({
1405
1677
  answer
1406
1678
  ])
1407
1679
  );
1408
- return /* @__PURE__ */ jsxs4("div", { className: "a24-ask-user a24-ask-user--resolved w-full", children: [
1409
- /* @__PURE__ */ jsxs4(
1680
+ return /* @__PURE__ */ jsxs5("div", { className: "a24-ask-user a24-ask-user--resolved w-full", children: [
1681
+ /* @__PURE__ */ jsxs5(
1410
1682
  "button",
1411
1683
  {
1412
1684
  "aria-expanded": open,
@@ -1415,13 +1687,13 @@ function ResolvedQuestions({
1415
1687
  ref: buttonRef,
1416
1688
  type: "button",
1417
1689
  children: [
1418
- /* @__PURE__ */ jsxs4("span", { children: [
1690
+ /* @__PURE__ */ jsxs5("span", { children: [
1419
1691
  "Asked ",
1420
1692
  count,
1421
1693
  " question",
1422
1694
  count === 1 ? "" : "s"
1423
1695
  ] }),
1424
- open ? /* @__PURE__ */ jsx6(ChevronDown, { "aria-hidden": "true", className: "size-3.5" }) : /* @__PURE__ */ jsx6(
1696
+ open ? /* @__PURE__ */ jsx7(ChevronDown, { "aria-hidden": "true", className: "size-3.5" }) : /* @__PURE__ */ jsx7(
1425
1697
  ChevronRight,
1426
1698
  {
1427
1699
  "aria-hidden": "true",
@@ -1431,12 +1703,12 @@ function ResolvedQuestions({
1431
1703
  ]
1432
1704
  }
1433
1705
  ),
1434
- open ? /* @__PURE__ */ jsx6("div", { className: "mt-2 space-y-3 ps-4", children: part.questions.map((question) => /* @__PURE__ */ jsxs4("div", { className: "min-w-0", children: [
1435
- /* @__PURE__ */ jsx6("div", { className: "text-sm font-medium text-foreground", children: question.question }),
1436
- /* @__PURE__ */ jsx6("div", { className: "mt-1 space-y-1 text-sm text-muted-foreground", children: resolvedAnswerText(
1706
+ open ? /* @__PURE__ */ jsx7("div", { className: "mt-2 space-y-3 ps-4", children: part.questions.map((question) => /* @__PURE__ */ jsxs5("div", { className: "min-w-0", children: [
1707
+ /* @__PURE__ */ jsx7("div", { className: "text-sm font-medium text-foreground", children: question.question }),
1708
+ /* @__PURE__ */ jsx7("div", { className: "mt-1 space-y-1 text-sm text-muted-foreground", children: resolvedAnswerText(
1437
1709
  question,
1438
1710
  answers.get(question.id)
1439
- ).map((value, index) => /* @__PURE__ */ jsx6(
1711
+ ).map((value, index) => /* @__PURE__ */ jsx7(
1440
1712
  "div",
1441
1713
  {
1442
1714
  className: "whitespace-pre-wrap break-words",
@@ -1454,23 +1726,23 @@ function AskUserInteraction({
1454
1726
  part
1455
1727
  }) {
1456
1728
  const questions = part.questions;
1457
- const [step, setStep] = React6.useState(0);
1458
- const [drafts, setDrafts] = React6.useState(
1729
+ const [step, setStep] = React9.useState(0);
1730
+ const [drafts, setDrafts] = React9.useState(
1459
1731
  () => emptyAskUserDrafts(questions)
1460
1732
  );
1461
- const headingRef = React6.useRef(null);
1462
- const questionsRef = React6.useRef(questions);
1463
- const resolvedButtonRef = React6.useRef(null);
1464
- const wasResolvedRef = React6.useRef(part.status !== "pending");
1733
+ const headingRef = React9.useRef(null);
1734
+ const questionsRef = React9.useRef(questions);
1735
+ const resolvedButtonRef = React9.useRef(null);
1736
+ const wasResolvedRef = React9.useRef(part.status !== "pending");
1465
1737
  questionsRef.current = questions;
1466
- React6.useEffect(() => {
1738
+ React9.useEffect(() => {
1467
1739
  setStep(0);
1468
1740
  setDrafts(emptyAskUserDrafts(questionsRef.current));
1469
1741
  }, [part.interruptId]);
1470
- React6.useEffect(() => {
1742
+ React9.useEffect(() => {
1471
1743
  headingRef.current?.focus();
1472
1744
  }, [step]);
1473
- React6.useEffect(() => {
1745
+ React9.useEffect(() => {
1474
1746
  const isResolved = part.status !== "pending";
1475
1747
  if (isResolved && !wasResolvedRef.current) {
1476
1748
  resolvedButtonRef.current?.focus();
@@ -1478,11 +1750,11 @@ function AskUserInteraction({
1478
1750
  wasResolvedRef.current = isResolved;
1479
1751
  }, [part.status]);
1480
1752
  if (part.status !== "pending") {
1481
- return /* @__PURE__ */ jsx6(ResolvedQuestions, { buttonRef: resolvedButtonRef, part });
1753
+ return /* @__PURE__ */ jsx7(ResolvedQuestions, { buttonRef: resolvedButtonRef, part });
1482
1754
  }
1483
1755
  const question = questions[step];
1484
1756
  if (!question) {
1485
- return /* @__PURE__ */ jsx6("div", { className: "text-sm text-destructive", role: "alert", children: "This question request is unavailable." });
1757
+ return /* @__PURE__ */ jsx7("div", { className: "text-sm text-destructive", role: "alert", children: "This question request is unavailable." });
1486
1758
  }
1487
1759
  const draft = drafts[question.id] || {
1488
1760
  customText: "",
@@ -1516,9 +1788,9 @@ function AskUserInteraction({
1516
1788
  })
1517
1789
  );
1518
1790
  };
1519
- return /* @__PURE__ */ jsxs4("div", { className: "a24-ask-user space-y-3 rounded-xl border border-border/50 bg-background p-4 shadow-sm", children: [
1520
- /* @__PURE__ */ jsxs4("div", { className: "flex min-w-0 items-start justify-between gap-4", children: [
1521
- /* @__PURE__ */ jsx6(
1791
+ return /* @__PURE__ */ jsxs5("div", { className: "a24-ask-user space-y-3 rounded-xl border border-border/50 bg-background p-4 shadow-sm", children: [
1792
+ /* @__PURE__ */ jsxs5("div", { className: "flex min-w-0 items-start justify-between gap-4", children: [
1793
+ /* @__PURE__ */ jsx7(
1522
1794
  "h3",
1523
1795
  {
1524
1796
  className: "min-w-0 flex-1 text-base font-medium leading-6 text-foreground outline-none",
@@ -1527,8 +1799,8 @@ function AskUserInteraction({
1527
1799
  children: question.question
1528
1800
  }
1529
1801
  ),
1530
- /* @__PURE__ */ jsxs4("div", { className: "flex shrink-0 items-center gap-1 text-muted-foreground", children: [
1531
- /* @__PURE__ */ jsx6(
1802
+ /* @__PURE__ */ jsxs5("div", { className: "flex shrink-0 items-center gap-1 text-muted-foreground", children: [
1803
+ /* @__PURE__ */ jsx7(
1532
1804
  "button",
1533
1805
  {
1534
1806
  "aria-label": "Previous question",
@@ -1536,7 +1808,7 @@ function AskUserInteraction({
1536
1808
  disabled: isBusy || step === 0,
1537
1809
  onClick: () => setStep((current) => Math.max(0, current - 1)),
1538
1810
  type: "button",
1539
- children: /* @__PURE__ */ jsx6(
1811
+ children: /* @__PURE__ */ jsx7(
1540
1812
  ChevronLeft,
1541
1813
  {
1542
1814
  "aria-hidden": "true",
@@ -1545,7 +1817,7 @@ function AskUserInteraction({
1545
1817
  )
1546
1818
  }
1547
1819
  ),
1548
- /* @__PURE__ */ jsxs4(
1820
+ /* @__PURE__ */ jsxs5(
1549
1821
  "span",
1550
1822
  {
1551
1823
  "aria-label": `Question ${step + 1} of ${questions.length}`,
@@ -1557,7 +1829,7 @@ function AskUserInteraction({
1557
1829
  ]
1558
1830
  }
1559
1831
  ),
1560
- /* @__PURE__ */ jsx6(
1832
+ /* @__PURE__ */ jsx7(
1561
1833
  "button",
1562
1834
  {
1563
1835
  "aria-label": isLastQuestion ? "Submit answers" : "Next question",
@@ -1565,7 +1837,7 @@ function AskUserInteraction({
1565
1837
  disabled: isBusy || !onSubmit || !canMoveForward,
1566
1838
  onClick: () => completeStep(drafts),
1567
1839
  type: "button",
1568
- children: /* @__PURE__ */ jsx6(
1840
+ children: /* @__PURE__ */ jsx7(
1569
1841
  ChevronRight,
1570
1842
  {
1571
1843
  "aria-hidden": "true",
@@ -1576,10 +1848,10 @@ function AskUserInteraction({
1576
1848
  )
1577
1849
  ] })
1578
1850
  ] }),
1579
- /* @__PURE__ */ jsxs4("div", { className: "space-y-1", children: [
1851
+ /* @__PURE__ */ jsxs5("div", { className: "space-y-1", children: [
1580
1852
  question.options.map((option, index) => {
1581
1853
  const selected = draft.selectedOptionIds.includes(option.id);
1582
- return /* @__PURE__ */ jsxs4(
1854
+ return /* @__PURE__ */ jsxs5(
1583
1855
  "button",
1584
1856
  {
1585
1857
  "aria-pressed": selected,
@@ -1591,7 +1863,7 @@ function AskUserInteraction({
1591
1863
  onClick: () => selectOption(option.id),
1592
1864
  type: "button",
1593
1865
  children: [
1594
- /* @__PURE__ */ jsx6(
1866
+ /* @__PURE__ */ jsx7(
1595
1867
  "span",
1596
1868
  {
1597
1869
  className: cn(
@@ -1601,8 +1873,8 @@ function AskUserInteraction({
1601
1873
  children: index + 1
1602
1874
  }
1603
1875
  ),
1604
- /* @__PURE__ */ jsx6("span", { className: "min-w-0 flex-1 font-medium text-foreground", children: option.label }),
1605
- /* @__PURE__ */ jsx6(
1876
+ /* @__PURE__ */ jsx7("span", { className: "min-w-0 flex-1 font-medium text-foreground", children: option.label }),
1877
+ /* @__PURE__ */ jsx7(
1606
1878
  ChevronRight,
1607
1879
  {
1608
1880
  "aria-hidden": "true",
@@ -1614,9 +1886,9 @@ function AskUserInteraction({
1614
1886
  option.id
1615
1887
  );
1616
1888
  }),
1617
- /* @__PURE__ */ jsxs4("div", { className: "flex min-h-11 items-center gap-2 rounded-xl bg-muted px-2.5 py-1.5 focus-within:ring-1 focus-within:ring-ring", children: [
1618
- /* @__PURE__ */ jsx6("span", { className: "flex size-8 shrink-0 items-center justify-center rounded-full border border-border/70 bg-background/70 text-muted-foreground", children: /* @__PURE__ */ jsx6(Pencil, { "aria-hidden": "true", className: "size-4" }) }),
1619
- /* @__PURE__ */ jsx6(
1889
+ /* @__PURE__ */ jsxs5("div", { className: "flex min-h-11 items-center gap-2 rounded-xl bg-muted px-2.5 py-1.5 focus-within:ring-1 focus-within:ring-ring", children: [
1890
+ /* @__PURE__ */ jsx7("span", { className: "flex size-8 shrink-0 items-center justify-center rounded-full border border-border/70 bg-background/70 text-muted-foreground", children: /* @__PURE__ */ jsx7(Pencil, { "aria-hidden": "true", className: "size-4" }) }),
1891
+ /* @__PURE__ */ jsx7(
1620
1892
  "input",
1621
1893
  {
1622
1894
  "aria-label": "Other response",
@@ -1635,7 +1907,7 @@ function AskUserInteraction({
1635
1907
  value: draft.customText
1636
1908
  }
1637
1909
  ),
1638
- draft.customText.trim() ? /* @__PURE__ */ jsx6(
1910
+ draft.customText.trim() ? /* @__PURE__ */ jsx7(
1639
1911
  "button",
1640
1912
  {
1641
1913
  className: "h-8 shrink-0 rounded-full bg-foreground px-4 text-sm font-medium text-background transition-colors hover:opacity-85 disabled:opacity-50",
@@ -1647,12 +1919,12 @@ function AskUserInteraction({
1647
1919
  ) : null
1648
1920
  ] })
1649
1921
  ] }),
1650
- error ? /* @__PURE__ */ jsx6("div", { className: "text-sm text-destructive", role: "alert", children: error }) : null
1922
+ error ? /* @__PURE__ */ jsx7("div", { className: "text-sm text-destructive", role: "alert", children: error }) : null
1651
1923
  ] });
1652
1924
  }
1653
1925
 
1654
1926
  // src/ui/agent-chat-context-compression-row.tsx
1655
- import { jsx as jsx7 } from "react/jsx-runtime";
1927
+ import { jsx as jsx8 } from "react/jsx-runtime";
1656
1928
  function compressionPresentation(part) {
1657
1929
  if (part.status === "failed") {
1658
1930
  return { label: "Could not compact message history", status: "failed" };
@@ -1667,7 +1939,7 @@ function AgentChatContextCompressionRow({
1667
1939
  part
1668
1940
  }) {
1669
1941
  const presentation = compressionPresentation(part);
1670
- return /* @__PURE__ */ jsx7(
1942
+ return /* @__PURE__ */ jsx8(
1671
1943
  "div",
1672
1944
  {
1673
1945
  "aria-live": "polite",
@@ -1675,11 +1947,11 @@ function AgentChatContextCompressionRow({
1675
1947
  "data-agents24-context-compression": "",
1676
1948
  "data-status": part.status,
1677
1949
  role: "status",
1678
- children: /* @__PURE__ */ jsx7("div", { className: "a24-tool-trigger-row", children: /* @__PURE__ */ jsx7("div", { className: cn(
1950
+ children: /* @__PURE__ */ jsx8("div", { className: "a24-tool-trigger-row", children: /* @__PURE__ */ jsx8("div", { className: cn(
1679
1951
  "a24-tool-trigger a24-tool-trigger--root a24-tool-trigger--without-orb",
1680
1952
  presentation.status === "failed" && "a24-tool-trigger--error",
1681
1953
  presentation.status === "running" && "a24-tool-trigger--active"
1682
- ), children: /* @__PURE__ */ jsx7("span", { className: cn(
1954
+ ), children: /* @__PURE__ */ jsx8("span", { className: cn(
1683
1955
  "a24-tool-trigger__label",
1684
1956
  presentation.status === "running" && "a24-tool-shimmer"
1685
1957
  ), children: presentation.label }) }) })
@@ -1688,7 +1960,7 @@ function AgentChatContextCompressionRow({
1688
1960
  }
1689
1961
 
1690
1962
  // src/ui/message-response.tsx
1691
- import * as React7 from "react";
1963
+ import * as React10 from "react";
1692
1964
  import { defaultRehypePlugins, Streamdown } from "streamdown";
1693
1965
 
1694
1966
  // src/active-content.ts
@@ -1713,17 +1985,17 @@ function safeChatMarkdownHref(value) {
1713
1985
  }
1714
1986
 
1715
1987
  // src/ui/message-response.tsx
1716
- import { Fragment as Fragment2, jsx as jsx8 } from "react/jsx-runtime";
1988
+ import { Fragment as Fragment2, jsx as jsx9 } from "react/jsx-runtime";
1717
1989
  function SafeMarkdownLink({ children, href, ...props }) {
1718
1990
  const domProps = { ...props };
1719
1991
  delete domProps.node;
1720
1992
  const safeHref = safeChatMarkdownHref(href);
1721
- if (!safeHref) return /* @__PURE__ */ jsx8("span", { children });
1722
- if (safeHref.startsWith("#")) return /* @__PURE__ */ jsx8("a", { ...domProps, href: safeHref, children });
1723
- return /* @__PURE__ */ jsx8("a", { ...domProps, href: safeHref, rel: "noopener noreferrer", target: "_blank", children });
1993
+ if (!safeHref) return /* @__PURE__ */ jsx9("span", { children });
1994
+ if (safeHref.startsWith("#")) return /* @__PURE__ */ jsx9("a", { ...domProps, href: safeHref, children });
1995
+ return /* @__PURE__ */ jsx9("a", { ...domProps, href: safeHref, rel: "noopener noreferrer", target: "_blank", children });
1724
1996
  }
1725
1997
  function BlockedMarkdownImage({ alt }) {
1726
- return alt ? /* @__PURE__ */ jsx8("span", { "aria-label": `Image blocked: ${alt}`, role: "img", children: alt }) : null;
1998
+ return alt ? /* @__PURE__ */ jsx9("span", { "aria-label": `Image blocked: ${alt}`, role: "img", children: alt }) : null;
1727
1999
  }
1728
2000
  var BLOCK_MARKDOWN_TAGS = /* @__PURE__ */ new Set([
1729
2001
  "blockquote",
@@ -1744,7 +2016,7 @@ function isMultilineCodeNode(node) {
1744
2016
  return node?.tagName === "code" && node.position?.start?.line !== void 0 && node.position?.end?.line !== void 0 && node.position.start.line !== node.position.end.line;
1745
2017
  }
1746
2018
  function hasBlockMarkdownChild(child) {
1747
- if (!React7.isValidElement(child)) {
2019
+ if (!React10.isValidElement(child)) {
1748
2020
  return false;
1749
2021
  }
1750
2022
  if (typeof child.type === "string" && BLOCK_MARKDOWN_TAGS.has(child.type)) {
@@ -1754,7 +2026,7 @@ function hasBlockMarkdownChild(child) {
1754
2026
  if (BLOCK_MARKDOWN_TAGS.has(props.node?.tagName || "") || isMultilineCodeNode(props.node)) {
1755
2027
  return true;
1756
2028
  }
1757
- return React7.Children.toArray(props.children).some(hasBlockMarkdownChild);
2029
+ return React10.Children.toArray(props.children).some(hasBlockMarkdownChild);
1758
2030
  }
1759
2031
  function MessageMarkdownParagraph({
1760
2032
  children,
@@ -1762,17 +2034,17 @@ function MessageMarkdownParagraph({
1762
2034
  }) {
1763
2035
  const domProps = { ...props };
1764
2036
  delete domProps.node;
1765
- const childArray = React7.Children.toArray(children).filter(
2037
+ const childArray = React10.Children.toArray(children).filter(
1766
2038
  (child) => child !== null && child !== ""
1767
2039
  );
1768
- const isImageOnly = childArray.length === 1 && React7.isValidElement(childArray[0]) && childArray[0].props.node?.tagName === "img";
2040
+ const isImageOnly = childArray.length === 1 && React10.isValidElement(childArray[0]) && childArray[0].props.node?.tagName === "img";
1769
2041
  if (isImageOnly) {
1770
- return /* @__PURE__ */ jsx8(Fragment2, { children });
2042
+ return /* @__PURE__ */ jsx9(Fragment2, { children });
1771
2043
  }
1772
2044
  if (childArray.some(hasBlockMarkdownChild)) {
1773
- return /* @__PURE__ */ jsx8("div", { ...domProps, children });
2045
+ return /* @__PURE__ */ jsx9("div", { ...domProps, children });
1774
2046
  }
1775
- return /* @__PURE__ */ jsx8("p", { ...domProps, children });
2047
+ return /* @__PURE__ */ jsx9("p", { ...domProps, children });
1776
2048
  }
1777
2049
  var MESSAGE_RESPONSE_COMPONENTS = {
1778
2050
  a: SafeMarkdownLink,
@@ -1792,7 +2064,7 @@ var MESSAGE_RESPONSE_REHYPE_PLUGINS = [
1792
2064
  ]
1793
2065
  ] : []
1794
2066
  ];
1795
- var MessageResponse = React7.memo(
2067
+ var MessageResponse = React10.memo(
1796
2068
  ({
1797
2069
  children,
1798
2070
  className,
@@ -1810,11 +2082,11 @@ var MessageResponse = React7.memo(
1810
2082
  maxCatchupChars: 32,
1811
2083
  text
1812
2084
  });
1813
- const streamdownComponents = React7.useMemo(
2085
+ const streamdownComponents = React10.useMemo(
1814
2086
  () => ({ ...MESSAGE_RESPONSE_COMPONENTS, ...components }),
1815
2087
  [components]
1816
2088
  );
1817
- return /* @__PURE__ */ jsx8(
2089
+ return /* @__PURE__ */ jsx9(
1818
2090
  Streamdown,
1819
2091
  {
1820
2092
  className: cn("a24-message-response", className),
@@ -1866,10 +2138,10 @@ function resolvedHitlLabel(part) {
1866
2138
  }
1867
2139
 
1868
2140
  // src/ui/tool-sessions.tsx
1869
- import * as React8 from "react";
2141
+ import * as React11 from "react";
1870
2142
  import { ChevronDown as ChevronDown2, ChevronRight as ChevronRight2 } from "lucide-react";
1871
2143
  import { ThinkingOrb } from "thinking-orbs";
1872
- import { Fragment as Fragment3, jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
2144
+ import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
1873
2145
  function toolSlotContext(isActive2, label, part, slots) {
1874
2146
  return {
1875
2147
  isActive: isActive2,
@@ -1887,7 +2159,7 @@ function ToolResourceAction({ context }) {
1887
2159
  const target = context.resourceTargets[0];
1888
2160
  if (!target) return null;
1889
2161
  const name = resourceTargetName(target);
1890
- return /* @__PURE__ */ jsx9(
2162
+ return /* @__PURE__ */ jsx10(
1891
2163
  "button",
1892
2164
  {
1893
2165
  "aria-label": `Open ${target.kind} ${target.path}`,
@@ -1901,8 +2173,8 @@ function ToolResourceAction({ context }) {
1901
2173
  );
1902
2174
  }
1903
2175
  function toolAccessory(context, slots) {
1904
- return /* @__PURE__ */ jsxs5(Fragment3, { children: [
1905
- slots?.onResourceAction ? /* @__PURE__ */ jsx9(ToolResourceAction, { context }) : null,
2176
+ return /* @__PURE__ */ jsxs6(Fragment3, { children: [
2177
+ slots?.onResourceAction ? /* @__PURE__ */ jsx10(ToolResourceAction, { context }) : null,
1906
2178
  slots?.renderToolAccessory?.(context)
1907
2179
  ] });
1908
2180
  }
@@ -1979,12 +2251,12 @@ function ToolActivityReel({
1979
2251
  }) {
1980
2252
  const initial = initialLabel && initialLabel !== label ? initialLabel : label;
1981
2253
  const parsedInitialShownAt = initialShownAt ? Date.parse(initialShownAt) : Number.NaN;
1982
- const [displayed, setDisplayed] = React8.useState(initial);
1983
- const [previous, setPrevious] = React8.useState(null);
1984
- const displayedRef = React8.useRef(initial);
1985
- const latestRef = React8.useRef(label);
1986
- const shownAtRef = React8.useRef(Number.isFinite(parsedInitialShownAt) ? parsedInitialShownAt : Date.now());
1987
- React8.useEffect(() => {
2254
+ const [displayed, setDisplayed] = React11.useState(initial);
2255
+ const [previous, setPrevious] = React11.useState(null);
2256
+ const displayedRef = React11.useRef(initial);
2257
+ const latestRef = React11.useRef(label);
2258
+ const shownAtRef = React11.useRef(Number.isFinite(parsedInitialShownAt) ? parsedInitialShownAt : Date.now());
2259
+ React11.useEffect(() => {
1988
2260
  latestRef.current = label;
1989
2261
  if (displayedRef.current === label) return;
1990
2262
  const showLatest = () => {
@@ -2004,39 +2276,39 @@ function ToolActivityReel({
2004
2276
  const timer = window.setTimeout(showLatest, remaining);
2005
2277
  return () => window.clearTimeout(timer);
2006
2278
  }, [label]);
2007
- React8.useEffect(() => {
2279
+ React11.useEffect(() => {
2008
2280
  if (!previous) return;
2009
2281
  const timer = window.setTimeout(() => setPrevious(null), 200);
2010
2282
  return () => window.clearTimeout(timer);
2011
2283
  }, [previous]);
2012
- return /* @__PURE__ */ jsxs5("span", { className: "a24-tool-reel", "aria-live": announce ? "polite" : "off", children: [
2013
- previous ? /* @__PURE__ */ jsx9("span", { "aria-hidden": "true", className: "a24-tool-reel__previous", children: previous }) : null,
2014
- /* @__PURE__ */ jsx9("span", { className: previous ? "a24-tool-reel__current" : void 0, children: displayed })
2284
+ return /* @__PURE__ */ jsxs6("span", { className: "a24-tool-reel", "aria-live": announce ? "polite" : "off", children: [
2285
+ previous ? /* @__PURE__ */ jsx10("span", { "aria-hidden": "true", className: "a24-tool-reel__previous", children: previous }) : null,
2286
+ /* @__PURE__ */ jsx10("span", { className: previous ? "a24-tool-reel__current" : void 0, children: displayed })
2015
2287
  ] });
2016
2288
  }
2017
2289
  function ToolActivityOrb({ label }) {
2018
- return /* @__PURE__ */ jsx9("span", { className: "a24-tool-orb", children: /* @__PURE__ */ jsx9(ThinkingOrb, { "aria-label": label || "Working", size: 20, speed: 0.8, state: "searching" }) });
2290
+ return /* @__PURE__ */ jsx10("span", { className: "a24-tool-orb", children: /* @__PURE__ */ jsx10(ThinkingOrb, { "aria-label": label || "Working", size: 20, speed: 0.8, state: "searching" }) });
2019
2291
  }
2020
2292
  function AgentChatActivityRow({
2021
2293
  label,
2022
2294
  modelTurnId,
2023
2295
  onSelect
2024
2296
  }) {
2025
- const content = /* @__PURE__ */ jsxs5(Fragment3, { children: [
2026
- /* @__PURE__ */ jsx9(ToolActivityOrb, { label }),
2027
- /* @__PURE__ */ jsx9("span", { className: "a24-tool-trigger__label a24-tool-shimmer", children: /* @__PURE__ */ jsx9(ToolActivityReel, { label }) })
2297
+ const content = /* @__PURE__ */ jsxs6(Fragment3, { children: [
2298
+ /* @__PURE__ */ jsx10(ToolActivityOrb, { label }),
2299
+ /* @__PURE__ */ jsx10("span", { className: "a24-tool-trigger__label a24-tool-shimmer", children: /* @__PURE__ */ jsx10(ToolActivityReel, { label }) })
2028
2300
  ] });
2029
- return /* @__PURE__ */ jsx9("div", { className: "a24-tool-trigger-row a24-tool-trigger-row--root", "data-agents24-model-turn": modelTurnId, role: "status", children: onSelect ? /* @__PURE__ */ jsx9("button", { className: "a24-tool-trigger a24-tool-trigger--root a24-tool-trigger--active", onClick: onSelect, type: "button", children: content }) : /* @__PURE__ */ jsx9("div", { className: "a24-tool-trigger a24-tool-trigger--root a24-tool-trigger--active", children: content }) });
2301
+ return /* @__PURE__ */ jsx10("div", { className: "a24-tool-trigger-row a24-tool-trigger-row--root", "data-agents24-model-turn": modelTurnId, role: "status", children: onSelect ? /* @__PURE__ */ jsx10("button", { className: "a24-tool-trigger a24-tool-trigger--root a24-tool-trigger--active", onClick: onSelect, type: "button", children: content }) : /* @__PURE__ */ jsx10("div", { className: "a24-tool-trigger a24-tool-trigger--root a24-tool-trigger--active", children: content }) });
2030
2302
  }
2031
2303
  function AgentChatModelTurnRow({ part }) {
2032
- return /* @__PURE__ */ jsx9(AgentChatActivityRow, { label: "Planning next moves", modelTurnId: part.modelTurnId });
2304
+ return /* @__PURE__ */ jsx10(AgentChatActivityRow, { label: "Planning next moves", modelTurnId: part.modelTurnId });
2033
2305
  }
2034
2306
  function AgentChatNodeActivityRow({
2035
2307
  onSelect,
2036
2308
  part
2037
2309
  }) {
2038
- const [open, setOpen] = React8.useState(false);
2039
- const contentId = React8.useId();
2310
+ const [open, setOpen] = React11.useState(false);
2311
+ const contentId = React11.useId();
2040
2312
  const running = part.status === "running";
2041
2313
  const failed = part.status === "failed";
2042
2314
  const failureMessage = part.failure?.message || "The node could not complete the request.";
@@ -2047,18 +2319,18 @@ function AgentChatNodeActivityRow({
2047
2319
  running && "a24-tool-trigger--active",
2048
2320
  failed && "a24-tool-trigger--error"
2049
2321
  );
2050
- const label = running ? /* @__PURE__ */ jsx9("span", { className: "a24-tool-shimmer", children: /* @__PURE__ */ jsx9(ToolActivityReel, { label: part.label }) }) : part.label;
2322
+ const label = running ? /* @__PURE__ */ jsx10("span", { className: "a24-tool-shimmer", children: /* @__PURE__ */ jsx10(ToolActivityReel, { label: part.label }) }) : part.label;
2051
2323
  if (!failed) {
2052
- return /* @__PURE__ */ jsx9("div", { className: "a24-tool-trigger-row", role: "status", children: onSelect ? /* @__PURE__ */ jsxs5("button", { className: triggerClassName, onClick: () => onSelect(part), type: "button", children: [
2053
- running ? /* @__PURE__ */ jsx9(ToolActivityOrb, { label: part.label }) : null,
2054
- /* @__PURE__ */ jsx9("span", { className: "a24-tool-trigger__label", children: label })
2055
- ] }) : /* @__PURE__ */ jsxs5("div", { className: triggerClassName, children: [
2056
- running ? /* @__PURE__ */ jsx9(ToolActivityOrb, { label: part.label }) : null,
2057
- /* @__PURE__ */ jsx9("span", { className: "a24-tool-trigger__label", children: label })
2324
+ return /* @__PURE__ */ jsx10("div", { className: "a24-tool-trigger-row", role: "status", children: onSelect ? /* @__PURE__ */ jsxs6("button", { className: triggerClassName, onClick: () => onSelect(part), type: "button", children: [
2325
+ running ? /* @__PURE__ */ jsx10(ToolActivityOrb, { label: part.label }) : null,
2326
+ /* @__PURE__ */ jsx10("span", { className: "a24-tool-trigger__label", children: label })
2327
+ ] }) : /* @__PURE__ */ jsxs6("div", { className: triggerClassName, children: [
2328
+ running ? /* @__PURE__ */ jsx10(ToolActivityOrb, { label: part.label }) : null,
2329
+ /* @__PURE__ */ jsx10("span", { className: "a24-tool-trigger__label", children: label })
2058
2330
  ] }) });
2059
2331
  }
2060
- return /* @__PURE__ */ jsxs5("div", { className: "a24-tool-disclosure", children: [
2061
- /* @__PURE__ */ jsx9("div", { className: "a24-tool-trigger-row", children: /* @__PURE__ */ jsxs5(
2332
+ return /* @__PURE__ */ jsxs6("div", { className: "a24-tool-disclosure", children: [
2333
+ /* @__PURE__ */ jsx10("div", { className: "a24-tool-trigger-row", children: /* @__PURE__ */ jsxs6(
2062
2334
  "button",
2063
2335
  {
2064
2336
  "aria-controls": contentId,
@@ -2070,12 +2342,12 @@ function AgentChatNodeActivityRow({
2070
2342
  },
2071
2343
  type: "button",
2072
2344
  children: [
2073
- /* @__PURE__ */ jsx9("span", { className: "a24-tool-trigger__label", children: label }),
2074
- open ? /* @__PURE__ */ jsx9(ChevronDown2, { "aria-hidden": "true", className: "a24-tool-trigger__chevron" }) : /* @__PURE__ */ jsx9(ChevronRight2, { "aria-hidden": "true", className: "a24-tool-trigger__chevron" })
2345
+ /* @__PURE__ */ jsx10("span", { className: "a24-tool-trigger__label", children: label }),
2346
+ open ? /* @__PURE__ */ jsx10(ChevronDown2, { "aria-hidden": "true", className: "a24-tool-trigger__chevron" }) : /* @__PURE__ */ jsx10(ChevronRight2, { "aria-hidden": "true", className: "a24-tool-trigger__chevron" })
2075
2347
  ]
2076
2348
  }
2077
2349
  ) }),
2078
- open ? /* @__PURE__ */ jsx9("div", { className: "a24-tool-detail a24-tool-detail--error", id: contentId, children: failureMessage }) : null
2350
+ open ? /* @__PURE__ */ jsx10("div", { className: "a24-tool-detail a24-tool-detail--error", id: contentId, children: failureMessage }) : null
2079
2351
  ] });
2080
2352
  }
2081
2353
  function toolRowLabel(part, separateResource = false) {
@@ -2101,25 +2373,25 @@ function ToolDisclosure({
2101
2373
  statusLabel,
2102
2374
  accessory
2103
2375
  }) {
2104
- const [open, setOpen] = React8.useState(false);
2105
- const contentId = React8.useId();
2106
- const expandable = React8.Children.toArray(children).length > 0;
2376
+ const [open, setOpen] = React11.useState(false);
2377
+ const contentId = React11.useId();
2378
+ const expandable = React11.Children.toArray(children).length > 0;
2107
2379
  if (!expandable) {
2108
2380
  const triggerClassName = cn("a24-tool-trigger", root && "a24-tool-trigger--root", root && !showOrb && "a24-tool-trigger--without-orb", error && "a24-tool-trigger--error", loading && "a24-tool-trigger--active");
2109
- return /* @__PURE__ */ jsxs5("div", { className: cn("a24-tool-trigger-row", root && "a24-tool-trigger-row--root"), children: [
2110
- onSelect && part ? /* @__PURE__ */ jsxs5("button", { className: triggerClassName, onClick: () => onSelect(part), type: "button", children: [
2111
- showOrb ? /* @__PURE__ */ jsx9(ToolActivityOrb, { label: statusLabel }) : null,
2112
- /* @__PURE__ */ jsx9("span", { className: "a24-tool-trigger__label", children: label })
2113
- ] }) : /* @__PURE__ */ jsxs5("div", { className: triggerClassName, children: [
2114
- showOrb ? /* @__PURE__ */ jsx9(ToolActivityOrb, { label: statusLabel }) : null,
2115
- /* @__PURE__ */ jsx9("span", { className: "a24-tool-trigger__label", children: label })
2381
+ return /* @__PURE__ */ jsxs6("div", { className: cn("a24-tool-trigger-row", root && "a24-tool-trigger-row--root"), children: [
2382
+ onSelect && part ? /* @__PURE__ */ jsxs6("button", { className: triggerClassName, onClick: () => onSelect(part), type: "button", children: [
2383
+ showOrb ? /* @__PURE__ */ jsx10(ToolActivityOrb, { label: statusLabel }) : null,
2384
+ /* @__PURE__ */ jsx10("span", { className: "a24-tool-trigger__label", children: label })
2385
+ ] }) : /* @__PURE__ */ jsxs6("div", { className: triggerClassName, children: [
2386
+ showOrb ? /* @__PURE__ */ jsx10(ToolActivityOrb, { label: statusLabel }) : null,
2387
+ /* @__PURE__ */ jsx10("span", { className: "a24-tool-trigger__label", children: label })
2116
2388
  ] }),
2117
2389
  accessory
2118
2390
  ] });
2119
2391
  }
2120
- return /* @__PURE__ */ jsxs5("div", { className: "a24-tool-disclosure", children: [
2121
- /* @__PURE__ */ jsxs5("div", { className: cn("a24-tool-trigger-row", root && "a24-tool-trigger-row--root"), children: [
2122
- /* @__PURE__ */ jsxs5(
2392
+ return /* @__PURE__ */ jsxs6("div", { className: "a24-tool-disclosure", children: [
2393
+ /* @__PURE__ */ jsxs6("div", { className: cn("a24-tool-trigger-row", root && "a24-tool-trigger-row--root"), children: [
2394
+ /* @__PURE__ */ jsxs6(
2123
2395
  "button",
2124
2396
  {
2125
2397
  "aria-controls": contentId,
@@ -2133,25 +2405,25 @@ function ToolDisclosure({
2133
2405
  },
2134
2406
  type: "button",
2135
2407
  children: [
2136
- showOrb ? /* @__PURE__ */ jsx9(ToolActivityOrb, { label: statusLabel }) : null,
2137
- /* @__PURE__ */ jsx9("span", { className: "a24-tool-trigger__label", children: label }),
2138
- open ? /* @__PURE__ */ jsx9(ChevronDown2, { "aria-hidden": "true", className: "a24-tool-trigger__chevron" }) : /* @__PURE__ */ jsx9(ChevronRight2, { "aria-hidden": "true", className: "a24-tool-trigger__chevron" })
2408
+ showOrb ? /* @__PURE__ */ jsx10(ToolActivityOrb, { label: statusLabel }) : null,
2409
+ /* @__PURE__ */ jsx10("span", { className: "a24-tool-trigger__label", children: label }),
2410
+ open ? /* @__PURE__ */ jsx10(ChevronDown2, { "aria-hidden": "true", className: "a24-tool-trigger__chevron" }) : /* @__PURE__ */ jsx10(ChevronRight2, { "aria-hidden": "true", className: "a24-tool-trigger__chevron" })
2139
2411
  ]
2140
2412
  }
2141
2413
  ),
2142
2414
  accessory
2143
2415
  ] }),
2144
- open ? /* @__PURE__ */ jsx9("div", { className: "a24-tool-disclosure__content", id: contentId, children }) : null
2416
+ open ? /* @__PURE__ */ jsx10("div", { className: "a24-tool-disclosure__content", id: contentId, children }) : null
2145
2417
  ] });
2146
2418
  }
2147
2419
  function ToolLabel({ active, label, part, slots }) {
2148
2420
  const rendered = slots?.renderToolLabel?.(toolSlotContext(active, label, part, slots)) ?? label;
2149
- return active ? /* @__PURE__ */ jsx9("span", { className: "a24-tool-shimmer", children: rendered }) : /* @__PURE__ */ jsx9(Fragment3, { children: rendered });
2421
+ return active ? /* @__PURE__ */ jsx10("span", { className: "a24-tool-shimmer", children: rendered }) : /* @__PURE__ */ jsx10(Fragment3, { children: rendered });
2150
2422
  }
2151
2423
  function ToolList({ children }) {
2152
- const listRef = React8.useRef(null);
2153
- const [fades, setFades] = React8.useState({ bottom: false, top: false });
2154
- const updateFades = React8.useCallback(() => {
2424
+ const listRef = React11.useRef(null);
2425
+ const [fades, setFades] = React11.useState({ bottom: false, top: false });
2426
+ const updateFades = React11.useCallback(() => {
2155
2427
  const list = listRef.current;
2156
2428
  if (!list) return;
2157
2429
  const next = {
@@ -2160,7 +2432,7 @@ function ToolList({ children }) {
2160
2432
  };
2161
2433
  setFades((current) => current.bottom === next.bottom && current.top === next.top ? current : next);
2162
2434
  }, []);
2163
- React8.useEffect(() => {
2435
+ React11.useEffect(() => {
2164
2436
  updateFades();
2165
2437
  const list = listRef.current;
2166
2438
  if (!list || typeof ResizeObserver === "undefined") return;
@@ -2168,13 +2440,13 @@ function ToolList({ children }) {
2168
2440
  observer.observe(list);
2169
2441
  return () => observer.disconnect();
2170
2442
  }, [children, updateFades]);
2171
- return /* @__PURE__ */ jsx9(
2443
+ return /* @__PURE__ */ jsx10(
2172
2444
  "div",
2173
2445
  {
2174
2446
  className: "a24-tool-list-frame",
2175
2447
  "data-scroll-fade-bottom": fades.bottom ? "" : void 0,
2176
2448
  "data-scroll-fade-top": fades.top ? "" : void 0,
2177
- children: /* @__PURE__ */ jsx9("div", { className: "a24-tool-list", onScroll: updateFades, ref: listRef, role: "list", children })
2449
+ children: /* @__PURE__ */ jsx10("div", { className: "a24-tool-list", onScroll: updateFades, ref: listRef, role: "list", children })
2178
2450
  }
2179
2451
  );
2180
2452
  }
@@ -2191,12 +2463,12 @@ function AgentChatToolRow({
2191
2463
  const detail = failed ? failureMessage : optionalText(part.presentation.detail);
2192
2464
  const hostDetails = failed ? null : slots?.renderToolDetails?.(part);
2193
2465
  const context = toolSlotContext(active, toolRowLabel(part, Boolean(slots?.onResourceAction)), part, slots);
2194
- return /* @__PURE__ */ jsx9("div", { "data-agents24-tool-part": part.type, "data-state": part.state, children: /* @__PURE__ */ jsxs5(
2466
+ return /* @__PURE__ */ jsx10("div", { "data-agents24-tool-part": part.type, "data-state": part.state, children: /* @__PURE__ */ jsxs6(
2195
2467
  ToolDisclosure,
2196
2468
  {
2197
2469
  error: failed,
2198
2470
  accessory: toolAccessory(context, slots),
2199
- label: /* @__PURE__ */ jsx9(ToolLabel, { active, label: context.label, part, slots }),
2471
+ label: /* @__PURE__ */ jsx10(ToolLabel, { active, label: context.label, part, slots }),
2200
2472
  loading: active,
2201
2473
  onExpandedChange: slots?.onExpandedChange,
2202
2474
  onSelect: slots?.onToolSelect,
@@ -2205,7 +2477,7 @@ function AgentChatToolRow({
2205
2477
  showOrb: showOrb ?? active,
2206
2478
  statusLabel: active ? context.label : void 0,
2207
2479
  children: [
2208
- detail ? /* @__PURE__ */ jsx9("div", { className: cn("a24-tool-detail", failed && "a24-tool-detail--error"), children: detail }) : null,
2480
+ detail ? /* @__PURE__ */ jsx10("div", { className: cn("a24-tool-detail", failed && "a24-tool-detail--error"), children: detail }) : null,
2209
2481
  hostDetails
2210
2482
  ]
2211
2483
  }
@@ -2225,19 +2497,19 @@ function AgentChatToolSession({
2225
2497
  parts,
2226
2498
  slots
2227
2499
  }) {
2228
- const presentedLiveRef = React8.useRef(false);
2500
+ const presentedLiveRef = React11.useRef(false);
2229
2501
  if (isLive && isLatestActivity) presentedLiveRef.current = true;
2230
2502
  const activeIndex = parts.findLastIndex((part) => isActive(part, activeToolId));
2231
2503
  const running = activeIndex >= 0;
2232
- if (parts.length === 1 && !modelTurn && !leadingModelTurn) return /* @__PURE__ */ jsx9(AgentChatToolRow, { activeToolId, part: parts[0], slots });
2504
+ if (parts.length === 1 && !modelTurn && !leadingModelTurn) return /* @__PURE__ */ jsx10(AgentChatToolRow, { activeToolId, part: parts[0], slots });
2233
2505
  const visibleIndex = running ? activeIndex : parts.length - 1;
2234
2506
  const visiblePart = parts[visibleIndex];
2235
2507
  const planning = Boolean(modelTurn);
2236
2508
  const presentingActivity = isLatestActivity && (planning || running || isLive);
2237
2509
  const failed = isLatestActivity && (visiblePart.state === "output-error" || visiblePart.state === "cancelled");
2238
2510
  const label = planning ? "Planning next moves" : isLive && isLatestActivity ? toolRowLabel(visiblePart, true) : sessionLabel(parts, visiblePart, running);
2239
- const labelContent = presentedLiveRef.current ? /* @__PURE__ */ jsx9("span", { className: presentingActivity ? "a24-tool-shimmer" : void 0, children: /* @__PURE__ */ jsx9(ToolActivityReel, { initialLabel: leadingModelTurn ? "Planning next moves" : void 0, initialShownAt: leadingModelTurn?.timestamp, label }) }) : label;
2240
- return /* @__PURE__ */ jsx9(
2511
+ const labelContent = presentedLiveRef.current ? /* @__PURE__ */ jsx10("span", { className: presentingActivity ? "a24-tool-shimmer" : void 0, children: /* @__PURE__ */ jsx10(ToolActivityReel, { initialLabel: leadingModelTurn ? "Planning next moves" : void 0, initialShownAt: leadingModelTurn?.timestamp, label }) }) : label;
2512
+ return /* @__PURE__ */ jsx10(
2241
2513
  ToolDisclosure,
2242
2514
  {
2243
2515
  error: failed,
@@ -2249,7 +2521,7 @@ function AgentChatToolSession({
2249
2521
  root: true,
2250
2522
  showOrb: isLive && isLatestActivity,
2251
2523
  statusLabel: planning ? "Planning next moves" : label,
2252
- children: /* @__PURE__ */ jsx9(ToolList, { children: parts.map((part) => /* @__PURE__ */ jsx9("div", { role: "listitem", children: /* @__PURE__ */ jsx9(AgentChatToolRow, { activeToolId, part, root: false, showOrb: false, slots }) }, part.id)) })
2524
+ children: /* @__PURE__ */ jsx10(ToolList, { children: parts.map((part) => /* @__PURE__ */ jsx10("div", { role: "listitem", children: /* @__PURE__ */ jsx10(AgentChatToolRow, { activeToolId, part, root: false, showOrb: false, slots }) }, part.id)) })
2253
2525
  }
2254
2526
  );
2255
2527
  }
@@ -2270,7 +2542,7 @@ function subagentLabel(part) {
2270
2542
  return { label: `Agent ${name}, ran ${children.length} ${children.length === 1 ? "tool" : "tools"}`, running: false };
2271
2543
  }
2272
2544
  function AgentChatSubagentGroup({ isLatestActivity = true, isLive = false, part, slots }) {
2273
- const presentedLiveRef = React8.useRef(false);
2545
+ const presentedLiveRef = React11.useRef(false);
2274
2546
  if (isLive && isLatestActivity) presentedLiveRef.current = true;
2275
2547
  const children = part.presentation.childTools || [];
2276
2548
  const state = subagentLabel(part);
@@ -2279,9 +2551,9 @@ function AgentChatSubagentGroup({ isLatestActivity = true, isLive = false, part,
2279
2551
  const hostDetails = failed ? null : slots?.renderToolDetails?.(part);
2280
2552
  const failureMessage = part.failure?.message || null;
2281
2553
  const presentingActivity = isLatestActivity && state.running;
2282
- const labelContent = presentedLiveRef.current ? /* @__PURE__ */ jsx9("span", { className: presentingActivity ? "a24-tool-shimmer" : void 0, children: /* @__PURE__ */ jsx9(ToolActivityReel, { label: state.label }) }) : state.label;
2554
+ const labelContent = presentedLiveRef.current ? /* @__PURE__ */ jsx10("span", { className: presentingActivity ? "a24-tool-shimmer" : void 0, children: /* @__PURE__ */ jsx10(ToolActivityReel, { label: state.label }) }) : state.label;
2283
2555
  const context = toolSlotContext(presentingActivity, state.label, part, slots);
2284
- return /* @__PURE__ */ jsx9(
2556
+ return /* @__PURE__ */ jsx10(
2285
2557
  ToolDisclosure,
2286
2558
  {
2287
2559
  accessory: slots?.renderToolAccessory?.(context),
@@ -2294,12 +2566,12 @@ function AgentChatSubagentGroup({ isLatestActivity = true, isLive = false, part,
2294
2566
  root: true,
2295
2567
  showOrb: isLive && isLatestActivity,
2296
2568
  statusLabel: state.label,
2297
- children: /* @__PURE__ */ jsxs5(ToolList, { children: [
2298
- children.map((child) => /* @__PURE__ */ jsx9("div", { className: cn("a24-child-tool", child.status === "failed" && "a24-child-tool--error"), role: "listitem", children: slots?.renderChildTool?.(child, part) ?? /* @__PURE__ */ jsxs5(Fragment3, { children: [
2299
- /* @__PURE__ */ jsx9("span", { className: cn(isLive && isLatestActivity && child.status === "running" && "a24-tool-shimmer"), children: childLabel(child) }),
2300
- child.detail ? /* @__PURE__ */ jsx9("span", { className: "a24-child-tool__detail", children: child.detail }) : null
2569
+ children: /* @__PURE__ */ jsxs6(ToolList, { children: [
2570
+ children.map((child) => /* @__PURE__ */ jsx10("div", { className: cn("a24-child-tool", child.status === "failed" && "a24-child-tool--error"), role: "listitem", children: slots?.renderChildTool?.(child, part) ?? /* @__PURE__ */ jsxs6(Fragment3, { children: [
2571
+ /* @__PURE__ */ jsx10("span", { className: cn(isLive && isLatestActivity && child.status === "running" && "a24-tool-shimmer"), children: childLabel(child) }),
2572
+ child.detail ? /* @__PURE__ */ jsx10("span", { className: "a24-child-tool__detail", children: child.detail }) : null
2301
2573
  ] }) }, child.id)),
2302
- failureMessage ? /* @__PURE__ */ jsx9("div", { className: "a24-tool-detail a24-tool-detail--error", children: failureMessage }) : null,
2574
+ failureMessage ? /* @__PURE__ */ jsx10("div", { className: "a24-tool-detail a24-tool-detail--error", children: failureMessage }) : null,
2303
2575
  hostDetails
2304
2576
  ] })
2305
2577
  }
@@ -2307,24 +2579,24 @@ function AgentChatSubagentGroup({ isLatestActivity = true, isLive = false, part,
2307
2579
  }
2308
2580
 
2309
2581
  // src/ui/agent-response-timeline.tsx
2310
- import { jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
2582
+ import { jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
2311
2583
  function isInternalAskUserTool(part) {
2312
2584
  return [part.toolName, part.toolKey, part.action].some((value) => value?.trim().toLowerCase() === "runtime__ask_user");
2313
2585
  }
2314
2586
  function Shimmer({ children, className }) {
2315
- return /* @__PURE__ */ jsx10("span", { className: cn("inline-block animate-pulse", className), children });
2587
+ return /* @__PURE__ */ jsx11("span", { className: cn("inline-block animate-pulse", className), children });
2316
2588
  }
2317
2589
  function AgentChatToolPart({
2318
2590
  activeToolId,
2319
2591
  part
2320
2592
  }) {
2321
- return /* @__PURE__ */ jsx10(AgentChatToolRow, { activeToolId, part });
2593
+ return /* @__PURE__ */ jsx11(AgentChatToolRow, { activeToolId, part });
2322
2594
  }
2323
2595
  function AgentChatToolGroup({
2324
2596
  activeToolId,
2325
2597
  parts
2326
2598
  }) {
2327
- return /* @__PURE__ */ jsx10(AgentChatToolSession, { activeToolId, parts });
2599
+ return /* @__PURE__ */ jsx11(AgentChatToolSession, { activeToolId, parts });
2328
2600
  }
2329
2601
  function HitlPartRow({
2330
2602
  getHitlActionState,
@@ -2333,18 +2605,18 @@ function HitlPartRow({
2333
2605
  part
2334
2606
  }) {
2335
2607
  const state = getHitlActionState?.(part);
2336
- const [comment, setComment] = React9.useState("");
2608
+ const [comment, setComment] = React12.useState("");
2337
2609
  const presentation = part.presentation || {};
2338
2610
  const requireComment = presentation.require_comment === true;
2339
2611
  const isTerminal = part.status !== "pending";
2340
- const resolvedRef = React9.useRef(null);
2341
- const wasTerminal = React9.useRef(isTerminal);
2342
- React9.useEffect(() => {
2612
+ const resolvedRef = React12.useRef(null);
2613
+ const wasTerminal = React12.useRef(isTerminal);
2614
+ React12.useEffect(() => {
2343
2615
  if (isTerminal && !wasTerminal.current) resolvedRef.current?.focus();
2344
2616
  wasTerminal.current = isTerminal;
2345
2617
  }, [isTerminal]);
2346
2618
  if (part.hitlKind === "user_input") {
2347
- return /* @__PURE__ */ jsx10(
2619
+ return /* @__PURE__ */ jsx11(
2348
2620
  AskUserInteraction,
2349
2621
  {
2350
2622
  error: state?.error,
@@ -2362,11 +2634,11 @@ function HitlPartRow({
2362
2634
  );
2363
2635
  }
2364
2636
  if (isTerminal) {
2365
- return /* @__PURE__ */ jsx10("div", { className: "a24-hitl a24-hitl--resolved px-1 py-1 text-sm text-muted-foreground", ref: resolvedRef, role: "status", tabIndex: -1, children: resolvedHitlLabel(part) });
2637
+ return /* @__PURE__ */ jsx11("div", { className: "a24-hitl a24-hitl--resolved px-1 py-1 text-sm text-muted-foreground", ref: resolvedRef, role: "status", tabIndex: -1, children: resolvedHitlLabel(part) });
2366
2638
  }
2367
- return /* @__PURE__ */ jsxs6("div", { className: "a24-hitl space-y-2.5 px-1 py-2", children: [
2368
- /* @__PURE__ */ jsx10("div", { className: "a24-hitl__title text-sm font-medium text-foreground text-pretty", children: hitlTitle(part) }),
2369
- requireComment ? /* @__PURE__ */ jsx10(
2639
+ return /* @__PURE__ */ jsxs7("div", { className: "a24-hitl space-y-2.5 px-1 py-2", children: [
2640
+ /* @__PURE__ */ jsx11("div", { className: "a24-hitl__title text-sm font-medium text-foreground text-pretty", children: hitlTitle(part) }),
2641
+ requireComment ? /* @__PURE__ */ jsx11(
2370
2642
  "textarea",
2371
2643
  {
2372
2644
  "aria-label": "Approval comment",
@@ -2378,8 +2650,8 @@ function HitlPartRow({
2378
2650
  value: comment
2379
2651
  }
2380
2652
  ) : null,
2381
- state?.error ? /* @__PURE__ */ jsx10("div", { className: "a24-hitl__error text-sm text-destructive", children: state.error }) : null,
2382
- /* @__PURE__ */ jsx10("div", { className: "a24-hitl__actions flex flex-wrap items-center gap-1.5", children: part.allowedActions.map((action) => /* @__PURE__ */ jsx10(
2653
+ state?.error ? /* @__PURE__ */ jsx11("div", { className: "a24-hitl__error text-sm text-destructive", children: state.error }) : null,
2654
+ /* @__PURE__ */ jsx11("div", { className: "a24-hitl__actions flex flex-wrap items-center gap-1.5", children: part.allowedActions.map((action) => /* @__PURE__ */ jsx11(
2383
2655
  "button",
2384
2656
  {
2385
2657
  "aria-label": hitlActionLabel(part, action),
@@ -2412,7 +2684,7 @@ function AgentChatPart({
2412
2684
  toolSlots
2413
2685
  }) {
2414
2686
  if (part.kind === "text") {
2415
- return /* @__PURE__ */ jsx10(
2687
+ return /* @__PURE__ */ jsx11(
2416
2688
  MessageResponse,
2417
2689
  {
2418
2690
  ...responseProps,
@@ -2422,23 +2694,23 @@ function AgentChatPart({
2422
2694
  }
2423
2695
  );
2424
2696
  }
2425
- if (part.kind === "tool") return isAgentChatSubagent(part) ? /* @__PURE__ */ jsx10(AgentChatSubagentGroup, { part, slots: toolSlots }) : /* @__PURE__ */ jsx10(AgentChatToolRow, { activeToolId, part, slots: toolSlots });
2426
- if (part.kind === "model-turn") return /* @__PURE__ */ jsx10(AgentChatModelTurnRow, { part });
2697
+ if (part.kind === "tool") return isAgentChatSubagent(part) ? /* @__PURE__ */ jsx11(AgentChatSubagentGroup, { part, slots: toolSlots }) : /* @__PURE__ */ jsx11(AgentChatToolRow, { activeToolId, part, slots: toolSlots });
2698
+ if (part.kind === "model-turn") return /* @__PURE__ */ jsx11(AgentChatModelTurnRow, { part });
2427
2699
  if (part.kind === "reasoning") {
2428
2700
  const label = part.text || part.label || "Thinking";
2429
- return part.status === "running" || part.status === "streaming" ? /* @__PURE__ */ jsx10(Shimmer, { className: "text-sm text-muted-foreground", children: label }) : /* @__PURE__ */ jsx10("div", { className: "text-sm text-muted-foreground", children: label });
2701
+ return part.status === "running" || part.status === "streaming" ? /* @__PURE__ */ jsx11(Shimmer, { className: "text-sm text-muted-foreground", children: label }) : /* @__PURE__ */ jsx11("div", { className: "text-sm text-muted-foreground", children: label });
2430
2702
  }
2431
2703
  if (part.kind === "context-compression") {
2432
- return /* @__PURE__ */ jsx10(AgentChatContextCompressionRow, { part });
2704
+ return /* @__PURE__ */ jsx11(AgentChatContextCompressionRow, { part });
2433
2705
  }
2434
2706
  if (part.kind === "ui-blocks") {
2435
2707
  if (part.state === "output-error") {
2436
- return /* @__PURE__ */ jsx10("div", { className: "a24-timeline-error text-sm text-destructive", children: part.errorText || "Failed to render UI content." });
2708
+ return /* @__PURE__ */ jsx11("div", { className: "a24-timeline-error text-sm text-destructive", children: part.errorText || "Failed to render UI content." });
2437
2709
  }
2438
- return part.bundle ? /* @__PURE__ */ jsx10(SafeUiBlocks, { bundle: part.bundle }) : /* @__PURE__ */ jsx10("div", { className: "text-sm text-muted-foreground", children: part.state === "input-streaming" ? /* @__PURE__ */ jsx10(Shimmer, { children: "Rendering UI block" }) : "Rendered UI block" });
2710
+ return part.bundle ? /* @__PURE__ */ jsx11(SafeUiBlocks, { bundle: part.bundle }) : /* @__PURE__ */ jsx11("div", { className: "text-sm text-muted-foreground", children: part.state === "input-streaming" ? /* @__PURE__ */ jsx11(Shimmer, { children: "Rendering UI block" }) : "Rendered UI block" });
2439
2711
  }
2440
2712
  if (part.kind === "hitl") {
2441
- return /* @__PURE__ */ jsx10(
2713
+ return /* @__PURE__ */ jsx11(
2442
2714
  HitlPartRow,
2443
2715
  {
2444
2716
  getHitlActionState,
@@ -2448,18 +2720,18 @@ function AgentChatPart({
2448
2720
  }
2449
2721
  );
2450
2722
  }
2451
- if (part.kind === "error") return /* @__PURE__ */ jsx10("div", { className: "a24-timeline-error text-sm text-destructive", role: "alert", children: part.errorText });
2723
+ if (part.kind === "error") return /* @__PURE__ */ jsx11("div", { className: "a24-timeline-error text-sm text-destructive", role: "alert", children: part.errorText });
2452
2724
  if (part.kind === "source") {
2453
2725
  const href = safeExternalUrl(part.url);
2454
- return href ? /* @__PURE__ */ jsx10("a", { className: "a24-source-link", href, rel: "noopener noreferrer", target: "_blank", children: part.title }) : /* @__PURE__ */ jsx10("span", { className: "a24-source-link", children: part.title });
2726
+ return href ? /* @__PURE__ */ jsx11("a", { className: "a24-source-link", href, rel: "noopener noreferrer", target: "_blank", children: part.title }) : /* @__PURE__ */ jsx11("span", { className: "a24-source-link", children: part.title });
2455
2727
  }
2456
2728
  if (part.kind === "citation") {
2457
2729
  const href = safeExternalUrl(part.url);
2458
2730
  const label = part.label || "Citation";
2459
- return href ? /* @__PURE__ */ jsx10("a", { className: "a24-citation", href, rel: "noopener noreferrer", target: "_blank", children: label }) : /* @__PURE__ */ jsx10("span", { className: "a24-citation", children: label });
2731
+ return href ? /* @__PURE__ */ jsx11("a", { className: "a24-citation", href, rel: "noopener noreferrer", target: "_blank", children: label }) : /* @__PURE__ */ jsx11("span", { className: "a24-citation", children: label });
2460
2732
  }
2461
- if (part.kind === "attachment") return /* @__PURE__ */ jsx10("div", { className: "a24-output-attachment", children: part.attachment.filename || "Attachment" });
2462
- if (part.kind === "data") return /* @__PURE__ */ jsx10("div", { "data-agents24-data-part": part.name, className: "hidden", children: message.id });
2733
+ if (part.kind === "attachment") return /* @__PURE__ */ jsx11("div", { className: "a24-output-attachment", children: part.attachment.filename || "Attachment" });
2734
+ if (part.kind === "data") return /* @__PURE__ */ jsx11("div", { "data-agents24-data-part": part.name, className: "hidden", children: message.id });
2463
2735
  return null;
2464
2736
  }
2465
2737
  function safeExternalUrl(value) {
@@ -2473,28 +2745,28 @@ function safeExternalUrl(value) {
2473
2745
  }
2474
2746
  function SafeUiBlocks({ bundle }) {
2475
2747
  const rows = Array.isArray(bundle.rows) ? bundle.rows : [];
2476
- return /* @__PURE__ */ jsxs6("section", { "aria-label": typeof bundle.title === "string" ? bundle.title : "Agent result", className: "a24-ui-blocks", children: [
2477
- typeof bundle.title === "string" ? /* @__PURE__ */ jsx10("h3", { children: bundle.title }) : null,
2748
+ return /* @__PURE__ */ jsxs7("section", { "aria-label": typeof bundle.title === "string" ? bundle.title : "Agent result", className: "a24-ui-blocks", children: [
2749
+ typeof bundle.title === "string" ? /* @__PURE__ */ jsx11("h3", { children: bundle.title }) : null,
2478
2750
  rows.map((rawRow, rowIndex) => {
2479
2751
  const row = rawRow && typeof rawRow === "object" && !Array.isArray(rawRow) ? rawRow : {};
2480
2752
  const blocks = Array.isArray(row.blocks) ? row.blocks : [];
2481
- return /* @__PURE__ */ jsx10("div", { className: "a24-ui-blocks__row", children: blocks.map((rawBlock, blockIndex) => {
2753
+ return /* @__PURE__ */ jsx11("div", { className: "a24-ui-blocks__row", children: blocks.map((rawBlock, blockIndex) => {
2482
2754
  const block = rawBlock && typeof rawBlock === "object" && !Array.isArray(rawBlock) ? rawBlock : {};
2483
2755
  const title = typeof block.title === "string" ? block.title : "Result";
2484
2756
  const kind = String(block.kind || "note");
2485
2757
  if (kind === "table" && Array.isArray(block.columns) && Array.isArray(block.rows)) {
2486
- return /* @__PURE__ */ jsxs6("div", { className: "a24-ui-block", children: [
2487
- /* @__PURE__ */ jsx10("h4", { children: title }),
2488
- /* @__PURE__ */ jsx10("div", { className: "a24-ui-table-scroll", children: /* @__PURE__ */ jsxs6("table", { children: [
2489
- /* @__PURE__ */ jsx10("thead", { children: /* @__PURE__ */ jsx10("tr", { children: block.columns.map((column, index) => /* @__PURE__ */ jsx10("th", { children: String(column) }, index)) }) }),
2490
- /* @__PURE__ */ jsx10("tbody", { children: block.rows.map((rawCells, index) => /* @__PURE__ */ jsx10("tr", { children: (Array.isArray(rawCells) ? rawCells : []).map((cell, cellIndex) => /* @__PURE__ */ jsx10("td", { children: String(cell) }, cellIndex)) }, index)) })
2758
+ return /* @__PURE__ */ jsxs7("div", { className: "a24-ui-block", children: [
2759
+ /* @__PURE__ */ jsx11("h4", { children: title }),
2760
+ /* @__PURE__ */ jsx11("div", { className: "a24-ui-table-scroll", children: /* @__PURE__ */ jsxs7("table", { children: [
2761
+ /* @__PURE__ */ jsx11("thead", { children: /* @__PURE__ */ jsx11("tr", { children: block.columns.map((column, index) => /* @__PURE__ */ jsx11("th", { children: String(column) }, index)) }) }),
2762
+ /* @__PURE__ */ jsx11("tbody", { children: block.rows.map((rawCells, index) => /* @__PURE__ */ jsx11("tr", { children: (Array.isArray(rawCells) ? rawCells : []).map((cell, cellIndex) => /* @__PURE__ */ jsx11("td", { children: String(cell) }, cellIndex)) }, index)) })
2491
2763
  ] }) })
2492
2764
  ] }, String(block.id || blockIndex));
2493
2765
  }
2494
2766
  const value = block.value ?? block.text ?? "";
2495
- return /* @__PURE__ */ jsxs6("div", { className: "a24-ui-block", children: [
2496
- /* @__PURE__ */ jsx10("h4", { children: title }),
2497
- /* @__PURE__ */ jsx10("p", { children: String(value) })
2767
+ return /* @__PURE__ */ jsxs7("div", { className: "a24-ui-block", children: [
2768
+ /* @__PURE__ */ jsx11("h4", { children: title }),
2769
+ /* @__PURE__ */ jsx11("p", { children: String(value) })
2498
2770
  ] }, String(block.id || blockIndex));
2499
2771
  }) }, `row-${rowIndex}`);
2500
2772
  })
@@ -2528,38 +2800,38 @@ function AgentResponseTimeline({
2528
2800
  if (segment.kind === "tool") {
2529
2801
  const isLatestActivity = segmentIndex === segments.length - 1;
2530
2802
  const showOrb = toolViewMode === "closed" ? isLoading && isLatestActivity : Boolean(activeToolId && (segment.part.id === activeToolId || segment.part.toolCallId === activeToolId));
2531
- rendered.push(/* @__PURE__ */ jsx10(AgentChatToolRow, { activeToolId, part: segment.part, showOrb, slots: toolSlots }, segment.part.id));
2803
+ rendered.push(/* @__PURE__ */ jsx11(AgentChatToolRow, { activeToolId, part: segment.part, showOrb, slots: toolSlots }, segment.part.id));
2532
2804
  continue;
2533
2805
  }
2534
2806
  if (segment.kind === "tool-session") {
2535
- rendered.push(/* @__PURE__ */ jsx10(AgentChatToolSession, { activeToolId, isLatestActivity: segmentIndex === segments.length - 1, isLive: isLoading, leadingModelTurn: segment.leadingModelTurn, modelTurn: segment.modelTurn, parts: segment.parts, slots: toolSlots }, `tool-session-${segment.parts[0].id}`));
2807
+ rendered.push(/* @__PURE__ */ jsx11(AgentChatToolSession, { activeToolId, isLatestActivity: segmentIndex === segments.length - 1, isLive: isLoading, leadingModelTurn: segment.leadingModelTurn, modelTurn: segment.modelTurn, parts: segment.parts, slots: toolSlots }, `tool-session-${segment.parts[0].id}`));
2536
2808
  continue;
2537
2809
  }
2538
2810
  if (segment.kind === "subagent") {
2539
- rendered.push(/* @__PURE__ */ jsx10(AgentChatSubagentGroup, { isLatestActivity: segmentIndex === segments.length - 1, isLive: isLoading, part: segment.part, slots: toolSlots }, segment.part.id));
2811
+ rendered.push(/* @__PURE__ */ jsx11(AgentChatSubagentGroup, { isLatestActivity: segmentIndex === segments.length - 1, isLive: isLoading, part: segment.part, slots: toolSlots }, segment.part.id));
2540
2812
  continue;
2541
2813
  }
2542
2814
  const part = segment.part;
2543
2815
  if (part.kind === "model-turn") {
2544
2816
  const key = activeActivityClaimed ? part.id : `${message.id}-active-activity`;
2545
2817
  activeActivityClaimed = true;
2546
- rendered.push(/* @__PURE__ */ jsx10(AgentChatActivityRow, { label: "Planning next moves", modelTurnId: part.modelTurnId }, key));
2818
+ rendered.push(/* @__PURE__ */ jsx11(AgentChatActivityRow, { label: "Planning next moves", modelTurnId: part.modelTurnId }, key));
2547
2819
  continue;
2548
2820
  }
2549
2821
  if (part.kind === "node-activity") {
2550
2822
  const key = activeActivityClaimed ? part.id : `${message.id}-active-activity`;
2551
2823
  if (part.status === "running") activeActivityClaimed = true;
2552
2824
  rendered.push(
2553
- part.status === "running" ? /* @__PURE__ */ jsx10(AgentChatActivityRow, { label: part.label, onSelect: onNodeSelect ? () => onNodeSelect(part) : void 0 }, key) : /* @__PURE__ */ jsx10(AgentChatNodeActivityRow, { onSelect: onNodeSelect, part }, part.id)
2825
+ part.status === "running" ? /* @__PURE__ */ jsx11(AgentChatActivityRow, { label: part.label, onSelect: onNodeSelect ? () => onNodeSelect(part) : void 0 }, key) : /* @__PURE__ */ jsx11(AgentChatNodeActivityRow, { onSelect: onNodeSelect, part }, part.id)
2554
2826
  );
2555
2827
  continue;
2556
2828
  }
2557
2829
  if (part.kind === "ui-blocks" && renderUiBlocks) {
2558
- rendered.push(/* @__PURE__ */ jsx10(React9.Fragment, { children: renderUiBlocks(part) }, part.id));
2830
+ rendered.push(/* @__PURE__ */ jsx11(React12.Fragment, { children: renderUiBlocks(part) }, part.id));
2559
2831
  continue;
2560
2832
  }
2561
2833
  rendered.push(
2562
- /* @__PURE__ */ jsx10(React9.Fragment, { children: /* @__PURE__ */ jsx10(
2834
+ /* @__PURE__ */ jsx11(React12.Fragment, { children: /* @__PURE__ */ jsx11(
2563
2835
  AgentChatPart,
2564
2836
  {
2565
2837
  activeToolId,
@@ -2576,16 +2848,16 @@ function AgentResponseTimeline({
2576
2848
  );
2577
2849
  }
2578
2850
  if (rendered.length === 0 && message.content) {
2579
- rendered.push(/* @__PURE__ */ jsx10(MessageResponse, { children: message.content }, `${message.id}-fallback`));
2851
+ rendered.push(/* @__PURE__ */ jsx11(MessageResponse, { children: message.content }, `${message.id}-fallback`));
2580
2852
  }
2581
2853
  if (rendered.length === 0 && isLoading) {
2582
- rendered.push(/* @__PURE__ */ jsx10(AgentChatActivityRow, { label: "Thinking" }, `${message.id}-active-activity`));
2854
+ rendered.push(/* @__PURE__ */ jsx11(AgentChatActivityRow, { label: "Thinking" }, `${message.id}-active-activity`));
2583
2855
  }
2584
- return /* @__PURE__ */ jsx10("div", { className: "space-y-3", children: rendered });
2856
+ return /* @__PURE__ */ jsx11("div", { className: "space-y-3", children: rendered });
2585
2857
  }
2586
2858
 
2587
2859
  // src/ui/agent-chat-message.tsx
2588
- import { jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
2860
+ import { jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
2589
2861
  var DEFAULT_USER_MESSAGE_COLLAPSE_THRESHOLD = 360;
2590
2862
  var DEFAULT_USER_MESSAGE_COLLAPSED_LINES = 4;
2591
2863
  function shouldCollapseUserMessage(content, threshold = DEFAULT_USER_MESSAGE_COLLAPSE_THRESHOLD) {
@@ -2598,8 +2870,8 @@ function AgentChatUserMessageContent({
2598
2870
  showLessLabel = "Show less",
2599
2871
  showMoreLabel = "Show more"
2600
2872
  }) {
2601
- const [isExpanded, setIsExpanded] = React10.useState(false);
2602
- const contentId = React10.useId();
2873
+ const [isExpanded, setIsExpanded] = React13.useState(false);
2874
+ const contentId = React13.useId();
2603
2875
  const shouldCollapse = shouldCollapseUserMessage(content, collapseThreshold);
2604
2876
  const Icon = isExpanded ? ChevronUp : ChevronDown3;
2605
2877
  const clampedStyle = shouldCollapse && !isExpanded ? {
@@ -2608,9 +2880,9 @@ function AgentChatUserMessageContent({
2608
2880
  display: "-webkit-box",
2609
2881
  overflow: "hidden"
2610
2882
  } : void 0;
2611
- return /* @__PURE__ */ jsxs7("div", { className: "flex min-w-0 flex-col gap-3", children: [
2612
- /* @__PURE__ */ jsx11("div", { id: contentId, className: "min-w-0", style: clampedStyle, children: content }),
2613
- shouldCollapse ? /* @__PURE__ */ jsxs7(
2883
+ return /* @__PURE__ */ jsxs8("div", { className: "flex min-w-0 flex-col gap-3", children: [
2884
+ /* @__PURE__ */ jsx12("div", { id: contentId, className: "min-w-0", style: clampedStyle, children: content }),
2885
+ shouldCollapse ? /* @__PURE__ */ jsxs8(
2614
2886
  "button",
2615
2887
  {
2616
2888
  "aria-controls": contentId,
@@ -2623,7 +2895,7 @@ function AgentChatUserMessageContent({
2623
2895
  type: "button",
2624
2896
  children: [
2625
2897
  isExpanded ? showLessLabel : showMoreLabel,
2626
- /* @__PURE__ */ jsx11(Icon, { "aria-hidden": "true", className: "size-4", strokeWidth: 2 })
2898
+ /* @__PURE__ */ jsx12(Icon, { "aria-hidden": "true", className: "size-4", strokeWidth: 2 })
2627
2899
  ]
2628
2900
  }
2629
2901
  ) : null
@@ -2633,9 +2905,9 @@ function AgentChatUserMessageActionRow({
2633
2905
  children,
2634
2906
  className
2635
2907
  }) {
2636
- const rowRef = React10.useRef(null);
2637
- const [visible, setVisible] = React10.useState(false);
2638
- React10.useEffect(() => {
2908
+ const rowRef = React13.useRef(null);
2909
+ const [visible, setVisible] = React13.useState(false);
2910
+ React13.useEffect(() => {
2639
2911
  const message = rowRef.current?.closest('[data-slot="message"]');
2640
2912
  if (!message) return;
2641
2913
  const show = () => setVisible(true);
@@ -2654,7 +2926,7 @@ function AgentChatUserMessageActionRow({
2654
2926
  message.removeEventListener("focusout", hideWhenFocusLeaves);
2655
2927
  };
2656
2928
  }, []);
2657
- return /* @__PURE__ */ jsx11(
2929
+ return /* @__PURE__ */ jsx12(
2658
2930
  "div",
2659
2931
  {
2660
2932
  ref: rowRef,
@@ -2693,7 +2965,7 @@ function AgentChatMessage({
2693
2965
  const resolvedUserContent = userMessageContent ?? message.content;
2694
2966
  const shouldRenderContent = isAssistant || Boolean(renderUserContent || resolvedUserContent);
2695
2967
  const showAttachments = Boolean(message.attachments?.length) && (message.role === "user" || showAssistantAttachments);
2696
- const renderedActions = actions ?? (actionHandlers ? /* @__PURE__ */ jsx11(
2968
+ const renderedActions = actions ?? (actionHandlers ? /* @__PURE__ */ jsx12(
2697
2969
  AgentChatDefaultActions,
2698
2970
  {
2699
2971
  additionalActions,
@@ -2703,14 +2975,14 @@ function AgentChatMessage({
2703
2975
  }
2704
2976
  ) : null);
2705
2977
  const defaultUserActions = !actions && Boolean(actionHandlers) && !isAssistant;
2706
- return /* @__PURE__ */ jsxs7(
2978
+ return /* @__PURE__ */ jsxs8(
2707
2979
  ChatMessage,
2708
2980
  {
2709
2981
  className,
2710
2982
  dir,
2711
2983
  from: message.role,
2712
2984
  children: [
2713
- showAttachments ? /* @__PURE__ */ jsx11(
2985
+ showAttachments ? /* @__PURE__ */ jsx12(
2714
2986
  ChatAttachmentRows,
2715
2987
  {
2716
2988
  attachments: message.attachments || [],
@@ -2719,14 +2991,14 @@ function AgentChatMessage({
2719
2991
  resolveContent: attachmentContentResolver
2720
2992
  }
2721
2993
  ) : null,
2722
- shouldRenderContent ? /* @__PURE__ */ jsxs7(
2994
+ shouldRenderContent ? /* @__PURE__ */ jsxs8(
2723
2995
  ChatMessageContent,
2724
2996
  {
2725
2997
  className: cn(isAssistant ? "bg-transparent p-0" : void 0, contentClassName),
2726
2998
  dir,
2727
2999
  from: message.role,
2728
3000
  children: [
2729
- isAssistant ? renderAssistantContent ? renderAssistantContent({ isStreaming, message }) : /* @__PURE__ */ jsx11(
3001
+ isAssistant ? renderAssistantContent ? renderAssistantContent({ isStreaming, message }) : /* @__PURE__ */ jsx12(
2730
3002
  Timeline,
2731
3003
  {
2732
3004
  getHitlActionState,
@@ -2737,7 +3009,7 @@ function AgentChatMessage({
2737
3009
  parts: message.parts
2738
3010
  }
2739
3011
  ) : null,
2740
- !isAssistant ? renderUserContent ? renderUserContent({ message }) : resolvedUserContent ? /* @__PURE__ */ jsx11(
3012
+ !isAssistant ? renderUserContent ? renderUserContent({ message }) : resolvedUserContent ? /* @__PURE__ */ jsx12(
2741
3013
  AgentChatUserMessageContent,
2742
3014
  {
2743
3015
  collapsedLines: userMessageCollapsedLines,
@@ -2750,108 +3022,7 @@ function AgentChatMessage({
2750
3022
  ]
2751
3023
  }
2752
3024
  ) : null,
2753
- renderedActions ? defaultUserActions ? /* @__PURE__ */ jsx11(AgentChatUserMessageActionRow, { className: actionsClassName, children: renderedActions }) : /* @__PURE__ */ jsx11("div", { className: cn(dir === "rtl" ? "mt-1 h-9 w-full" : "mt-1 h-9 w-full -translate-x-2", actionsClassName), children: renderedActions }) : null
2754
- ]
2755
- }
2756
- );
2757
- }
2758
-
2759
- // src/ui/agent-chat-context-status.tsx
2760
- import * as React11 from "react";
2761
- import { jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
2762
- var COMPACT_TOKENS = new Intl.NumberFormat("en-US", { notation: "compact" });
2763
- var PERCENT = new Intl.NumberFormat("en-US", {
2764
- style: "percent",
2765
- maximumFractionDigits: 1
2766
- });
2767
- var EXACT_SOURCES = /* @__PURE__ */ new Set(["exact", "provider_usage"]);
2768
- function contextStatusMetrics(contextWindow) {
2769
- const maximum = Number(contextWindow?.max_tokens || 0);
2770
- const input = Math.max(0, Number(contextWindow?.input_tokens || 0));
2771
- const admissionInput = Math.max(0, Number(contextWindow?.admission_input_tokens ?? input));
2772
- const usageRatio = Math.max(0, Math.min(1, Number(
2773
- contextWindow?.usage_ratio ?? input / maximum
2774
- )));
2775
- const admissionRatio = Math.max(0, Math.min(1, Number(
2776
- contextWindow?.admission_ratio ?? admissionInput / maximum
2777
- )));
2778
- return { maximum, input, admissionInput, usageRatio, admissionRatio };
2779
- }
2780
- function AgentChatContextStatus({
2781
- className,
2782
- contextWindow
2783
- }) {
2784
- const tooltipId = React11.useId();
2785
- const [open, setOpen] = React11.useState(false);
2786
- const { maximum, input, admissionInput, usageRatio, admissionRatio } = contextStatusMetrics(contextWindow);
2787
- const percentage = Math.round(usageRatio * 100);
2788
- const exact = EXACT_SOURCES.has(contextWindow?.source || "unknown");
2789
- if (!Number.isFinite(maximum) || maximum <= 0) return null;
2790
- return /* @__PURE__ */ jsxs8(
2791
- "div",
2792
- {
2793
- className: cn("a24-context-status", className),
2794
- onBlurCapture: (event) => {
2795
- if (!event.currentTarget.contains(event.relatedTarget)) setOpen(false);
2796
- },
2797
- onFocusCapture: () => setOpen(true),
2798
- onMouseEnter: () => setOpen(true),
2799
- onMouseLeave: () => setOpen(false),
2800
- children: [
2801
- /* @__PURE__ */ jsxs8(
2802
- "button",
2803
- {
2804
- "aria-describedby": open ? tooltipId : void 0,
2805
- "aria-label": `Context window ${percentage}% used`,
2806
- className: "a24-context-status__trigger",
2807
- onKeyDown: (event) => {
2808
- if (event.key === "Escape") setOpen(false);
2809
- },
2810
- type: "button",
2811
- children: [
2812
- /* @__PURE__ */ jsxs8("svg", { "aria-hidden": "true", className: "a24-context-status__ring", viewBox: "0 0 24 24", children: [
2813
- /* @__PURE__ */ jsx12("circle", { className: "a24-context-status__track", cx: "12", cy: "12", r: "10" }),
2814
- /* @__PURE__ */ jsx12(
2815
- "circle",
2816
- {
2817
- className: "a24-context-status__value",
2818
- cx: "12",
2819
- cy: "12",
2820
- pathLength: "100",
2821
- r: "10",
2822
- strokeDasharray: `${percentage} 100`
2823
- }
2824
- )
2825
- ] }),
2826
- /* @__PURE__ */ jsxs8("span", { children: [
2827
- percentage,
2828
- "%"
2829
- ] })
2830
- ]
2831
- }
2832
- ),
2833
- open ? /* @__PURE__ */ jsxs8("div", { className: "a24-context-status__popover", id: tooltipId, role: "tooltip", children: [
2834
- /* @__PURE__ */ jsx12("span", { className: "a24-context-status__sr-only", children: "Context window" }),
2835
- /* @__PURE__ */ jsxs8("strong", { children: [
2836
- PERCENT.format(usageRatio),
2837
- " context used"
2838
- ] }),
2839
- /* @__PURE__ */ jsxs8("span", { children: [
2840
- exact ? "Input" : "Estimated input",
2841
- ": ",
2842
- COMPACT_TOKENS.format(input),
2843
- " / ",
2844
- COMPACT_TOKENS.format(maximum),
2845
- " tokens"
2846
- ] }),
2847
- admissionInput !== input ? /* @__PURE__ */ jsxs8("span", { children: [
2848
- "Safety bound: ",
2849
- COMPACT_TOKENS.format(admissionInput),
2850
- " tokens (",
2851
- PERCENT.format(admissionRatio),
2852
- ")"
2853
- ] }) : null
2854
- ] }) : null
3025
+ renderedActions ? defaultUserActions ? /* @__PURE__ */ jsx12(AgentChatUserMessageActionRow, { className: actionsClassName, children: renderedActions }) : /* @__PURE__ */ jsx12("div", { className: cn(dir === "rtl" ? "mt-1 h-9 w-full" : "mt-1 h-9 w-full -translate-x-2", actionsClassName), children: renderedActions }) : null
2855
3026
  ]
2856
3027
  }
2857
3028
  );
@@ -3081,68 +3252,6 @@ function McpConnectRequiredCard({
3081
3252
  }
3082
3253
  );
3083
3254
  }
3084
-
3085
- // src/ui/use-audio-recorder.ts
3086
- import * as React12 from "react";
3087
- var RECORDING_CANDIDATES = ["audio/webm;codecs=opus", "audio/webm", "audio/mp4"];
3088
- function recordingExtension(mimeType) {
3089
- return mimeType.startsWith("audio/mp4") ? "m4a" : "webm";
3090
- }
3091
- function useAudioRecorder(onRecorded, options = {}) {
3092
- const recorderRef = React12.useRef(null);
3093
- const streamRef = React12.useRef(null);
3094
- const onRecordedRef = React12.useRef(onRecorded);
3095
- const onStreamChangeRef = React12.useRef(options.onStreamChange);
3096
- onRecordedRef.current = onRecorded;
3097
- onStreamChangeRef.current = options.onStreamChange;
3098
- const [isRecording, setIsRecording] = React12.useState(false);
3099
- const [error, setError] = React12.useState(null);
3100
- const stop = React12.useCallback(() => recorderRef.current?.stop(), []);
3101
- const start = React12.useCallback(async () => {
3102
- setError(null);
3103
- if (!navigator.mediaDevices?.getUserMedia || typeof MediaRecorder === "undefined") {
3104
- setError("Audio recording is not supported in this browser.");
3105
- return;
3106
- }
3107
- try {
3108
- const mimeType = RECORDING_CANDIDATES.find((candidate) => MediaRecorder.isTypeSupported(candidate));
3109
- if (!mimeType) {
3110
- setError("Audio recording is not supported in this browser.");
3111
- return;
3112
- }
3113
- const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
3114
- onStreamChangeRef.current?.(stream);
3115
- const recorder = new MediaRecorder(stream, { mimeType });
3116
- const chunks = [];
3117
- recorder.ondataavailable = (event) => {
3118
- if (event.data.size) chunks.push(event.data);
3119
- };
3120
- recorder.onstop = () => {
3121
- const actualType = recorder.mimeType || "audio/webm";
3122
- onRecordedRef.current(new File([new Blob(chunks, { type: actualType })], `recording-${Date.now()}.${recordingExtension(actualType)}`, { type: actualType }));
3123
- stream.getTracks().forEach((track) => track.stop());
3124
- onStreamChangeRef.current?.(null);
3125
- recorderRef.current = null;
3126
- streamRef.current = null;
3127
- setIsRecording(false);
3128
- };
3129
- recorderRef.current = recorder;
3130
- streamRef.current = stream;
3131
- recorder.start();
3132
- setIsRecording(true);
3133
- } catch {
3134
- streamRef.current?.getTracks().forEach((track) => track.stop());
3135
- onStreamChangeRef.current?.(null);
3136
- setError("Microphone access is unavailable.");
3137
- }
3138
- }, []);
3139
- React12.useEffect(() => () => {
3140
- recorderRef.current?.stop();
3141
- streamRef.current?.getTracks().forEach((track) => track.stop());
3142
- onStreamChangeRef.current?.(null);
3143
- }, []);
3144
- return { error, isRecording, start, stop };
3145
- }
3146
3255
  export {
3147
3256
  AgentChatActivityRow,
3148
3257
  AgentChatComposer,
@@ -3220,6 +3329,7 @@ export {
3220
3329
  resolvedHitlLabel,
3221
3330
  revokeAgentChatComposerFiles,
3222
3331
  shouldCollapseUserMessage,
3332
+ useAgentChatComposer,
3223
3333
  useAudioRecorder,
3224
3334
  useChatComposerFocus,
3225
3335
  useCompletionActionEntrance,