@agents24/chat-react 0.5.29 → 0.5.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.md +28 -1
  2. package/dist/chunk-INO5DSSJ.js +86 -0
  3. package/dist/chunk-INO5DSSJ.js.map +1 -0
  4. package/dist/generated/delegation-types.d.ts +129 -0
  5. package/dist/registry/agents24-chat.json +267 -0
  6. package/dist/runtime/index.cjs +53 -1
  7. package/dist/runtime/index.cjs.map +1 -1
  8. package/dist/runtime/index.d.ts +2 -0
  9. package/dist/runtime/index.js +62 -78
  10. package/dist/runtime/index.js.map +1 -1
  11. package/dist/runtime/input-contract.d.ts +3 -1
  12. package/dist/runtime/use-delegation-tasks.d.ts +20 -0
  13. package/dist/scaffold/app.d.ts +3 -0
  14. package/dist/scaffold/components/chat/chat-composer.d.ts +5 -3
  15. package/dist/scaffold/components/chat/subagent-tasks.d.ts +3 -0
  16. package/dist/scaffold/components/chat/theme-menu.d.ts +1 -1
  17. package/dist/scaffold/components/theme-provider.d.ts +3 -1
  18. package/dist/scaffold/index.cjs +420 -333
  19. package/dist/scaffold/index.cjs.map +1 -1
  20. package/dist/scaffold/index.d.ts +5 -2
  21. package/dist/scaffold/index.js +415 -332
  22. package/dist/scaffold/index.js.map +1 -1
  23. package/dist/styles.css +14 -0
  24. package/dist/ui/adapters.d.ts +5 -2
  25. package/dist/ui/agent-chat-composer.d.ts +8 -2
  26. package/dist/ui/agent-chat-queue.d.ts +0 -2
  27. package/dist/ui/index.cjs +817 -623
  28. package/dist/ui/index.cjs.map +1 -1
  29. package/dist/ui/index.d.ts +1 -0
  30. package/dist/ui/index.js +776 -622
  31. package/dist/ui/index.js.map +1 -1
  32. package/dist/ui/tool-sessions.d.ts +10 -2
  33. package/dist/ui/use-agent-chat-composer.d.ts +47 -0
  34. package/dist/ui/use-audio-recorder.d.ts +5 -3
  35. package/package.json +11 -6
  36. package/scaffold/host-styles.css +3 -0
  37. package/scaffold/inherit.css +4 -0
  38. package/scaffold/manifest.json +2 -2
  39. package/scaffold/src/app.tsx +5 -0
  40. package/scaffold/src/chat.css +35 -1
  41. package/scaffold/src/components/chat/chat-composer.tsx +31 -97
  42. package/scaffold/src/components/chat/chat-shell.tsx +7 -5
  43. package/scaffold/src/components/chat/external-link-dialog.tsx +6 -5
  44. package/scaffold/src/components/chat/identity-control.tsx +0 -1
  45. package/scaffold/src/components/chat/subagent-tasks.tsx +109 -0
  46. package/scaffold/src/components/chat/theme-menu.tsx +3 -1
  47. package/scaffold/src/components/theme-provider.tsx +28 -23
  48. package/scaffold/src/components/ui/tooltip.tsx +3 -0
  49. package/scaffold/src/host.css +11 -0
  50. package/scaffold/src/index.css +1 -213
  51. package/scaffold/src/index.tsx +9 -2
  52. package/scaffold/src/theme.css +215 -0
package/dist/ui/index.cjs CHANGED
@@ -106,6 +106,7 @@ __export(ui_exports, {
106
106
  resolvedHitlLabel: () => resolvedHitlLabel,
107
107
  revokeAgentChatComposerFiles: () => revokeAgentChatComposerFiles,
108
108
  shouldCollapseUserMessage: () => shouldCollapseUserMessage,
109
+ useAgentChatComposer: () => useAgentChatComposer,
109
110
  useAudioRecorder: () => useAudioRecorder,
110
111
  useChatComposerFocus: () => useChatComposerFocus,
111
112
  useCompletionActionEntrance: () => useCompletionActionEntrance,
@@ -114,7 +115,7 @@ __export(ui_exports, {
114
115
  module.exports = __toCommonJS(ui_exports);
115
116
 
116
117
  // src/ui/agent-chat-composer.tsx
117
- var React3 = __toESM(require("react"), 1);
118
+ var React6 = __toESM(require("react"), 1);
118
119
  var import_lucide_react2 = require("lucide-react");
119
120
 
120
121
  // src/ui/adapters.ts
@@ -589,6 +590,7 @@ function attachmentRenderKey(data, index, rowKind) {
589
590
  function ChatImageAttachment({
590
591
  className,
591
592
  data,
593
+ presentation = "message",
592
594
  onRemove,
593
595
  resolveContent,
594
596
  ...props
@@ -668,7 +670,7 @@ function ChatImageAttachment({
668
670
  Attachment,
669
671
  {
670
672
  ...props,
671
- className: cn("a24-chat-image-attachment", className),
673
+ className: cn("a24-chat-image-attachment", presentation === "composer" && "a24-composer-attachment-card", className),
672
674
  orientation: "vertical",
673
675
  ref: rootRef,
674
676
  state: status === "error" ? "error" : status === "done" ? "done" : "processing",
@@ -691,6 +693,7 @@ function ChatImageAttachment({
691
693
  status !== "done" && status !== "error" ? React.createElement("span", { className: "sr-only", role: "status" }, `Loading ${filename}`) : null,
692
694
  status === "error" ? React.createElement("span", { className: "sr-only" }, `${filename} preview unavailable`) : null
693
695
  ),
696
+ presentation === "composer" ? React.createElement("span", { className: "a24-composer-image-caption", title: filename }, filename) : null,
694
697
  onRemove ? React.createElement(
695
698
  "button",
696
699
  {
@@ -709,6 +712,7 @@ function ChatImageAttachment({
709
712
  function ChatAttachment({
710
713
  className,
711
714
  data,
715
+ presentation = "message",
712
716
  onRemove,
713
717
  orientation,
714
718
  resolveContent,
@@ -721,6 +725,7 @@ function ChatAttachment({
721
725
  ...props,
722
726
  className,
723
727
  data,
728
+ presentation,
724
729
  onRemove,
725
730
  resolveContent
726
731
  });
@@ -728,11 +733,16 @@ function ChatAttachment({
728
733
  return React.createElement(
729
734
  Attachment,
730
735
  {
731
- className: cn("a24-chat-attachment", className),
736
+ className: cn("a24-chat-attachment", presentation === "composer" && "a24-composer-attachment-card", className),
732
737
  orientation: orientation || "horizontal",
733
738
  size: "sm",
734
739
  ...props
735
740
  },
741
+ presentation === "composer" ? React.createElement(
742
+ "div",
743
+ { className: "a24-composer-file-preview", "aria-hidden": true },
744
+ React.createElement(import_file_icons.FileIcon, { size: 64, type: fileIconType, variant: "solid" })
745
+ ) : null,
736
746
  React.createElement(
737
747
  AttachmentMedia,
738
748
  Object.assign(
@@ -790,6 +800,7 @@ function ChatAttachments({
790
800
  }
791
801
  function ChatAttachmentRows({
792
802
  attachments,
803
+ layout = "message",
793
804
  className,
794
805
  dir,
795
806
  onRemove,
@@ -805,6 +816,7 @@ function ChatAttachmentRows({
805
816
  items.map(
806
817
  (attachment, index) => React.createElement(ChatAttachment, {
807
818
  data: attachment,
819
+ presentation: layout,
808
820
  dir: "ltr",
809
821
  key: attachmentRenderKey(attachment, index, kind),
810
822
  onRemove: onRemove ? () => onRemove(attachment) : void 0,
@@ -815,17 +827,65 @@ function ChatAttachmentRows({
815
827
  return React.createElement(
816
828
  "div",
817
829
  {
818
- className: cn("a24-chat-attachment-rows", className),
830
+ className: cn("a24-chat-attachment-rows", layout === "composer" && "a24-composer-attachment-row", className),
819
831
  dir,
820
832
  ...props
821
833
  },
822
- renderRow(imageAttachments, "images"),
823
- renderRow(fileAttachments, "files")
834
+ layout === "composer" ? renderRow(attachments, "mixed") : renderRow(imageAttachments, "images"),
835
+ layout === "message" ? renderRow(fileAttachments, "files") : null
824
836
  );
825
837
  }
826
838
 
827
- // src/ui/composer-attachments.ts
839
+ // src/ui/use-agent-chat-composer.ts
840
+ var React4 = __toESM(require("react"), 1);
841
+
842
+ // src/runtime/input-contract.ts
828
843
  var import_client = require("@agents24/client");
844
+ var import_client2 = require("@agents24/client");
845
+ var canonicalInputMimeType = import_client.normalizeAttachmentMimeType;
846
+ function uploadByteSize(data) {
847
+ if (data instanceof Uint8Array || data instanceof ArrayBuffer) return data.byteLength;
848
+ return data.size;
849
+ }
850
+ function inputMimeTypes(contract) {
851
+ if (!contract) return [];
852
+ return [
853
+ ...contract.modalities.files.allowed_mime_types,
854
+ ...contract.modalities.images.allowed_mime_types,
855
+ ...contract.modalities.audio.allowed_mime_types
856
+ ];
857
+ }
858
+ function validateAgentChatSubmission(contract, text, files) {
859
+ const { modalities, limits } = contract;
860
+ const normalizedText = text.trim();
861
+ if (normalizedText && !modalities.text.enabled) return "This agent does not accept text input.";
862
+ if (modalities.text.required && !normalizedText) return "Text input is required.";
863
+ if (files.length > limits.max_attachments_per_turn) {
864
+ return `You can attach up to ${limits.max_attachments_per_turn} items per message.`;
865
+ }
866
+ let totalBytes = 0;
867
+ const present = { files: false, images: false, audio: false };
868
+ for (const file of files) {
869
+ const mediaType = (0, import_client.normalizeAttachmentMimeType)(file.mediaType, file.name);
870
+ const modality = mediaType.startsWith("image/") ? "images" : mediaType.startsWith("audio/") ? "audio" : "files";
871
+ const rule = modalities[modality];
872
+ if (!rule.enabled || !rule.allowed_mime_types.includes(mediaType)) {
873
+ return `The attachment type ${mediaType || "unknown"} is not accepted.`;
874
+ }
875
+ const byteSize = file.byteSize ?? uploadByteSize(file.data);
876
+ if (byteSize > limits.max_file_bytes) return "An attachment exceeds the per-file size limit.";
877
+ totalBytes += byteSize;
878
+ present[modality] = true;
879
+ }
880
+ if (totalBytes > limits.max_total_bytes) return "Attachments exceed the total size limit.";
881
+ for (const modality of ["files", "images", "audio"]) {
882
+ if (modalities[modality].required && !present[modality]) return `${modality[0].toUpperCase()}${modality.slice(1)} input is required.`;
883
+ }
884
+ return null;
885
+ }
886
+
887
+ // src/ui/composer-attachments.ts
888
+ var import_client3 = require("@agents24/client");
829
889
  var createId = () => typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : `file-${Date.now()}-${Math.random().toString(36).slice(2)}`;
830
890
  function createAgentChatComposerFile(file) {
831
891
  return {
@@ -833,7 +893,7 @@ function createAgentChatComposerFile(file) {
833
893
  type: "file",
834
894
  url: URL.createObjectURL(file),
835
895
  filename: file.name || "attachment",
836
- mediaType: (0, import_client.normalizeAttachmentMimeType)(file.type, file.name),
896
+ mediaType: (0, import_client3.normalizeAttachmentMimeType)(file.type, file.name),
837
897
  source: file
838
898
  };
839
899
  }
@@ -885,8 +945,317 @@ function useChatComposerFocus({ textareaRef, disabled, focusKey, restoredDraft,
885
945
  return focus;
886
946
  }
887
947
 
888
- // src/ui/agent-chat-composer.tsx
948
+ // src/ui/use-audio-recorder.ts
949
+ var React3 = __toESM(require("react"), 1);
950
+ var RECORDING_CANDIDATES = ["audio/webm;codecs=opus", "audio/webm", "audio/mp4"];
951
+ function useAudioRecorder(onRecorded, options = {}) {
952
+ const recorderRef = React3.useRef(null);
953
+ const streamRef = React3.useRef(null);
954
+ const pendingRef = React3.useRef(false);
955
+ const generationRef = React3.useRef(0);
956
+ const onRecordedRef = React3.useRef(onRecorded);
957
+ const onStreamChangeRef = React3.useRef(options.onStreamChange);
958
+ React3.useEffect(() => {
959
+ onRecordedRef.current = onRecorded;
960
+ onStreamChangeRef.current = options.onStreamChange;
961
+ });
962
+ const [isRecording, setIsRecording] = React3.useState(false);
963
+ const [error, setError] = React3.useState(null);
964
+ const release = React3.useCallback(() => {
965
+ streamRef.current?.getTracks().forEach((track) => track.stop());
966
+ streamRef.current = null;
967
+ recorderRef.current = null;
968
+ onStreamChangeRef.current?.(null);
969
+ }, []);
970
+ const stop = React3.useCallback(() => {
971
+ const recorder = recorderRef.current;
972
+ if (recorder && recorder.state !== "inactive") recorder.stop();
973
+ }, []);
974
+ const start = React3.useCallback(async () => {
975
+ if (pendingRef.current || recorderRef.current) return;
976
+ setError(null);
977
+ if (!navigator.mediaDevices?.getUserMedia || typeof MediaRecorder === "undefined") {
978
+ setError("Audio recording is not supported in this browser.");
979
+ return;
980
+ }
981
+ pendingRef.current = true;
982
+ const generation = generationRef.current;
983
+ try {
984
+ const mimeType = RECORDING_CANDIDATES.find((candidate) => MediaRecorder.isTypeSupported(candidate));
985
+ if (!mimeType) {
986
+ setError("Audio recording is not supported in this browser.");
987
+ return;
988
+ }
989
+ const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
990
+ if (generation !== generationRef.current) {
991
+ stream.getTracks().forEach((track) => track.stop());
992
+ return;
993
+ }
994
+ streamRef.current = stream;
995
+ const recorder = new MediaRecorder(stream, { mimeType });
996
+ recorderRef.current = recorder;
997
+ const chunks = [];
998
+ recorder.ondataavailable = (event) => {
999
+ if (event.data.size) chunks.push(event.data);
1000
+ };
1001
+ recorder.onstop = () => {
1002
+ if (generation !== generationRef.current) return;
1003
+ const actualType = recorder.mimeType || mimeType;
1004
+ const extension = actualType.startsWith("audio/mp4") ? "m4a" : "webm";
1005
+ try {
1006
+ onRecordedRef.current(new File([new Blob(chunks, { type: actualType })], `recording-${Date.now()}.${extension}`, { type: actualType }));
1007
+ } finally {
1008
+ release();
1009
+ setIsRecording(false);
1010
+ }
1011
+ };
1012
+ recorder.onerror = () => {
1013
+ recorder.onstop = null;
1014
+ if (recorder.state !== "inactive") recorder.stop();
1015
+ release();
1016
+ setIsRecording(false);
1017
+ setError("Audio recording failed. Try again.");
1018
+ };
1019
+ recorder.start();
1020
+ setIsRecording(true);
1021
+ onStreamChangeRef.current?.(stream);
1022
+ } catch {
1023
+ if (generation === generationRef.current) {
1024
+ release();
1025
+ setIsRecording(false);
1026
+ setError("Microphone access is unavailable.");
1027
+ }
1028
+ } finally {
1029
+ if (generation === generationRef.current) pendingRef.current = false;
1030
+ }
1031
+ }, [release]);
1032
+ React3.useEffect(() => () => {
1033
+ generationRef.current += 1;
1034
+ pendingRef.current = false;
1035
+ const recorder = recorderRef.current;
1036
+ if (recorder) {
1037
+ recorder.onstop = null;
1038
+ recorder.onerror = null;
1039
+ if (recorder.state !== "inactive") recorder.stop();
1040
+ }
1041
+ release();
1042
+ }, [release]);
1043
+ return { error, isRecording, start, stop };
1044
+ }
1045
+
1046
+ // src/ui/use-agent-chat-composer.ts
1047
+ function useAgentChatComposer(options) {
1048
+ const [text, setTextState] = React4.useState("");
1049
+ const [files, setFiles] = React4.useState([]);
1050
+ const [attachmentError, setAttachmentError] = React4.useState(null);
1051
+ const [isSubmitting, setIsSubmitting] = React4.useState(false);
1052
+ const localTextareaRef = React4.useRef(null);
1053
+ const textareaRef = options.textareaRef ?? localTextareaRef;
1054
+ const filesRef = React4.useRef(files);
1055
+ const inFlightFilesRef = React4.useRef([]);
1056
+ const revisionRef = React4.useRef(0);
1057
+ const submittingRef = React4.useRef(false);
1058
+ React4.useEffect(() => {
1059
+ filesRef.current = files;
1060
+ }, [files]);
1061
+ React4.useEffect(() => () => {
1062
+ revokeAgentChatComposerFiles(filesRef.current);
1063
+ revokeAgentChatComposerFiles(inFlightFilesRef.current);
1064
+ }, []);
1065
+ const focusComposer = useChatComposerFocus({
1066
+ textareaRef,
1067
+ disabled: Boolean(options.disabled),
1068
+ focusKey: options.focusKey,
1069
+ restoredDraft: options.restoredDraft,
1070
+ onRestore: (draft) => {
1071
+ revisionRef.current += 1;
1072
+ revokeAgentChatComposerFiles(filesRef.current);
1073
+ setTextState(draft.text);
1074
+ setFiles(draft.files);
1075
+ }
1076
+ });
1077
+ const attachmentMimeTypes = inputMimeTypes(options.inputContract);
1078
+ const setText = (value) => {
1079
+ revisionRef.current += 1;
1080
+ setTextState(value);
1081
+ };
1082
+ const addFiles = (selected) => {
1083
+ const accepted = selected.filter((file) => attachmentMimeTypes.includes(canonicalInputMimeType(file.type, file.name)));
1084
+ revisionRef.current += 1;
1085
+ const additions = accepted.map(createAgentChatComposerFile);
1086
+ setFiles((current) => [...current, ...additions]);
1087
+ setAttachmentError(accepted.length === selected.length ? null : "One or more selected file types are not supported by this Agent.");
1088
+ };
1089
+ const removeFile = (id) => {
1090
+ revisionRef.current += 1;
1091
+ const removed = filesRef.current.find((file) => file.id === id);
1092
+ if (removed) revokeAgentChatComposerFiles([removed]);
1093
+ setFiles((current) => current.filter((file) => file.id !== id));
1094
+ };
1095
+ const recorder = useAudioRecorder((file) => addFiles([file]), { onStreamChange: options.onRecordingStreamChange });
1096
+ const validationError = options.inputContract ? validateAgentChatSubmission(
1097
+ options.inputContract,
1098
+ text,
1099
+ files.map((file) => ({ data: file.source || new Uint8Array(), mediaType: file.mediaType, name: file.filename }))
1100
+ ) : null;
1101
+ const canSubmit = Boolean(text.trim() || files.length) && !options.disabled && !options.isCancelling && !isSubmitting && !recorder.isRecording && !validationError;
1102
+ const submit = async () => {
1103
+ if (!canSubmit || submittingRef.current) return;
1104
+ const value = text;
1105
+ const selectedFiles = files;
1106
+ const clearedRevision = ++revisionRef.current;
1107
+ submittingRef.current = true;
1108
+ setIsSubmitting(true);
1109
+ inFlightFilesRef.current = selectedFiles;
1110
+ setTextState("");
1111
+ setFiles([]);
1112
+ focusComposer();
1113
+ try {
1114
+ await options.onSubmit(value, selectedFiles);
1115
+ revokeAgentChatComposerFiles(selectedFiles);
1116
+ } catch {
1117
+ if (revisionRef.current === clearedRevision) {
1118
+ revisionRef.current += 1;
1119
+ setTextState(value);
1120
+ setFiles(selectedFiles);
1121
+ } else revokeAgentChatComposerFiles(selectedFiles);
1122
+ } finally {
1123
+ inFlightFilesRef.current = [];
1124
+ submittingRef.current = false;
1125
+ setIsSubmitting(false);
1126
+ }
1127
+ };
1128
+ return {
1129
+ text,
1130
+ setText,
1131
+ files,
1132
+ addFiles,
1133
+ removeFile,
1134
+ textareaRef,
1135
+ submit,
1136
+ canSubmit,
1137
+ isSubmitting,
1138
+ attachmentError,
1139
+ attachmentMimeTypes,
1140
+ validationError,
1141
+ recorder,
1142
+ recordingAllowed: options.recordingEnabled ?? options.inputContract?.modalities.audio.recording_enabled ?? false,
1143
+ fileAccept: (0, import_client2.attachmentFileAccept)(attachmentMimeTypes),
1144
+ onKeyDown: (event) => {
1145
+ if (event.key === "Enter" && !event.shiftKey && !event.nativeEvent.isComposing) {
1146
+ event.preventDefault();
1147
+ void submit();
1148
+ }
1149
+ },
1150
+ allowAttachments: attachmentMimeTypes.length > 0,
1151
+ isExpanded: Boolean(options.forceExpanded || text.includes("\n") || text.length > 62),
1152
+ action: chatComposerAction({ hasDraft: Boolean(text.trim() || files.length), isRunning: Boolean(options.isRunning), isCancelling: Boolean(options.isCancelling) })
1153
+ };
1154
+ }
1155
+
1156
+ // src/ui/agent-chat-context-status.tsx
1157
+ var React5 = __toESM(require("react"), 1);
889
1158
  var import_jsx_runtime3 = require("react/jsx-runtime");
1159
+ var COMPACT_TOKENS = new Intl.NumberFormat("en-US", { notation: "compact" });
1160
+ var PERCENT = new Intl.NumberFormat("en-US", {
1161
+ style: "percent",
1162
+ maximumFractionDigits: 1
1163
+ });
1164
+ var EXACT_SOURCES = /* @__PURE__ */ new Set(["exact", "provider_usage"]);
1165
+ function contextStatusMetrics(contextWindow) {
1166
+ const maximum = Number(contextWindow?.max_tokens || 0);
1167
+ const input = Math.max(0, Number(contextWindow?.input_tokens || 0));
1168
+ const admissionInput = Math.max(0, Number(contextWindow?.admission_input_tokens ?? input));
1169
+ const usageRatio = Math.max(0, Math.min(1, Number(
1170
+ contextWindow?.usage_ratio ?? input / maximum
1171
+ )));
1172
+ const admissionRatio = Math.max(0, Math.min(1, Number(
1173
+ contextWindow?.admission_ratio ?? admissionInput / maximum
1174
+ )));
1175
+ return { maximum, input, admissionInput, usageRatio, admissionRatio };
1176
+ }
1177
+ function AgentChatContextStatus({
1178
+ className,
1179
+ contextWindow
1180
+ }) {
1181
+ const tooltipId = React5.useId();
1182
+ const [open, setOpen] = React5.useState(false);
1183
+ const { maximum, input, admissionInput, usageRatio, admissionRatio } = contextStatusMetrics(contextWindow);
1184
+ const percentage = Math.round(usageRatio * 100);
1185
+ const exact = EXACT_SOURCES.has(contextWindow?.source || "unknown");
1186
+ if (!Number.isFinite(maximum) || maximum <= 0) return null;
1187
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1188
+ "div",
1189
+ {
1190
+ className: cn("a24-context-status", className),
1191
+ onBlurCapture: (event) => {
1192
+ if (!event.currentTarget.contains(event.relatedTarget)) setOpen(false);
1193
+ },
1194
+ onFocusCapture: () => setOpen(true),
1195
+ onMouseEnter: () => setOpen(true),
1196
+ onMouseLeave: () => setOpen(false),
1197
+ children: [
1198
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1199
+ "button",
1200
+ {
1201
+ "aria-describedby": open ? tooltipId : void 0,
1202
+ "aria-label": `Context window ${percentage}% used`,
1203
+ className: "a24-context-status__trigger",
1204
+ onKeyDown: (event) => {
1205
+ if (event.key === "Escape") setOpen(false);
1206
+ },
1207
+ type: "button",
1208
+ children: [
1209
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { "aria-hidden": "true", className: "a24-context-status__ring", viewBox: "0 0 24 24", children: [
1210
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { className: "a24-context-status__track", cx: "12", cy: "12", r: "10" }),
1211
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1212
+ "circle",
1213
+ {
1214
+ className: "a24-context-status__value",
1215
+ cx: "12",
1216
+ cy: "12",
1217
+ pathLength: "100",
1218
+ r: "10",
1219
+ strokeDasharray: `${percentage} 100`
1220
+ }
1221
+ )
1222
+ ] }),
1223
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { children: [
1224
+ percentage,
1225
+ "%"
1226
+ ] })
1227
+ ]
1228
+ }
1229
+ ),
1230
+ open ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "a24-context-status__popover", id: tooltipId, role: "tooltip", children: [
1231
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "a24-context-status__sr-only", children: "Context window" }),
1232
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("strong", { children: [
1233
+ PERCENT.format(usageRatio),
1234
+ " context used"
1235
+ ] }),
1236
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { children: [
1237
+ exact ? "Input" : "Estimated input",
1238
+ ": ",
1239
+ COMPACT_TOKENS.format(input),
1240
+ " / ",
1241
+ COMPACT_TOKENS.format(maximum),
1242
+ " tokens"
1243
+ ] }),
1244
+ admissionInput !== input ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { children: [
1245
+ "Safety bound: ",
1246
+ COMPACT_TOKENS.format(admissionInput),
1247
+ " tokens (",
1248
+ PERCENT.format(admissionRatio),
1249
+ ")"
1250
+ ] }) : null
1251
+ ] }) : null
1252
+ ]
1253
+ }
1254
+ );
1255
+ }
1256
+
1257
+ // src/ui/agent-chat-composer.tsx
1258
+ var import_jsx_runtime4 = require("react/jsx-runtime");
890
1259
  function formatFileCount(files) {
891
1260
  if (files.length === 1) return files[0].filename;
892
1261
  return `${files.length} files`;
@@ -894,9 +1263,11 @@ function formatFileCount(files) {
894
1263
  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";
895
1264
  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";
896
1265
  function AgentChatComposer({
1266
+ inputContract,
1267
+ recordingEnabled,
1268
+ contextWindow,
897
1269
  accept,
898
1270
  allowAttachments = true,
899
- attachmentLayout = "flow",
900
1271
  className,
901
1272
  disabled = false,
902
1273
  forceExpanded = false,
@@ -911,16 +1282,22 @@ function AgentChatComposer({
911
1282
  restoredDraft,
912
1283
  textareaRef
913
1284
  }) {
914
- const fileInputRef = React3.useRef(null);
915
- const filesRef = React3.useRef([]);
916
- const inFlightFilesRef = React3.useRef([]);
917
- const localTextareaRef = React3.useRef(null);
918
- const draftRevisionRef = React3.useRef(0);
919
- const [text, setText] = React3.useState("");
920
- const [files, setFiles] = React3.useState([]);
921
- const [isSubmitting, setIsSubmitting] = React3.useState(false);
1285
+ const fileInputRef = React6.useRef(null);
1286
+ const localTextareaRef = React6.useRef(null);
1287
+ const composer = useAgentChatComposer({
1288
+ inputContract,
1289
+ recordingEnabled,
1290
+ disabled,
1291
+ isRunning,
1292
+ isCancelling,
1293
+ focusKey,
1294
+ restoredDraft,
1295
+ textareaRef: localTextareaRef,
1296
+ onSubmit: (text2, files2) => onSubmit({ text: text2.trim(), files: files2, attachmentIds: files2.flatMap((file) => file.id ? [file.id] : []) })
1297
+ });
1298
+ const { text, files, setText, isSubmitting, canSubmit, action, submit } = composer;
922
1299
  const isExpanded = forceExpanded || text.includes("\n") || text.length > 62;
923
- const setTextareaRef = React3.useCallback(
1300
+ const setTextareaRef = React6.useCallback(
924
1301
  (node) => {
925
1302
  localTextareaRef.current = node;
926
1303
  if (typeof textareaRef === "function") {
@@ -931,27 +1308,10 @@ function AgentChatComposer({
931
1308
  },
932
1309
  [textareaRef]
933
1310
  );
934
- React3.useEffect(() => {
935
- filesRef.current = files;
1311
+ React6.useEffect(() => {
936
1312
  onAttachmentCountChange?.(files.length);
937
1313
  }, [files, onAttachmentCountChange]);
938
- React3.useEffect(() => () => {
939
- revokeAgentChatComposerFiles(filesRef.current);
940
- revokeAgentChatComposerFiles(inFlightFilesRef.current);
941
- }, []);
942
- const focusComposer = useChatComposerFocus({
943
- textareaRef: localTextareaRef,
944
- disabled,
945
- focusKey,
946
- restoredDraft,
947
- onRestore: (draft) => {
948
- draftRevisionRef.current += 1;
949
- revokeAgentChatComposerFiles(filesRef.current);
950
- setText(draft.text);
951
- setFiles(draft.files);
952
- }
953
- });
954
- React3.useLayoutEffect(() => {
1314
+ React6.useLayoutEffect(() => {
955
1315
  const textarea = localTextareaRef.current;
956
1316
  if (!textarea) return;
957
1317
  if (isExpanded) {
@@ -964,83 +1324,13 @@ function AgentChatComposer({
964
1324
  textarea.style.height = "";
965
1325
  textarea.style.overflowY = "hidden";
966
1326
  }, [isExpanded, text]);
967
- const canSubmit = !disabled && !isCancelling && !isSubmitting && (text.trim().length > 0 || files.length > 0);
968
- const action = chatComposerAction({ hasDraft: text.trim().length > 0 || files.length > 0, isRunning, isCancelling });
969
1327
  const handleFilesChange = (event) => {
970
- const selected = Array.from(event.target.files || []).map(createAgentChatComposerFile);
971
- if (selected.length > 0) {
972
- draftRevisionRef.current += 1;
973
- setFiles((current) => [...current, ...selected]);
974
- }
1328
+ composer.addFiles(Array.from(event.target.files || []));
975
1329
  event.currentTarget.value = "";
976
1330
  };
977
- const removeFile = (fileId) => {
978
- draftRevisionRef.current += 1;
979
- setFiles((current) => {
980
- const target = current.find((file) => file.id === fileId);
981
- if (target) revokeAgentChatComposerFiles([target]);
982
- return current.filter((file) => file.id !== fileId);
983
- });
984
- };
985
- const submit = async () => {
986
- if (!canSubmit) return;
987
- const submittedText = text.trim();
988
- const submittedFiles = files;
989
- const clearedRevision = draftRevisionRef.current + 1;
990
- draftRevisionRef.current = clearedRevision;
991
- inFlightFilesRef.current = submittedFiles;
992
- setIsSubmitting(true);
993
- setText("");
994
- setFiles([]);
995
- focusComposer();
996
- try {
997
- await onSubmit({
998
- text: submittedText,
999
- files: submittedFiles,
1000
- attachmentIds: submittedFiles.flatMap((file) => file.persistedAttachmentId ? [file.persistedAttachmentId] : [])
1001
- });
1002
- revokeAgentChatComposerFiles(submittedFiles);
1003
- inFlightFilesRef.current = [];
1004
- } catch {
1005
- if (draftRevisionRef.current === clearedRevision) {
1006
- draftRevisionRef.current += 1;
1007
- setText(submittedText);
1008
- setFiles(submittedFiles);
1009
- inFlightFilesRef.current = [];
1010
- } else {
1011
- revokeAgentChatComposerFiles(submittedFiles);
1012
- inFlightFilesRef.current = [];
1013
- }
1014
- } finally {
1015
- setIsSubmitting(false);
1016
- }
1017
- };
1018
- const handleKeyDown = (event) => {
1019
- if (event.key === "Enter" && !event.shiftKey && !event.nativeEvent.isComposing) {
1020
- event.preventDefault();
1021
- void submit();
1022
- }
1023
- };
1024
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: cn("a24-composer relative mx-auto w-full max-w-3xl overflow-visible", className), "data-agents24-chat-composer": "", children: [
1025
- files.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1026
- "div",
1027
- {
1028
- "data-testid": "agent-chat-composer-attachments",
1029
- "data-layout": attachmentLayout,
1030
- className: cn(
1031
- "a24-composer__attachments min-w-0 py-1",
1032
- 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"
1033
- ),
1034
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1035
- ChatAttachmentRows,
1036
- {
1037
- attachments: files,
1038
- onRemove: (attachment) => attachment.id && removeFile(attachment.id)
1039
- }
1040
- )
1041
- }
1042
- ) : null,
1043
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1331
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: cn("a24-composer relative mx-auto w-full max-w-3xl overflow-visible", className), "data-agents24-chat-composer": "", children: [
1332
+ composer.attachmentError || composer.recorder.error ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { role: "alert", children: composer.attachmentError || composer.recorder.error }) : null,
1333
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1044
1334
  "div",
1045
1335
  {
1046
1336
  "data-expanded": isExpanded ? "" : void 0,
@@ -1048,101 +1338,142 @@ function AgentChatComposer({
1048
1338
  "a24-composer__surface relative w-full border border-input bg-background shadow-sm",
1049
1339
  isExpanded ? "rounded-xl p-2 pb-1.5" : "rounded-xl px-2 py-[3px]"
1050
1340
  ),
1051
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1052
- "div",
1053
- {
1054
- className: "a24-composer__grid w-full",
1055
- style: {
1056
- alignItems: "center",
1057
- display: "grid",
1058
- gridTemplateAreas: isExpanded ? `"textarea textarea textarea" "plus toolbar submit"` : `"plus textarea composer mic submit"`,
1059
- gridTemplateColumns: isExpanded ? "auto 1fr auto" : "auto 1fr auto auto auto",
1060
- gap: isExpanded ? "3px 10px" : "8px"
1061
- },
1062
- children: [
1063
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { gridArea: "plus" }, className: "a24-composer__attach-slot flex shrink-0 items-center justify-center", children: [
1064
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1065
- "input",
1341
+ children: [
1342
+ files.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1343
+ ChatAttachmentRows,
1344
+ {
1345
+ "data-testid": "agent-chat-composer-attachments",
1346
+ layout: "composer",
1347
+ attachments: files,
1348
+ onRemove: (attachment) => attachment.id && composer.removeFile(attachment.id)
1349
+ }
1350
+ ) : null,
1351
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1352
+ "div",
1353
+ {
1354
+ className: "a24-composer__grid w-full",
1355
+ style: {
1356
+ alignItems: "center",
1357
+ display: "grid",
1358
+ gridTemplateAreas: isExpanded ? `"textarea textarea textarea" "plus toolbar submit"` : `"plus textarea composer mic submit"`,
1359
+ gridTemplateColumns: isExpanded ? "auto 1fr auto" : "auto 1fr auto auto auto",
1360
+ gap: isExpanded ? "3px 10px" : "8px"
1361
+ },
1362
+ children: [
1363
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { gridArea: "plus" }, className: "a24-composer__attach-slot flex shrink-0 items-center justify-center", children: [
1364
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1365
+ "input",
1366
+ {
1367
+ ref: fileInputRef,
1368
+ accept: composer.fileAccept ?? accept,
1369
+ "aria-label": "Attach files",
1370
+ className: "hidden",
1371
+ multiple: true,
1372
+ name: "attachments",
1373
+ onChange: handleFilesChange,
1374
+ type: "file"
1375
+ }
1376
+ ),
1377
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1378
+ "button",
1379
+ {
1380
+ "aria-label": "Attach files",
1381
+ className: cn("a24-composer__attach", pillButtonClass),
1382
+ disabled: disabled || isSubmitting || !allowAttachments || !composer.allowAttachments,
1383
+ onClick: () => fileInputRef.current?.click(),
1384
+ title: "Attach files",
1385
+ type: "button",
1386
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react2.Plus, { className: "size-4" })
1387
+ }
1388
+ )
1389
+ ] }),
1390
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { gridArea: "textarea" }, className: "a24-composer__textarea-slot min-w-0 w-full", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1391
+ "textarea",
1066
1392
  {
1067
- ref: fileInputRef,
1068
- accept,
1069
- "aria-label": "Attach files",
1070
- className: "hidden",
1071
- multiple: true,
1072
- name: "attachments",
1073
- onChange: handleFilesChange,
1074
- type: "file"
1393
+ ref: setTextareaRef,
1394
+ "aria-label": "Message",
1395
+ autoComplete: "off",
1396
+ className: cn(
1397
+ "a24-composer__textarea w-full resize-none border-0 bg-transparent px-1 text-[15px] leading-relaxed text-foreground shadow-none outline-none placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-0",
1398
+ isExpanded ? "min-h-9 max-h-[224px] py-1" : "min-h-9 h-9 max-h-9 py-2"
1399
+ ),
1400
+ disabled,
1401
+ name: "message",
1402
+ onChange: (event) => setText(event.target.value),
1403
+ onKeyDown: composer.onKeyDown,
1404
+ placeholder,
1405
+ rows: isExpanded ? 2 : 1,
1406
+ value: text
1075
1407
  }
1076
- ),
1077
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1408
+ ) }),
1409
+ isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { gridArea: "toolbar" }, className: "a24-composer__toolbar flex min-w-0 items-center gap-1.5", children: [
1410
+ inputToolbarContent,
1411
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(AgentChatContextStatus, { contextWindow }),
1412
+ composer.recordingAllowed ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1413
+ "button",
1414
+ {
1415
+ type: "button",
1416
+ className: pillButtonClass,
1417
+ "aria-label": composer.recorder.isRecording ? "Stop recording" : "Record audio",
1418
+ disabled: disabled || isRunning || isSubmitting,
1419
+ onClick: () => {
1420
+ if (composer.recorder.isRecording) composer.recorder.stop();
1421
+ else void composer.recorder.start();
1422
+ },
1423
+ children: composer.recorder.isRecording ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react2.Square, {}) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react2.Mic, {})
1424
+ }
1425
+ ) : null
1426
+ ] }) : null,
1427
+ !isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { gridArea: "composer" }, className: "flex min-w-0 items-center gap-1.5" }) : null,
1428
+ !isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { gridArea: "mic" }, className: "flex shrink-0 items-center justify-end", children: [
1429
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(AgentChatContextStatus, { contextWindow }),
1430
+ composer.recordingAllowed ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1431
+ "button",
1432
+ {
1433
+ type: "button",
1434
+ className: pillButtonClass,
1435
+ "aria-label": composer.recorder.isRecording ? "Stop recording" : "Record audio",
1436
+ disabled: disabled || isRunning || isSubmitting,
1437
+ onClick: () => {
1438
+ if (composer.recorder.isRecording) composer.recorder.stop();
1439
+ else void composer.recorder.start();
1440
+ },
1441
+ children: composer.recorder.isRecording ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react2.Square, {}) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react2.Mic, {})
1442
+ }
1443
+ ) : null
1444
+ ] }) : null,
1445
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { gridArea: "submit" }, className: "a24-composer__submit-slot flex shrink-0 items-center justify-end gap-1", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1078
1446
  "button",
1079
1447
  {
1080
- "aria-label": "Attach files",
1081
- className: cn("a24-composer__attach", pillButtonClass),
1082
- disabled: disabled || isSubmitting || !allowAttachments,
1083
- onClick: () => fileInputRef.current?.click(),
1084
- title: "Attach files",
1448
+ "aria-label": isCancelling ? "Cancelling response" : action === "stop" ? "Stop generating" : files.length > 0 ? `Send ${formatFileCount(files)}` : isRunning ? "Queue message" : "Send message",
1449
+ className: cn("a24-composer__submit", submitButtonClass, text.trim().length > 0 && "hover:scale-105"),
1450
+ disabled: isCancelling || (action === "stop" ? disabled : !canSubmit),
1451
+ onClick: () => {
1452
+ if (action === "stop") onStop?.();
1453
+ else void submit();
1454
+ },
1455
+ title: composer.validationError || (action === "stop" ? "Stop" : isRunning ? "Queue message" : "Send"),
1085
1456
  type: "button",
1086
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react2.Plus, { className: "size-4" })
1457
+ children: isCancelling ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react2.Loader2, { className: "size-3.5 animate-spin" }) : action === "stop" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react2.Square, { className: "size-3.5 fill-current" }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react2.ArrowUp, { className: "size-4", strokeWidth: 2.5 })
1087
1458
  }
1088
- )
1089
- ] }),
1090
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { gridArea: "textarea" }, className: "a24-composer__textarea-slot min-w-0 w-full", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1091
- "textarea",
1092
- {
1093
- ref: setTextareaRef,
1094
- "aria-label": "Message",
1095
- autoComplete: "off",
1096
- className: cn(
1097
- "a24-composer__textarea w-full resize-none border-0 bg-transparent px-1 text-[15px] leading-relaxed text-foreground shadow-none outline-none placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-0",
1098
- isExpanded ? "min-h-9 max-h-[224px] py-1" : "min-h-9 h-9 max-h-9 py-2"
1099
- ),
1100
- disabled,
1101
- name: "message",
1102
- onChange: (event) => {
1103
- draftRevisionRef.current += 1;
1104
- setText(event.target.value);
1105
- },
1106
- onKeyDown: handleKeyDown,
1107
- placeholder,
1108
- rows: isExpanded ? 2 : 1,
1109
- value: text
1110
- }
1111
- ) }),
1112
- isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { gridArea: "toolbar" }, className: "a24-composer__toolbar flex min-w-0 items-center gap-1.5", children: inputToolbarContent }) : null,
1113
- !isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { gridArea: "composer" }, className: "flex min-w-0 items-center gap-1.5" }) : null,
1114
- !isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { gridArea: "mic" }, className: "flex shrink-0 items-center justify-end" }) : null,
1115
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { gridArea: "submit" }, className: "a24-composer__submit-slot flex shrink-0 items-center justify-end gap-1", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1116
- "button",
1117
- {
1118
- "aria-label": isCancelling ? "Cancelling response" : action === "stop" ? "Stop generating" : files.length > 0 ? `Send ${formatFileCount(files)}` : isRunning ? "Queue message" : "Send message",
1119
- className: cn("a24-composer__submit", submitButtonClass, text.trim().length > 0 && "hover:scale-105"),
1120
- disabled: isCancelling || (action === "stop" ? disabled : !canSubmit),
1121
- onClick: () => {
1122
- if (action === "stop") onStop?.();
1123
- else void submit();
1124
- },
1125
- title: action === "stop" ? "Stop" : isRunning ? "Queue message" : "Send",
1126
- type: "button",
1127
- children: isCancelling ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react2.Loader2, { className: "size-3.5 animate-spin" }) : action === "stop" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react2.Square, { className: "size-3.5 fill-current" }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react2.ArrowUp, { className: "size-4", strokeWidth: 2.5 })
1128
- }
1129
- ) })
1130
- ]
1131
- }
1132
- )
1459
+ ) })
1460
+ ]
1461
+ }
1462
+ )
1463
+ ]
1133
1464
  }
1134
1465
  ),
1135
- !isExpanded && inputToolbarContent ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "a24-composer__toolbar a24-composer__toolbar--external mt-2 flex items-center gap-2 px-1", children: inputToolbarContent }) : null
1466
+ !isExpanded && inputToolbarContent ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "a24-composer__toolbar a24-composer__toolbar--external mt-2 flex items-center gap-2 px-1", children: inputToolbarContent }) : null
1136
1467
  ] });
1137
1468
  }
1138
1469
 
1139
1470
  // src/ui/agent-chat-queue.tsx
1140
- var React4 = __toESM(require("react"), 1);
1471
+ var React7 = __toESM(require("react"), 1);
1141
1472
  var import_lucide_react3 = require("lucide-react");
1142
- var import_jsx_runtime4 = require("react/jsx-runtime");
1473
+ var import_jsx_runtime5 = require("react/jsx-runtime");
1143
1474
  function QueueRow({ message, onRemove, onEdit }) {
1144
- const [busy, setBusy] = React4.useState(false);
1145
- const [error, setError] = React4.useState(null);
1475
+ const [busy, setBusy] = React7.useState(false);
1476
+ const [error, setError] = React7.useState(null);
1146
1477
  const editable = message.revision > 0 && (message.status === "queued" || message.status === "blocked");
1147
1478
  const act = async (operation) => {
1148
1479
  setBusy(true);
@@ -1155,14 +1486,14 @@ function QueueRow({ message, onRemove, onEdit }) {
1155
1486
  setBusy(false);
1156
1487
  }
1157
1488
  };
1158
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { "data-message-id": message.id, className: "a24-queue__row min-w-0 border-b border-border/40 last:border-b-0", children: [
1159
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "a24-queue__row-content flex min-h-9 items-center gap-2 px-3 text-sm", children: [
1160
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react3.CornerDownLeft, { "aria-hidden": true, className: "size-4 shrink-0 text-muted-foreground" }),
1161
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("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"}` }),
1162
- message.status === "blocked" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "shrink-0 text-xs text-amber-700", children: "Needs attention" }) : null,
1163
- ["steer_pending", "delivering"].includes(message.status) ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "shrink-0 text-xs text-muted-foreground", children: "Steering\u2026" }) : null,
1164
- editable ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
1165
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1489
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { "data-message-id": message.id, className: "a24-queue__row min-w-0 border-b border-border/40 last:border-b-0", children: [
1490
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "a24-queue__row-content flex min-h-9 items-center gap-2 px-3 text-sm", children: [
1491
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react3.CornerDownLeft, { "aria-hidden": true, className: "size-4 shrink-0 text-muted-foreground" }),
1492
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("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"}` }),
1493
+ message.status === "blocked" ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "shrink-0 text-xs text-amber-700", children: "Needs attention" }) : null,
1494
+ ["steer_pending", "delivering"].includes(message.status) ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "shrink-0 text-xs text-muted-foreground", children: "Sending\u2026" }) : null,
1495
+ editable ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
1496
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1166
1497
  "button",
1167
1498
  {
1168
1499
  type: "button",
@@ -1170,12 +1501,12 @@ function QueueRow({ message, onRemove, onEdit }) {
1170
1501
  disabled: busy,
1171
1502
  onClick: () => void act(() => onRemove(message)),
1172
1503
  className: "a24-queue__icon flex size-8 shrink-0 items-center justify-center text-muted-foreground hover:text-foreground disabled:opacity-50",
1173
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react3.Trash2, { "aria-hidden": true, className: "size-4" })
1504
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react3.Trash2, { "aria-hidden": true, className: "size-4" })
1174
1505
  }
1175
1506
  ),
1176
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("details", { className: "a24-queue__menu relative group/queue-menu", children: [
1177
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("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__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react3.Ellipsis, { "aria-hidden": true, className: "size-4" }) }),
1178
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "a24-queue__menu-content mt-1 min-w-28 rounded-md border bg-background p-1", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1507
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("details", { className: "a24-queue__menu relative group/queue-menu", children: [
1508
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("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__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react3.Ellipsis, { "aria-hidden": true, className: "size-4" }) }),
1509
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "a24-queue__menu-content mt-1 min-w-28 rounded-md border bg-background p-1", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1179
1510
  "button",
1180
1511
  {
1181
1512
  type: "button",
@@ -1188,29 +1519,29 @@ function QueueRow({ message, onRemove, onEdit }) {
1188
1519
  ] })
1189
1520
  ] }) : null
1190
1521
  ] }),
1191
- error ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { role: "alert", className: "px-9 pb-2 text-xs text-destructive", children: error }) : null
1522
+ error ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { role: "alert", className: "px-9 pb-2 text-xs text-destructive", children: error }) : null
1192
1523
  ] });
1193
1524
  }
1194
1525
  function AgentChatQueue({ pending, queue, onRemove, onEdit, onResume, className }) {
1195
- const [resumeError, setResumeError] = React4.useState(null);
1526
+ const [resumeError, setResumeError] = React7.useState(null);
1196
1527
  if (!pending.length && !queue?.paused) return null;
1197
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("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: [
1198
- queue?.paused ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex min-h-9 items-center justify-between gap-2 px-3 text-xs text-muted-foreground", children: [
1199
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "Queue paused" }),
1200
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", onClick: () => {
1528
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("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: [
1529
+ queue?.paused ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex min-h-9 items-center justify-between gap-2 px-3 text-xs text-muted-foreground", children: [
1530
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Queue paused" }),
1531
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", onClick: () => {
1201
1532
  void onResume().then(() => setResumeError(null)).catch((cause) => setResumeError(cause instanceof Error ? cause.message : "Could not resume queue"));
1202
1533
  }, className: "font-medium text-foreground hover:underline", children: "Resume queue" })
1203
1534
  ] }) : null,
1204
- resumeError ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { role: "alert", className: "px-3 pb-2 text-xs text-destructive", children: resumeError }) : null,
1205
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "max-h-40 overflow-y-auto", children: pending.map((message) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(QueueRow, { message, onRemove, onEdit }, message.client_submission_id || message.id)) })
1535
+ resumeError ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { role: "alert", className: "px-3 pb-2 text-xs text-destructive", children: resumeError }) : null,
1536
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "max-h-40 overflow-y-auto", children: pending.map((message) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(QueueRow, { message, onRemove, onEdit }, message.client_submission_id || message.id)) })
1206
1537
  ] });
1207
1538
  }
1208
1539
 
1209
1540
  // src/ui/agent-chat-actions.tsx
1210
- var React5 = __toESM(require("react"), 1);
1541
+ var React8 = __toESM(require("react"), 1);
1211
1542
  var import_lucide_react4 = require("lucide-react");
1212
1543
  var import_radix_ui = require("radix-ui");
1213
- var import_jsx_runtime5 = require("react/jsx-runtime");
1544
+ var import_jsx_runtime6 = require("react/jsx-runtime");
1214
1545
  var FEEDBACK_REASONS = [
1215
1546
  ["incorrect", "Incorrect"],
1216
1547
  ["irrelevant", "Irrelevant"],
@@ -1221,8 +1552,8 @@ var FEEDBACK_REASONS = [
1221
1552
  ];
1222
1553
  var enteredCompletionRuns = /* @__PURE__ */ new Set();
1223
1554
  function useLiveCompletionRuns(runId, isRunning) {
1224
- const liveRuns = React5.useRef(/* @__PURE__ */ new Set());
1225
- React5.useEffect(() => {
1555
+ const liveRuns = React8.useRef(/* @__PURE__ */ new Set());
1556
+ React8.useEffect(() => {
1226
1557
  if (!runId || !isRunning) return;
1227
1558
  liveRuns.current.add(runId);
1228
1559
  if (liveRuns.current.size > 256) liveRuns.current.delete(liveRuns.current.values().next().value);
@@ -1230,10 +1561,10 @@ function useLiveCompletionRuns(runId, isRunning) {
1230
1561
  return (candidate) => Boolean(candidate && liveRuns.current.has(candidate));
1231
1562
  }
1232
1563
  function useCompletionActionEntrance(isComplete, runId, wasLive = false) {
1233
- const sawIncomplete = React5.useRef(!isComplete);
1234
- const entered = React5.useRef(false);
1564
+ const sawIncomplete = React8.useRef(!isComplete);
1565
+ const entered = React8.useRef(false);
1235
1566
  const shouldEnter = isComplete && !entered.current && (runId ? wasLive && !enteredCompletionRuns.has(runId) : sawIncomplete.current);
1236
- React5.useEffect(() => {
1567
+ React8.useEffect(() => {
1237
1568
  if (!isComplete) sawIncomplete.current = true;
1238
1569
  else if (shouldEnter) {
1239
1570
  entered.current = true;
@@ -1261,19 +1592,34 @@ function AgentChatDefaultActions({
1261
1592
  handlers,
1262
1593
  message
1263
1594
  }) {
1264
- const [feedbackOpen, setFeedbackOpen] = React5.useState(false);
1265
- const [feedbackTrigger, setFeedbackTrigger] = React5.useState(null);
1266
- const [reason, setReason] = React5.useState(handlers?.feedback?.reason ?? null);
1267
- const [comment, setComment] = React5.useState(handlers?.feedback?.comment ?? "");
1268
- const [optimisticCopied, setOptimisticCopied] = React5.useState(false);
1269
- const copyResetTimer = React5.useRef(null);
1270
- React5.useEffect(() => {
1595
+ const [feedbackOpen, setFeedbackOpen] = React8.useState(false);
1596
+ const [feedbackTrigger, setFeedbackTrigger] = React8.useState(null);
1597
+ const [reason, setReason] = React8.useState(handlers?.feedback?.reason ?? null);
1598
+ const [comment, setComment] = React8.useState(handlers?.feedback?.comment ?? "");
1599
+ const [optimisticCopied, setOptimisticCopied] = React8.useState(false);
1600
+ const copyResetTimer = React8.useRef(null);
1601
+ React8.useEffect(() => {
1271
1602
  setReason(handlers?.feedback?.reason ?? null);
1272
1603
  setComment(handlers?.feedback?.comment ?? "");
1273
1604
  }, [handlers?.feedback?.comment, handlers?.feedback?.reason]);
1274
- React5.useEffect(() => () => {
1605
+ React8.useEffect(() => () => {
1275
1606
  if (copyResetTimer.current !== null) window.clearTimeout(copyResetTimer.current);
1276
1607
  }, []);
1608
+ const [feedbackPending, setFeedbackPending] = React8.useState(false);
1609
+ const feedbackInFlight = React8.useRef(false);
1610
+ const writeFeedback = async (input, close = false) => {
1611
+ if (feedbackInFlight.current) return;
1612
+ feedbackInFlight.current = true;
1613
+ setFeedbackPending(true);
1614
+ try {
1615
+ await handlers?.onSetFeedback?.(message, input);
1616
+ if (close) setFeedbackOpen(false);
1617
+ } catch {
1618
+ } finally {
1619
+ feedbackInFlight.current = false;
1620
+ setFeedbackPending(false);
1621
+ }
1622
+ };
1277
1623
  const copied = handlers?.copied ?? optimisticCopied;
1278
1624
  const copyMessage = () => {
1279
1625
  handlers?.onCopy?.(message.content || "", message.id);
@@ -1288,63 +1634,65 @@ function AgentChatDefaultActions({
1288
1634
  if (!handlers || message.isFinal === false) return null;
1289
1635
  if (message.role === "user") {
1290
1636
  const sentAt = formatSentAt(message.createdAt);
1291
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(ChatMessageActions, { className: "a24-user-message-actions", children: [
1292
- sentAt ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("time", { dateTime: sentAt.dateTime, children: sentAt.label }) : null,
1293
- handlers.onCopy ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1637
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(ChatMessageActions, { className: "a24-user-message-actions", children: [
1638
+ sentAt ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("time", { dateTime: sentAt.dateTime, children: sentAt.label }) : null,
1639
+ handlers.onCopy ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1294
1640
  ChatMessageAction,
1295
1641
  {
1296
1642
  label: "Copy",
1297
1643
  onClick: copyMessage,
1298
1644
  tooltip: copied ? "Copied!" : "Copy to clipboard",
1299
1645
  className: "a24-user-message-copy",
1300
- children: copied ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.Check, { className: "a24-user-message-copy__check text-green-600" }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.Copy, { className: "a24-user-message-copy__icon" })
1646
+ children: copied ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react4.Check, { className: "a24-user-message-copy__check text-green-600" }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react4.Copy, { className: "a24-user-message-copy__icon" })
1301
1647
  }
1302
1648
  ) : null
1303
1649
  ] });
1304
1650
  }
1305
1651
  const canWriteFeedback = Boolean(message.runId && handlers.onSetFeedback);
1306
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(ChatMessageActions, { className: animateCompletion ? "a24-chat-completion-actions" : void 0, children: [
1307
- handlers.onRetry ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1652
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(ChatMessageActions, { className: animateCompletion ? "a24-chat-completion-actions" : void 0, children: [
1653
+ handlers.onRetry ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1308
1654
  ChatMessageAction,
1309
1655
  {
1310
1656
  label: "Retry",
1311
1657
  onClick: () => void handlers.onRetry?.(message),
1312
1658
  tooltip: "Regenerate response",
1313
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.RefreshCcw, { className: "size-4" })
1659
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react4.RefreshCcw, { className: "size-4" })
1314
1660
  }
1315
1661
  ) : null,
1316
- canWriteFeedback ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1662
+ canWriteFeedback ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1317
1663
  ChatMessageAction,
1318
1664
  {
1319
1665
  label: "Like",
1320
- onClick: () => void handlers.onSetFeedback?.(message, {
1666
+ disabled: feedbackPending,
1667
+ onClick: () => void writeFeedback({
1321
1668
  rating: handlers.feedback?.rating === "like" ? null : "like"
1322
1669
  }),
1323
1670
  tooltip: "Like this response",
1324
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.ThumbsUp, { className: "size-4", fill: handlers.feedback?.rating === "like" ? "currentColor" : "none" })
1671
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react4.ThumbsUp, { className: "size-4", fill: handlers.feedback?.rating === "like" ? "currentColor" : "none" })
1325
1672
  }
1326
1673
  ) : null,
1327
- canWriteFeedback ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_radix_ui.Popover.Root, { onOpenChange: setFeedbackOpen, open: feedbackOpen, children: [
1328
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_radix_ui.Popover.Trigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1674
+ canWriteFeedback ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_radix_ui.Popover.Root, { onOpenChange: setFeedbackOpen, open: feedbackOpen, children: [
1675
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_radix_ui.Popover.Trigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1329
1676
  ChatMessageAction,
1330
1677
  {
1678
+ disabled: feedbackPending,
1331
1679
  label: "Dislike",
1332
1680
  onClick: () => {
1333
1681
  if (handlers.feedback?.rating !== "dislike") {
1334
- void handlers.onSetFeedback?.(message, { rating: "dislike" });
1682
+ void writeFeedback({ rating: "dislike" });
1335
1683
  }
1336
1684
  setFeedbackOpen(true);
1337
1685
  },
1338
1686
  ref: setFeedbackTrigger,
1339
1687
  tooltip: "Dislike this response",
1340
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.ThumbsDown, { className: "size-4", fill: handlers.feedback?.rating === "dislike" ? "currentColor" : "none" })
1688
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react4.ThumbsDown, { className: "size-4", fill: handlers.feedback?.rating === "dislike" ? "currentColor" : "none" })
1341
1689
  }
1342
1690
  ) }),
1343
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1691
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1344
1692
  import_radix_ui.Popover.Portal,
1345
1693
  {
1346
1694
  container: feedbackTrigger?.closest("[data-agents24-theme-root]") ?? void 0,
1347
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1695
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1348
1696
  import_radix_ui.Popover.Content,
1349
1697
  {
1350
1698
  align: "end",
@@ -1354,8 +1702,8 @@ function AgentChatDefaultActions({
1354
1702
  side: "top",
1355
1703
  sideOffset: 8,
1356
1704
  children: [
1357
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "text-sm font-medium", children: "What could be better?" }),
1358
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "mt-3 flex flex-wrap gap-1.5", children: FEEDBACK_REASONS.map(([value, label]) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1705
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-sm font-medium", children: "What could be better?" }),
1706
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "mt-3 flex flex-wrap gap-1.5", children: FEEDBACK_REASONS.map(([value, label]) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1359
1707
  "button",
1360
1708
  {
1361
1709
  "aria-pressed": reason === value,
@@ -1366,7 +1714,7 @@ function AgentChatDefaultActions({
1366
1714
  },
1367
1715
  value
1368
1716
  )) }),
1369
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1717
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1370
1718
  "textarea",
1371
1719
  {
1372
1720
  "aria-label": "Optional feedback comment",
@@ -1377,24 +1725,26 @@ function AgentChatDefaultActions({
1377
1725
  value: comment
1378
1726
  }
1379
1727
  ),
1380
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "mt-3 flex items-center justify-between gap-2", children: [
1381
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1728
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "mt-3 flex items-center justify-between gap-2", children: [
1729
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1382
1730
  "button",
1383
1731
  {
1732
+ disabled: feedbackPending,
1384
1733
  className: "rounded-md px-3 py-2 text-sm font-medium hover:bg-muted",
1385
1734
  onClick: () => {
1386
- void Promise.resolve(handlers.onSetFeedback?.(message, { rating: null })).then(() => setFeedbackOpen(false)).catch(() => void 0);
1735
+ void writeFeedback({ rating: null }, true);
1387
1736
  },
1388
1737
  type: "button",
1389
1738
  children: "Remove feedback"
1390
1739
  }
1391
1740
  ),
1392
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1741
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1393
1742
  "button",
1394
1743
  {
1744
+ disabled: feedbackPending,
1395
1745
  className: "rounded-md bg-primary px-3 py-2 text-sm font-medium text-primary-foreground hover:opacity-90",
1396
1746
  onClick: () => {
1397
- void Promise.resolve(handlers.onSetFeedback?.(message, { rating: "dislike", reason, comment })).then(() => setFeedbackOpen(false)).catch(() => void 0);
1747
+ void writeFeedback({ rating: "dislike", reason, comment }, true);
1398
1748
  },
1399
1749
  type: "button",
1400
1750
  children: "Save"
@@ -1408,24 +1758,24 @@ function AgentChatDefaultActions({
1408
1758
  )
1409
1759
  ] }) : null,
1410
1760
  additionalActions,
1411
- handlers.onCopy ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1761
+ handlers.onCopy ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1412
1762
  ChatMessageAction,
1413
1763
  {
1414
1764
  label: "Copy",
1415
1765
  onClick: copyMessage,
1416
1766
  tooltip: copied ? "Copied!" : "Copy to clipboard",
1417
- children: copied ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.Check, { className: "size-4 text-green-600" }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.Copy, { className: "size-4" })
1767
+ children: copied ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react4.Check, { className: "size-4 text-green-600" }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react4.Copy, { className: "size-4" })
1418
1768
  }
1419
1769
  ) : null
1420
1770
  ] });
1421
1771
  }
1422
1772
 
1423
1773
  // src/ui/agent-chat-message.tsx
1424
- var React10 = __toESM(require("react"), 1);
1774
+ var React13 = __toESM(require("react"), 1);
1425
1775
  var import_lucide_react7 = require("lucide-react");
1426
1776
 
1427
1777
  // src/ui/agent-response-timeline.tsx
1428
- var React9 = __toESM(require("react"), 1);
1778
+ var React12 = __toESM(require("react"), 1);
1429
1779
 
1430
1780
  // src/streaming-text.ts
1431
1781
  var import_react = require("react");
@@ -1610,7 +1960,7 @@ function useStreamingText({
1610
1960
  }
1611
1961
 
1612
1962
  // src/ui/ask-user-interaction.tsx
1613
- var React6 = __toESM(require("react"), 1);
1963
+ var React9 = __toESM(require("react"), 1);
1614
1964
  var import_lucide_react5 = require("lucide-react");
1615
1965
 
1616
1966
  // src/ui/ask-user-interaction-state.ts
@@ -1667,7 +2017,7 @@ function askUserAnswersFromDrafts(questions, drafts) {
1667
2017
  }
1668
2018
 
1669
2019
  // src/ui/ask-user-interaction.tsx
1670
- var import_jsx_runtime6 = require("react/jsx-runtime");
2020
+ var import_jsx_runtime7 = require("react/jsx-runtime");
1671
2021
  function resolvedAnswerText(question, answer) {
1672
2022
  if (!answer) return ["No response recorded"];
1673
2023
  const labels = answer.selectedOptionIds.flatMap((optionId) => {
@@ -1681,7 +2031,7 @@ function ResolvedQuestions({
1681
2031
  buttonRef,
1682
2032
  part
1683
2033
  }) {
1684
- const [open, setOpen] = React6.useState(false);
2034
+ const [open, setOpen] = React9.useState(false);
1685
2035
  const count = part.questions.length;
1686
2036
  const answers = new Map(
1687
2037
  (part.resolution?.answers || []).map((answer) => [
@@ -1689,8 +2039,8 @@ function ResolvedQuestions({
1689
2039
  answer
1690
2040
  ])
1691
2041
  );
1692
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "a24-ask-user a24-ask-user--resolved w-full", children: [
1693
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
2042
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "a24-ask-user a24-ask-user--resolved w-full", children: [
2043
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1694
2044
  "button",
1695
2045
  {
1696
2046
  "aria-expanded": open,
@@ -1699,13 +2049,13 @@ function ResolvedQuestions({
1699
2049
  ref: buttonRef,
1700
2050
  type: "button",
1701
2051
  children: [
1702
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
2052
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { children: [
1703
2053
  "Asked ",
1704
2054
  count,
1705
2055
  " question",
1706
2056
  count === 1 ? "" : "s"
1707
2057
  ] }),
1708
- open ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react5.ChevronDown, { "aria-hidden": "true", className: "size-3.5" }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2058
+ open ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react5.ChevronDown, { "aria-hidden": "true", className: "size-3.5" }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1709
2059
  import_lucide_react5.ChevronRight,
1710
2060
  {
1711
2061
  "aria-hidden": "true",
@@ -1715,12 +2065,12 @@ function ResolvedQuestions({
1715
2065
  ]
1716
2066
  }
1717
2067
  ),
1718
- open ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "mt-2 space-y-3 ps-4", children: part.questions.map((question) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "min-w-0", children: [
1719
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "text-sm font-medium text-foreground", children: question.question }),
1720
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "mt-1 space-y-1 text-sm text-muted-foreground", children: resolvedAnswerText(
2068
+ open ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "mt-2 space-y-3 ps-4", children: part.questions.map((question) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "min-w-0", children: [
2069
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "text-sm font-medium text-foreground", children: question.question }),
2070
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "mt-1 space-y-1 text-sm text-muted-foreground", children: resolvedAnswerText(
1721
2071
  question,
1722
2072
  answers.get(question.id)
1723
- ).map((value, index) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2073
+ ).map((value, index) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1724
2074
  "div",
1725
2075
  {
1726
2076
  className: "whitespace-pre-wrap break-words",
@@ -1738,23 +2088,23 @@ function AskUserInteraction({
1738
2088
  part
1739
2089
  }) {
1740
2090
  const questions = part.questions;
1741
- const [step, setStep] = React6.useState(0);
1742
- const [drafts, setDrafts] = React6.useState(
2091
+ const [step, setStep] = React9.useState(0);
2092
+ const [drafts, setDrafts] = React9.useState(
1743
2093
  () => emptyAskUserDrafts(questions)
1744
2094
  );
1745
- const headingRef = React6.useRef(null);
1746
- const questionsRef = React6.useRef(questions);
1747
- const resolvedButtonRef = React6.useRef(null);
1748
- const wasResolvedRef = React6.useRef(part.status !== "pending");
2095
+ const headingRef = React9.useRef(null);
2096
+ const questionsRef = React9.useRef(questions);
2097
+ const resolvedButtonRef = React9.useRef(null);
2098
+ const wasResolvedRef = React9.useRef(part.status !== "pending");
1749
2099
  questionsRef.current = questions;
1750
- React6.useEffect(() => {
2100
+ React9.useEffect(() => {
1751
2101
  setStep(0);
1752
2102
  setDrafts(emptyAskUserDrafts(questionsRef.current));
1753
2103
  }, [part.interruptId]);
1754
- React6.useEffect(() => {
2104
+ React9.useEffect(() => {
1755
2105
  headingRef.current?.focus();
1756
2106
  }, [step]);
1757
- React6.useEffect(() => {
2107
+ React9.useEffect(() => {
1758
2108
  const isResolved = part.status !== "pending";
1759
2109
  if (isResolved && !wasResolvedRef.current) {
1760
2110
  resolvedButtonRef.current?.focus();
@@ -1762,11 +2112,11 @@ function AskUserInteraction({
1762
2112
  wasResolvedRef.current = isResolved;
1763
2113
  }, [part.status]);
1764
2114
  if (part.status !== "pending") {
1765
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ResolvedQuestions, { buttonRef: resolvedButtonRef, part });
2115
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ResolvedQuestions, { buttonRef: resolvedButtonRef, part });
1766
2116
  }
1767
2117
  const question = questions[step];
1768
2118
  if (!question) {
1769
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "text-sm text-destructive", role: "alert", children: "This question request is unavailable." });
2119
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "text-sm text-destructive", role: "alert", children: "This question request is unavailable." });
1770
2120
  }
1771
2121
  const draft = drafts[question.id] || {
1772
2122
  customText: "",
@@ -1800,9 +2150,9 @@ function AskUserInteraction({
1800
2150
  })
1801
2151
  );
1802
2152
  };
1803
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "a24-ask-user space-y-3 rounded-xl border border-border/50 bg-background p-4 shadow-sm", children: [
1804
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex min-w-0 items-start justify-between gap-4", children: [
1805
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2153
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "a24-ask-user space-y-3 rounded-xl border border-border/50 bg-background p-4 shadow-sm", children: [
2154
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex min-w-0 items-start justify-between gap-4", children: [
2155
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1806
2156
  "h3",
1807
2157
  {
1808
2158
  className: "min-w-0 flex-1 text-base font-medium leading-6 text-foreground outline-none",
@@ -1811,8 +2161,8 @@ function AskUserInteraction({
1811
2161
  children: question.question
1812
2162
  }
1813
2163
  ),
1814
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex shrink-0 items-center gap-1 text-muted-foreground", children: [
1815
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2164
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex shrink-0 items-center gap-1 text-muted-foreground", children: [
2165
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1816
2166
  "button",
1817
2167
  {
1818
2168
  "aria-label": "Previous question",
@@ -1820,7 +2170,7 @@ function AskUserInteraction({
1820
2170
  disabled: isBusy || step === 0,
1821
2171
  onClick: () => setStep((current) => Math.max(0, current - 1)),
1822
2172
  type: "button",
1823
- children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2173
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1824
2174
  import_lucide_react5.ChevronLeft,
1825
2175
  {
1826
2176
  "aria-hidden": "true",
@@ -1829,7 +2179,7 @@ function AskUserInteraction({
1829
2179
  )
1830
2180
  }
1831
2181
  ),
1832
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
2182
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1833
2183
  "span",
1834
2184
  {
1835
2185
  "aria-label": `Question ${step + 1} of ${questions.length}`,
@@ -1841,7 +2191,7 @@ function AskUserInteraction({
1841
2191
  ]
1842
2192
  }
1843
2193
  ),
1844
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2194
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1845
2195
  "button",
1846
2196
  {
1847
2197
  "aria-label": isLastQuestion ? "Submit answers" : "Next question",
@@ -1849,7 +2199,7 @@ function AskUserInteraction({
1849
2199
  disabled: isBusy || !onSubmit || !canMoveForward,
1850
2200
  onClick: () => completeStep(drafts),
1851
2201
  type: "button",
1852
- children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2202
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1853
2203
  import_lucide_react5.ChevronRight,
1854
2204
  {
1855
2205
  "aria-hidden": "true",
@@ -1860,10 +2210,10 @@ function AskUserInteraction({
1860
2210
  )
1861
2211
  ] })
1862
2212
  ] }),
1863
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "space-y-1", children: [
2213
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "space-y-1", children: [
1864
2214
  question.options.map((option, index) => {
1865
2215
  const selected = draft.selectedOptionIds.includes(option.id);
1866
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
2216
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1867
2217
  "button",
1868
2218
  {
1869
2219
  "aria-pressed": selected,
@@ -1875,7 +2225,7 @@ function AskUserInteraction({
1875
2225
  onClick: () => selectOption(option.id),
1876
2226
  type: "button",
1877
2227
  children: [
1878
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2228
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1879
2229
  "span",
1880
2230
  {
1881
2231
  className: cn(
@@ -1885,8 +2235,8 @@ function AskUserInteraction({
1885
2235
  children: index + 1
1886
2236
  }
1887
2237
  ),
1888
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "min-w-0 flex-1 font-medium text-foreground", children: option.label }),
1889
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2238
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "min-w-0 flex-1 font-medium text-foreground", children: option.label }),
2239
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1890
2240
  import_lucide_react5.ChevronRight,
1891
2241
  {
1892
2242
  "aria-hidden": "true",
@@ -1898,9 +2248,9 @@ function AskUserInteraction({
1898
2248
  option.id
1899
2249
  );
1900
2250
  }),
1901
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("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: [
1902
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("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__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react5.Pencil, { "aria-hidden": "true", className: "size-4" }) }),
1903
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2251
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("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: [
2252
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("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__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react5.Pencil, { "aria-hidden": "true", className: "size-4" }) }),
2253
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1904
2254
  "input",
1905
2255
  {
1906
2256
  "aria-label": "Other response",
@@ -1919,7 +2269,7 @@ function AskUserInteraction({
1919
2269
  value: draft.customText
1920
2270
  }
1921
2271
  ),
1922
- draft.customText.trim() ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2272
+ draft.customText.trim() ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1923
2273
  "button",
1924
2274
  {
1925
2275
  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",
@@ -1931,12 +2281,12 @@ function AskUserInteraction({
1931
2281
  ) : null
1932
2282
  ] })
1933
2283
  ] }),
1934
- error ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "text-sm text-destructive", role: "alert", children: error }) : null
2284
+ error ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "text-sm text-destructive", role: "alert", children: error }) : null
1935
2285
  ] });
1936
2286
  }
1937
2287
 
1938
2288
  // src/ui/agent-chat-context-compression-row.tsx
1939
- var import_jsx_runtime7 = require("react/jsx-runtime");
2289
+ var import_jsx_runtime8 = require("react/jsx-runtime");
1940
2290
  function compressionPresentation(part) {
1941
2291
  if (part.status === "failed") {
1942
2292
  return { label: "Could not compact message history", status: "failed" };
@@ -1951,7 +2301,7 @@ function AgentChatContextCompressionRow({
1951
2301
  part
1952
2302
  }) {
1953
2303
  const presentation = compressionPresentation(part);
1954
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2304
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1955
2305
  "div",
1956
2306
  {
1957
2307
  "aria-live": "polite",
@@ -1959,11 +2309,11 @@ function AgentChatContextCompressionRow({
1959
2309
  "data-agents24-context-compression": "",
1960
2310
  "data-status": part.status,
1961
2311
  role: "status",
1962
- children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "a24-tool-trigger-row", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: cn(
2312
+ children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "a24-tool-trigger-row", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: cn(
1963
2313
  "a24-tool-trigger a24-tool-trigger--root a24-tool-trigger--without-orb",
1964
2314
  presentation.status === "failed" && "a24-tool-trigger--error",
1965
2315
  presentation.status === "running" && "a24-tool-trigger--active"
1966
- ), children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: cn(
2316
+ ), children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: cn(
1967
2317
  "a24-tool-trigger__label",
1968
2318
  presentation.status === "running" && "a24-tool-shimmer"
1969
2319
  ), children: presentation.label }) }) })
@@ -1972,7 +2322,7 @@ function AgentChatContextCompressionRow({
1972
2322
  }
1973
2323
 
1974
2324
  // src/ui/message-response.tsx
1975
- var React7 = __toESM(require("react"), 1);
2325
+ var React10 = __toESM(require("react"), 1);
1976
2326
  var import_streamdown = require("streamdown");
1977
2327
 
1978
2328
  // src/active-content.ts
@@ -1997,17 +2347,17 @@ function safeChatMarkdownHref(value) {
1997
2347
  }
1998
2348
 
1999
2349
  // src/ui/message-response.tsx
2000
- var import_jsx_runtime8 = require("react/jsx-runtime");
2350
+ var import_jsx_runtime9 = require("react/jsx-runtime");
2001
2351
  function SafeMarkdownLink({ children, href, ...props }) {
2002
2352
  const domProps = { ...props };
2003
2353
  delete domProps.node;
2004
2354
  const safeHref = safeChatMarkdownHref(href);
2005
- if (!safeHref) return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children });
2006
- if (safeHref.startsWith("#")) return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("a", { ...domProps, href: safeHref, children });
2007
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("a", { ...domProps, href: safeHref, rel: "noopener noreferrer", target: "_blank", children });
2355
+ if (!safeHref) return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children });
2356
+ if (safeHref.startsWith("#")) return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("a", { ...domProps, href: safeHref, children });
2357
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("a", { ...domProps, href: safeHref, rel: "noopener noreferrer", target: "_blank", children });
2008
2358
  }
2009
2359
  function BlockedMarkdownImage({ alt }) {
2010
- return alt ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { "aria-label": `Image blocked: ${alt}`, role: "img", children: alt }) : null;
2360
+ return alt ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { "aria-label": `Image blocked: ${alt}`, role: "img", children: alt }) : null;
2011
2361
  }
2012
2362
  var BLOCK_MARKDOWN_TAGS = /* @__PURE__ */ new Set([
2013
2363
  "blockquote",
@@ -2028,7 +2378,7 @@ function isMultilineCodeNode(node) {
2028
2378
  return node?.tagName === "code" && node.position?.start?.line !== void 0 && node.position?.end?.line !== void 0 && node.position.start.line !== node.position.end.line;
2029
2379
  }
2030
2380
  function hasBlockMarkdownChild(child) {
2031
- if (!React7.isValidElement(child)) {
2381
+ if (!React10.isValidElement(child)) {
2032
2382
  return false;
2033
2383
  }
2034
2384
  if (typeof child.type === "string" && BLOCK_MARKDOWN_TAGS.has(child.type)) {
@@ -2038,7 +2388,7 @@ function hasBlockMarkdownChild(child) {
2038
2388
  if (BLOCK_MARKDOWN_TAGS.has(props.node?.tagName || "") || isMultilineCodeNode(props.node)) {
2039
2389
  return true;
2040
2390
  }
2041
- return React7.Children.toArray(props.children).some(hasBlockMarkdownChild);
2391
+ return React10.Children.toArray(props.children).some(hasBlockMarkdownChild);
2042
2392
  }
2043
2393
  function MessageMarkdownParagraph({
2044
2394
  children,
@@ -2046,17 +2396,17 @@ function MessageMarkdownParagraph({
2046
2396
  }) {
2047
2397
  const domProps = { ...props };
2048
2398
  delete domProps.node;
2049
- const childArray = React7.Children.toArray(children).filter(
2399
+ const childArray = React10.Children.toArray(children).filter(
2050
2400
  (child) => child !== null && child !== ""
2051
2401
  );
2052
- const isImageOnly = childArray.length === 1 && React7.isValidElement(childArray[0]) && childArray[0].props.node?.tagName === "img";
2402
+ const isImageOnly = childArray.length === 1 && React10.isValidElement(childArray[0]) && childArray[0].props.node?.tagName === "img";
2053
2403
  if (isImageOnly) {
2054
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_jsx_runtime8.Fragment, { children });
2404
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_jsx_runtime9.Fragment, { children });
2055
2405
  }
2056
2406
  if (childArray.some(hasBlockMarkdownChild)) {
2057
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { ...domProps, children });
2407
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { ...domProps, children });
2058
2408
  }
2059
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { ...domProps, children });
2409
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { ...domProps, children });
2060
2410
  }
2061
2411
  var MESSAGE_RESPONSE_COMPONENTS = {
2062
2412
  a: SafeMarkdownLink,
@@ -2076,7 +2426,7 @@ var MESSAGE_RESPONSE_REHYPE_PLUGINS = [
2076
2426
  ]
2077
2427
  ] : []
2078
2428
  ];
2079
- var MessageResponse = React7.memo(
2429
+ var MessageResponse = React10.memo(
2080
2430
  ({
2081
2431
  children,
2082
2432
  className,
@@ -2094,11 +2444,11 @@ var MessageResponse = React7.memo(
2094
2444
  maxCatchupChars: 32,
2095
2445
  text
2096
2446
  });
2097
- const streamdownComponents = React7.useMemo(
2447
+ const streamdownComponents = React10.useMemo(
2098
2448
  () => ({ ...MESSAGE_RESPONSE_COMPONENTS, ...components }),
2099
2449
  [components]
2100
2450
  );
2101
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2451
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2102
2452
  import_streamdown.Streamdown,
2103
2453
  {
2104
2454
  className: cn("a24-message-response", className),
@@ -2150,10 +2500,10 @@ function resolvedHitlLabel(part) {
2150
2500
  }
2151
2501
 
2152
2502
  // src/ui/tool-sessions.tsx
2153
- var React8 = __toESM(require("react"), 1);
2503
+ var React11 = __toESM(require("react"), 1);
2154
2504
  var import_lucide_react6 = require("lucide-react");
2155
2505
  var import_thinking_orbs = require("thinking-orbs");
2156
- var import_jsx_runtime9 = require("react/jsx-runtime");
2506
+ var import_jsx_runtime10 = require("react/jsx-runtime");
2157
2507
  function toolSlotContext(isActive2, label, part, slots) {
2158
2508
  return {
2159
2509
  isActive: isActive2,
@@ -2171,7 +2521,7 @@ function ToolResourceAction({ context }) {
2171
2521
  const target = context.resourceTargets[0];
2172
2522
  if (!target) return null;
2173
2523
  const name = resourceTargetName(target);
2174
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2524
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2175
2525
  "button",
2176
2526
  {
2177
2527
  "aria-label": `Open ${target.kind} ${target.path}`,
@@ -2185,8 +2535,8 @@ function ToolResourceAction({ context }) {
2185
2535
  );
2186
2536
  }
2187
2537
  function toolAccessory(context, slots) {
2188
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
2189
- slots?.onResourceAction ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ToolResourceAction, { context }) : null,
2538
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
2539
+ slots?.onResourceAction ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ToolResourceAction, { context }) : null,
2190
2540
  slots?.renderToolAccessory?.(context)
2191
2541
  ] });
2192
2542
  }
@@ -2263,12 +2613,12 @@ function ToolActivityReel({
2263
2613
  }) {
2264
2614
  const initial = initialLabel && initialLabel !== label ? initialLabel : label;
2265
2615
  const parsedInitialShownAt = initialShownAt ? Date.parse(initialShownAt) : Number.NaN;
2266
- const [displayed, setDisplayed] = React8.useState(initial);
2267
- const [previous, setPrevious] = React8.useState(null);
2268
- const displayedRef = React8.useRef(initial);
2269
- const latestRef = React8.useRef(label);
2270
- const shownAtRef = React8.useRef(Number.isFinite(parsedInitialShownAt) ? parsedInitialShownAt : Date.now());
2271
- React8.useEffect(() => {
2616
+ const [displayed, setDisplayed] = React11.useState(initial);
2617
+ const [previous, setPrevious] = React11.useState(null);
2618
+ const displayedRef = React11.useRef(initial);
2619
+ const latestRef = React11.useRef(label);
2620
+ const shownAtRef = React11.useRef(Number.isFinite(parsedInitialShownAt) ? parsedInitialShownAt : Date.now());
2621
+ React11.useEffect(() => {
2272
2622
  latestRef.current = label;
2273
2623
  if (displayedRef.current === label) return;
2274
2624
  const showLatest = () => {
@@ -2288,39 +2638,41 @@ function ToolActivityReel({
2288
2638
  const timer = window.setTimeout(showLatest, remaining);
2289
2639
  return () => window.clearTimeout(timer);
2290
2640
  }, [label]);
2291
- React8.useEffect(() => {
2641
+ React11.useEffect(() => {
2292
2642
  if (!previous) return;
2293
2643
  const timer = window.setTimeout(() => setPrevious(null), 200);
2294
2644
  return () => window.clearTimeout(timer);
2295
2645
  }, [previous]);
2296
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "a24-tool-reel", "aria-live": announce ? "polite" : "off", children: [
2297
- previous ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { "aria-hidden": "true", className: "a24-tool-reel__previous", children: previous }) : null,
2298
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: previous ? "a24-tool-reel__current" : void 0, children: displayed })
2646
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "a24-tool-reel", "aria-live": announce ? "polite" : "off", children: [
2647
+ previous ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { "aria-hidden": "true", className: "a24-tool-reel__previous", children: previous }) : null,
2648
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: previous ? "a24-tool-reel__current" : void 0, children: displayed })
2299
2649
  ] });
2300
2650
  }
2301
- function ToolActivityOrb({ label }) {
2302
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "a24-tool-orb", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_thinking_orbs.ThinkingOrb, { "aria-label": label || "Working", size: 20, speed: 0.8, state: "searching" }) });
2651
+ function ToolActivityOrb({ label = "Working", kind = "tool", part, renderActivityIcon }) {
2652
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "a24-tool-orb", children: renderActivityIcon ? renderActivityIcon({ label, kind, part }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_thinking_orbs.ThinkingOrb, { "aria-label": label, size: 20, speed: 0.8, state: "searching" }) });
2303
2653
  }
2304
2654
  function AgentChatActivityRow({
2305
2655
  label,
2306
2656
  modelTurnId,
2657
+ renderActivityIcon,
2307
2658
  onSelect
2308
2659
  }) {
2309
- const content = /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
2310
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ToolActivityOrb, { label }),
2311
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "a24-tool-trigger__label a24-tool-shimmer", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ToolActivityReel, { label }) })
2660
+ const content = /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
2661
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ToolActivityOrb, { label, kind: "planning", renderActivityIcon }),
2662
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "a24-tool-trigger__label a24-tool-shimmer", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ToolActivityReel, { label }) })
2312
2663
  ] });
2313
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "a24-tool-trigger-row a24-tool-trigger-row--root", "data-agents24-model-turn": modelTurnId, role: "status", children: onSelect ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { className: "a24-tool-trigger a24-tool-trigger--root a24-tool-trigger--active", onClick: onSelect, type: "button", children: content }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "a24-tool-trigger a24-tool-trigger--root a24-tool-trigger--active", children: content }) });
2664
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "a24-tool-trigger-row a24-tool-trigger-row--root", "data-agents24-model-turn": modelTurnId, role: "status", children: onSelect ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { className: "a24-tool-trigger a24-tool-trigger--root a24-tool-trigger--active", onClick: onSelect, type: "button", children: content }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "a24-tool-trigger a24-tool-trigger--root a24-tool-trigger--active", children: content }) });
2314
2665
  }
2315
2666
  function AgentChatModelTurnRow({ part }) {
2316
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(AgentChatActivityRow, { label: "Planning next moves", modelTurnId: part.modelTurnId });
2667
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(AgentChatActivityRow, { label: "Planning next moves", modelTurnId: part.modelTurnId });
2317
2668
  }
2318
2669
  function AgentChatNodeActivityRow({
2670
+ renderActivityIcon,
2319
2671
  onSelect,
2320
2672
  part
2321
2673
  }) {
2322
- const [open, setOpen] = React8.useState(false);
2323
- const contentId = React8.useId();
2674
+ const [open, setOpen] = React11.useState(false);
2675
+ const contentId = React11.useId();
2324
2676
  const running = part.status === "running";
2325
2677
  const failed = part.status === "failed";
2326
2678
  const failureMessage = part.failure?.message || "The node could not complete the request.";
@@ -2331,18 +2683,18 @@ function AgentChatNodeActivityRow({
2331
2683
  running && "a24-tool-trigger--active",
2332
2684
  failed && "a24-tool-trigger--error"
2333
2685
  );
2334
- const label = running ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "a24-tool-shimmer", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ToolActivityReel, { label: part.label }) }) : part.label;
2686
+ const label = running ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "a24-tool-shimmer", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ToolActivityReel, { label: part.label }) }) : part.label;
2335
2687
  if (!failed) {
2336
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "a24-tool-trigger-row", role: "status", children: onSelect ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("button", { className: triggerClassName, onClick: () => onSelect(part), type: "button", children: [
2337
- running ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ToolActivityOrb, { label: part.label }) : null,
2338
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "a24-tool-trigger__label", children: label })
2339
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: triggerClassName, children: [
2340
- running ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ToolActivityOrb, { label: part.label }) : null,
2341
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "a24-tool-trigger__label", children: label })
2688
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "a24-tool-trigger-row", role: "status", children: onSelect ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("button", { className: triggerClassName, onClick: () => onSelect(part), type: "button", children: [
2689
+ running ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ToolActivityOrb, { label: part.label, kind: "node", part, renderActivityIcon }) : null,
2690
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "a24-tool-trigger__label", children: label })
2691
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: triggerClassName, children: [
2692
+ running ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ToolActivityOrb, { label: part.label, kind: "node", part, renderActivityIcon }) : null,
2693
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "a24-tool-trigger__label", children: label })
2342
2694
  ] }) });
2343
2695
  }
2344
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "a24-tool-disclosure", children: [
2345
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "a24-tool-trigger-row", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
2696
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "a24-tool-disclosure", children: [
2697
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "a24-tool-trigger-row", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
2346
2698
  "button",
2347
2699
  {
2348
2700
  "aria-controls": contentId,
@@ -2354,12 +2706,12 @@ function AgentChatNodeActivityRow({
2354
2706
  },
2355
2707
  type: "button",
2356
2708
  children: [
2357
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "a24-tool-trigger__label", children: label }),
2358
- open ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react6.ChevronDown, { "aria-hidden": "true", className: "a24-tool-trigger__chevron" }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react6.ChevronRight, { "aria-hidden": "true", className: "a24-tool-trigger__chevron" })
2709
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "a24-tool-trigger__label", children: label }),
2710
+ open ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react6.ChevronDown, { "aria-hidden": "true", className: "a24-tool-trigger__chevron" }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react6.ChevronRight, { "aria-hidden": "true", className: "a24-tool-trigger__chevron" })
2359
2711
  ]
2360
2712
  }
2361
2713
  ) }),
2362
- open ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "a24-tool-detail a24-tool-detail--error", id: contentId, children: failureMessage }) : null
2714
+ open ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "a24-tool-detail a24-tool-detail--error", id: contentId, children: failureMessage }) : null
2363
2715
  ] });
2364
2716
  }
2365
2717
  function toolRowLabel(part, separateResource = false) {
@@ -2383,27 +2735,28 @@ function ToolDisclosure({
2383
2735
  root,
2384
2736
  showOrb,
2385
2737
  statusLabel,
2738
+ renderActivityIcon,
2386
2739
  accessory
2387
2740
  }) {
2388
- const [open, setOpen] = React8.useState(false);
2389
- const contentId = React8.useId();
2390
- const expandable = React8.Children.toArray(children).length > 0;
2741
+ const [open, setOpen] = React11.useState(false);
2742
+ const contentId = React11.useId();
2743
+ const expandable = React11.Children.toArray(children).length > 0;
2391
2744
  if (!expandable) {
2392
2745
  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");
2393
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: cn("a24-tool-trigger-row", root && "a24-tool-trigger-row--root"), children: [
2394
- onSelect && part ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("button", { className: triggerClassName, onClick: () => onSelect(part), type: "button", children: [
2395
- showOrb ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ToolActivityOrb, { label: statusLabel }) : null,
2396
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "a24-tool-trigger__label", children: label })
2397
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: triggerClassName, children: [
2398
- showOrb ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ToolActivityOrb, { label: statusLabel }) : null,
2399
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "a24-tool-trigger__label", children: label })
2746
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: cn("a24-tool-trigger-row", root && "a24-tool-trigger-row--root"), children: [
2747
+ onSelect && part ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("button", { className: triggerClassName, onClick: () => onSelect(part), type: "button", children: [
2748
+ showOrb ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ToolActivityOrb, { label: statusLabel || "Working", kind: grouped ? "group" : "tool", part, renderActivityIcon }) : null,
2749
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "a24-tool-trigger__label", children: label })
2750
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: triggerClassName, children: [
2751
+ showOrb ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ToolActivityOrb, { label: statusLabel || "Working", kind: grouped ? "group" : "tool", part, renderActivityIcon }) : null,
2752
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "a24-tool-trigger__label", children: label })
2400
2753
  ] }),
2401
2754
  accessory
2402
2755
  ] });
2403
2756
  }
2404
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "a24-tool-disclosure", children: [
2405
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: cn("a24-tool-trigger-row", root && "a24-tool-trigger-row--root"), children: [
2406
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
2757
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "a24-tool-disclosure", children: [
2758
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: cn("a24-tool-trigger-row", root && "a24-tool-trigger-row--root"), children: [
2759
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
2407
2760
  "button",
2408
2761
  {
2409
2762
  "aria-controls": contentId,
@@ -2417,25 +2770,25 @@ function ToolDisclosure({
2417
2770
  },
2418
2771
  type: "button",
2419
2772
  children: [
2420
- showOrb ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ToolActivityOrb, { label: statusLabel }) : null,
2421
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "a24-tool-trigger__label", children: label }),
2422
- open ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react6.ChevronDown, { "aria-hidden": "true", className: "a24-tool-trigger__chevron" }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react6.ChevronRight, { "aria-hidden": "true", className: "a24-tool-trigger__chevron" })
2773
+ showOrb ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ToolActivityOrb, { label: statusLabel || "Working", kind: grouped ? "group" : "tool", part, renderActivityIcon }) : null,
2774
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "a24-tool-trigger__label", children: label }),
2775
+ open ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react6.ChevronDown, { "aria-hidden": "true", className: "a24-tool-trigger__chevron" }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react6.ChevronRight, { "aria-hidden": "true", className: "a24-tool-trigger__chevron" })
2423
2776
  ]
2424
2777
  }
2425
2778
  ),
2426
2779
  accessory
2427
2780
  ] }),
2428
- open ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "a24-tool-disclosure__content", id: contentId, children }) : null
2781
+ open ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "a24-tool-disclosure__content", id: contentId, children }) : null
2429
2782
  ] });
2430
2783
  }
2431
2784
  function ToolLabel({ active, label, part, slots }) {
2432
2785
  const rendered = slots?.renderToolLabel?.(toolSlotContext(active, label, part, slots)) ?? label;
2433
- return active ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "a24-tool-shimmer", children: rendered }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_jsx_runtime9.Fragment, { children: rendered });
2786
+ return active ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "a24-tool-shimmer", children: rendered }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_jsx_runtime10.Fragment, { children: rendered });
2434
2787
  }
2435
2788
  function ToolList({ children }) {
2436
- const listRef = React8.useRef(null);
2437
- const [fades, setFades] = React8.useState({ bottom: false, top: false });
2438
- const updateFades = React8.useCallback(() => {
2789
+ const listRef = React11.useRef(null);
2790
+ const [fades, setFades] = React11.useState({ bottom: false, top: false });
2791
+ const updateFades = React11.useCallback(() => {
2439
2792
  const list = listRef.current;
2440
2793
  if (!list) return;
2441
2794
  const next = {
@@ -2444,7 +2797,7 @@ function ToolList({ children }) {
2444
2797
  };
2445
2798
  setFades((current) => current.bottom === next.bottom && current.top === next.top ? current : next);
2446
2799
  }, []);
2447
- React8.useEffect(() => {
2800
+ React11.useEffect(() => {
2448
2801
  updateFades();
2449
2802
  const list = listRef.current;
2450
2803
  if (!list || typeof ResizeObserver === "undefined") return;
@@ -2452,13 +2805,13 @@ function ToolList({ children }) {
2452
2805
  observer.observe(list);
2453
2806
  return () => observer.disconnect();
2454
2807
  }, [children, updateFades]);
2455
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2808
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2456
2809
  "div",
2457
2810
  {
2458
2811
  className: "a24-tool-list-frame",
2459
2812
  "data-scroll-fade-bottom": fades.bottom ? "" : void 0,
2460
2813
  "data-scroll-fade-top": fades.top ? "" : void 0,
2461
- children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "a24-tool-list", onScroll: updateFades, ref: listRef, role: "list", children })
2814
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "a24-tool-list", onScroll: updateFades, ref: listRef, role: "list", children })
2462
2815
  }
2463
2816
  );
2464
2817
  }
@@ -2475,12 +2828,13 @@ function AgentChatToolRow({
2475
2828
  const detail = failed ? failureMessage : optionalText(part.presentation.detail);
2476
2829
  const hostDetails = failed ? null : slots?.renderToolDetails?.(part);
2477
2830
  const context = toolSlotContext(active, toolRowLabel(part, Boolean(slots?.onResourceAction)), part, slots);
2478
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { "data-agents24-tool-part": part.type, "data-state": part.state, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
2831
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { "data-agents24-tool-part": part.type, "data-state": part.state, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
2479
2832
  ToolDisclosure,
2480
2833
  {
2834
+ renderActivityIcon: slots?.renderActivityIcon,
2481
2835
  error: failed,
2482
2836
  accessory: toolAccessory(context, slots),
2483
- label: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ToolLabel, { active, label: context.label, part, slots }),
2837
+ label: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ToolLabel, { active, label: context.label, part, slots }),
2484
2838
  loading: active,
2485
2839
  onExpandedChange: slots?.onExpandedChange,
2486
2840
  onSelect: slots?.onToolSelect,
@@ -2489,7 +2843,7 @@ function AgentChatToolRow({
2489
2843
  showOrb: showOrb ?? active,
2490
2844
  statusLabel: active ? context.label : void 0,
2491
2845
  children: [
2492
- detail ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: cn("a24-tool-detail", failed && "a24-tool-detail--error"), children: detail }) : null,
2846
+ detail ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: cn("a24-tool-detail", failed && "a24-tool-detail--error"), children: detail }) : null,
2493
2847
  hostDetails
2494
2848
  ]
2495
2849
  }
@@ -2509,21 +2863,22 @@ function AgentChatToolSession({
2509
2863
  parts,
2510
2864
  slots
2511
2865
  }) {
2512
- const presentedLiveRef = React8.useRef(false);
2866
+ const presentedLiveRef = React11.useRef(false);
2513
2867
  if (isLive && isLatestActivity) presentedLiveRef.current = true;
2514
2868
  const activeIndex = parts.findLastIndex((part) => isActive(part, activeToolId));
2515
2869
  const running = activeIndex >= 0;
2516
- if (parts.length === 1 && !modelTurn && !leadingModelTurn) return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(AgentChatToolRow, { activeToolId, part: parts[0], slots });
2870
+ if (parts.length === 1 && !modelTurn && !leadingModelTurn) return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(AgentChatToolRow, { activeToolId, part: parts[0], slots });
2517
2871
  const visibleIndex = running ? activeIndex : parts.length - 1;
2518
2872
  const visiblePart = parts[visibleIndex];
2519
2873
  const planning = Boolean(modelTurn);
2520
2874
  const presentingActivity = isLatestActivity && (planning || running || isLive);
2521
2875
  const failed = isLatestActivity && (visiblePart.state === "output-error" || visiblePart.state === "cancelled");
2522
2876
  const label = planning ? "Planning next moves" : isLive && isLatestActivity ? toolRowLabel(visiblePart, true) : sessionLabel(parts, visiblePart, running);
2523
- const labelContent = presentedLiveRef.current ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: presentingActivity ? "a24-tool-shimmer" : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ToolActivityReel, { initialLabel: leadingModelTurn ? "Planning next moves" : void 0, initialShownAt: leadingModelTurn?.timestamp, label }) }) : label;
2524
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2877
+ const labelContent = presentedLiveRef.current ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: presentingActivity ? "a24-tool-shimmer" : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ToolActivityReel, { initialLabel: leadingModelTurn ? "Planning next moves" : void 0, initialShownAt: leadingModelTurn?.timestamp, label }) }) : label;
2878
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2525
2879
  ToolDisclosure,
2526
2880
  {
2881
+ renderActivityIcon: slots?.renderActivityIcon,
2527
2882
  error: failed,
2528
2883
  grouped: true,
2529
2884
  label: labelContent,
@@ -2533,7 +2888,7 @@ function AgentChatToolSession({
2533
2888
  root: true,
2534
2889
  showOrb: isLive && isLatestActivity,
2535
2890
  statusLabel: planning ? "Planning next moves" : label,
2536
- children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ToolList, { children: parts.map((part) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { role: "listitem", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(AgentChatToolRow, { activeToolId, part, root: false, showOrb: false, slots }) }, part.id)) })
2891
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ToolList, { children: parts.map((part) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { role: "listitem", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(AgentChatToolRow, { activeToolId, part, root: false, showOrb: false, slots }) }, part.id)) })
2537
2892
  }
2538
2893
  );
2539
2894
  }
@@ -2554,7 +2909,7 @@ function subagentLabel(part) {
2554
2909
  return { label: `Agent ${name}, ran ${children.length} ${children.length === 1 ? "tool" : "tools"}`, running: false };
2555
2910
  }
2556
2911
  function AgentChatSubagentGroup({ isLatestActivity = true, isLive = false, part, slots }) {
2557
- const presentedLiveRef = React8.useRef(false);
2912
+ const presentedLiveRef = React11.useRef(false);
2558
2913
  if (isLive && isLatestActivity) presentedLiveRef.current = true;
2559
2914
  const children = part.presentation.childTools || [];
2560
2915
  const state = subagentLabel(part);
@@ -2563,11 +2918,12 @@ function AgentChatSubagentGroup({ isLatestActivity = true, isLive = false, part,
2563
2918
  const hostDetails = failed ? null : slots?.renderToolDetails?.(part);
2564
2919
  const failureMessage = part.failure?.message || null;
2565
2920
  const presentingActivity = isLatestActivity && state.running;
2566
- const labelContent = presentedLiveRef.current ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: presentingActivity ? "a24-tool-shimmer" : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ToolActivityReel, { label: state.label }) }) : state.label;
2921
+ const labelContent = presentedLiveRef.current ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: presentingActivity ? "a24-tool-shimmer" : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ToolActivityReel, { label: state.label }) }) : state.label;
2567
2922
  const context = toolSlotContext(presentingActivity, state.label, part, slots);
2568
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2923
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2569
2924
  ToolDisclosure,
2570
2925
  {
2926
+ renderActivityIcon: slots?.renderActivityIcon,
2571
2927
  accessory: slots?.renderToolAccessory?.(context),
2572
2928
  error: failed,
2573
2929
  grouped: true,
@@ -2578,12 +2934,12 @@ function AgentChatSubagentGroup({ isLatestActivity = true, isLive = false, part,
2578
2934
  root: true,
2579
2935
  showOrb: isLive && isLatestActivity,
2580
2936
  statusLabel: state.label,
2581
- children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(ToolList, { children: [
2582
- children.map((child) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: cn("a24-child-tool", child.status === "failed" && "a24-child-tool--error"), role: "listitem", children: slots?.renderChildTool?.(child, part) ?? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
2583
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: cn(isLive && isLatestActivity && child.status === "running" && "a24-tool-shimmer"), children: childLabel(child) }),
2584
- child.detail ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "a24-child-tool__detail", children: child.detail }) : null
2937
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(ToolList, { children: [
2938
+ children.map((child) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: cn("a24-child-tool", child.status === "failed" && "a24-child-tool--error"), role: "listitem", children: slots?.renderChildTool?.(child, part) ?? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
2939
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: cn(isLive && isLatestActivity && child.status === "running" && "a24-tool-shimmer"), children: childLabel(child) }),
2940
+ child.detail ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "a24-child-tool__detail", children: child.detail }) : null
2585
2941
  ] }) }, child.id)),
2586
- failureMessage ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "a24-tool-detail a24-tool-detail--error", children: failureMessage }) : null,
2942
+ failureMessage ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "a24-tool-detail a24-tool-detail--error", children: failureMessage }) : null,
2587
2943
  hostDetails
2588
2944
  ] })
2589
2945
  }
@@ -2591,24 +2947,24 @@ function AgentChatSubagentGroup({ isLatestActivity = true, isLive = false, part,
2591
2947
  }
2592
2948
 
2593
2949
  // src/ui/agent-response-timeline.tsx
2594
- var import_jsx_runtime10 = require("react/jsx-runtime");
2950
+ var import_jsx_runtime11 = require("react/jsx-runtime");
2595
2951
  function isInternalAskUserTool(part) {
2596
2952
  return [part.toolName, part.toolKey, part.action].some((value) => value?.trim().toLowerCase() === "runtime__ask_user");
2597
2953
  }
2598
2954
  function Shimmer({ children, className }) {
2599
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: cn("inline-block animate-pulse", className), children });
2955
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: cn("inline-block animate-pulse", className), children });
2600
2956
  }
2601
2957
  function AgentChatToolPart({
2602
2958
  activeToolId,
2603
2959
  part
2604
2960
  }) {
2605
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(AgentChatToolRow, { activeToolId, part });
2961
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(AgentChatToolRow, { activeToolId, part });
2606
2962
  }
2607
2963
  function AgentChatToolGroup({
2608
2964
  activeToolId,
2609
2965
  parts
2610
2966
  }) {
2611
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(AgentChatToolSession, { activeToolId, parts });
2967
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(AgentChatToolSession, { activeToolId, parts });
2612
2968
  }
2613
2969
  function HitlPartRow({
2614
2970
  getHitlActionState,
@@ -2617,18 +2973,18 @@ function HitlPartRow({
2617
2973
  part
2618
2974
  }) {
2619
2975
  const state = getHitlActionState?.(part);
2620
- const [comment, setComment] = React9.useState("");
2976
+ const [comment, setComment] = React12.useState("");
2621
2977
  const presentation = part.presentation || {};
2622
2978
  const requireComment = presentation.require_comment === true;
2623
2979
  const isTerminal = part.status !== "pending";
2624
- const resolvedRef = React9.useRef(null);
2625
- const wasTerminal = React9.useRef(isTerminal);
2626
- React9.useEffect(() => {
2980
+ const resolvedRef = React12.useRef(null);
2981
+ const wasTerminal = React12.useRef(isTerminal);
2982
+ React12.useEffect(() => {
2627
2983
  if (isTerminal && !wasTerminal.current) resolvedRef.current?.focus();
2628
2984
  wasTerminal.current = isTerminal;
2629
2985
  }, [isTerminal]);
2630
2986
  if (part.hitlKind === "user_input") {
2631
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2987
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2632
2988
  AskUserInteraction,
2633
2989
  {
2634
2990
  error: state?.error,
@@ -2646,11 +3002,11 @@ function HitlPartRow({
2646
3002
  );
2647
3003
  }
2648
3004
  if (isTerminal) {
2649
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("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) });
3005
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("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) });
2650
3006
  }
2651
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "a24-hitl space-y-2.5 px-1 py-2", children: [
2652
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "a24-hitl__title text-sm font-medium text-foreground text-pretty", children: hitlTitle(part) }),
2653
- requireComment ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3007
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "a24-hitl space-y-2.5 px-1 py-2", children: [
3008
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "a24-hitl__title text-sm font-medium text-foreground text-pretty", children: hitlTitle(part) }),
3009
+ requireComment ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2654
3010
  "textarea",
2655
3011
  {
2656
3012
  "aria-label": "Approval comment",
@@ -2662,8 +3018,8 @@ function HitlPartRow({
2662
3018
  value: comment
2663
3019
  }
2664
3020
  ) : null,
2665
- state?.error ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "a24-hitl__error text-sm text-destructive", children: state.error }) : null,
2666
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "a24-hitl__actions flex flex-wrap items-center gap-1.5", children: part.allowedActions.map((action) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3021
+ state?.error ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "a24-hitl__error text-sm text-destructive", children: state.error }) : null,
3022
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "a24-hitl__actions flex flex-wrap items-center gap-1.5", children: part.allowedActions.map((action) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2667
3023
  "button",
2668
3024
  {
2669
3025
  "aria-label": hitlActionLabel(part, action),
@@ -2696,7 +3052,7 @@ function AgentChatPart({
2696
3052
  toolSlots
2697
3053
  }) {
2698
3054
  if (part.kind === "text") {
2699
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3055
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2700
3056
  MessageResponse,
2701
3057
  {
2702
3058
  ...responseProps,
@@ -2706,23 +3062,23 @@ function AgentChatPart({
2706
3062
  }
2707
3063
  );
2708
3064
  }
2709
- if (part.kind === "tool") return isAgentChatSubagent(part) ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(AgentChatSubagentGroup, { part, slots: toolSlots }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(AgentChatToolRow, { activeToolId, part, slots: toolSlots });
2710
- if (part.kind === "model-turn") return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(AgentChatModelTurnRow, { part });
3065
+ if (part.kind === "tool") return isAgentChatSubagent(part) ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(AgentChatSubagentGroup, { part, slots: toolSlots }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(AgentChatToolRow, { activeToolId, part, slots: toolSlots });
3066
+ if (part.kind === "model-turn") return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(AgentChatModelTurnRow, { part });
2711
3067
  if (part.kind === "reasoning") {
2712
3068
  const label = part.text || part.label || "Thinking";
2713
- return part.status === "running" || part.status === "streaming" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Shimmer, { className: "text-sm text-muted-foreground", children: label }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "text-sm text-muted-foreground", children: label });
3069
+ return part.status === "running" || part.status === "streaming" ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Shimmer, { className: "text-sm text-muted-foreground", children: label }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "text-sm text-muted-foreground", children: label });
2714
3070
  }
2715
3071
  if (part.kind === "context-compression") {
2716
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(AgentChatContextCompressionRow, { part });
3072
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(AgentChatContextCompressionRow, { part });
2717
3073
  }
2718
3074
  if (part.kind === "ui-blocks") {
2719
3075
  if (part.state === "output-error") {
2720
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "a24-timeline-error text-sm text-destructive", children: part.errorText || "Failed to render UI content." });
3076
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "a24-timeline-error text-sm text-destructive", children: part.errorText || "Failed to render UI content." });
2721
3077
  }
2722
- return part.bundle ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(SafeUiBlocks, { bundle: part.bundle }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "text-sm text-muted-foreground", children: part.state === "input-streaming" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Shimmer, { children: "Rendering UI block" }) : "Rendered UI block" });
3078
+ return part.bundle ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(SafeUiBlocks, { bundle: part.bundle }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "text-sm text-muted-foreground", children: part.state === "input-streaming" ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Shimmer, { children: "Rendering UI block" }) : "Rendered UI block" });
2723
3079
  }
2724
3080
  if (part.kind === "hitl") {
2725
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3081
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2726
3082
  HitlPartRow,
2727
3083
  {
2728
3084
  getHitlActionState,
@@ -2732,18 +3088,18 @@ function AgentChatPart({
2732
3088
  }
2733
3089
  );
2734
3090
  }
2735
- if (part.kind === "error") return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "a24-timeline-error text-sm text-destructive", role: "alert", children: part.errorText });
3091
+ if (part.kind === "error") return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "a24-timeline-error text-sm text-destructive", role: "alert", children: part.errorText });
2736
3092
  if (part.kind === "source") {
2737
3093
  const href = safeExternalUrl(part.url);
2738
- return href ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("a", { className: "a24-source-link", href, rel: "noopener noreferrer", target: "_blank", children: part.title }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "a24-source-link", children: part.title });
3094
+ return href ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("a", { className: "a24-source-link", href, rel: "noopener noreferrer", target: "_blank", children: part.title }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "a24-source-link", children: part.title });
2739
3095
  }
2740
3096
  if (part.kind === "citation") {
2741
3097
  const href = safeExternalUrl(part.url);
2742
3098
  const label = part.label || "Citation";
2743
- return href ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("a", { className: "a24-citation", href, rel: "noopener noreferrer", target: "_blank", children: label }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "a24-citation", children: label });
3099
+ return href ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("a", { className: "a24-citation", href, rel: "noopener noreferrer", target: "_blank", children: label }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "a24-citation", children: label });
2744
3100
  }
2745
- if (part.kind === "attachment") return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "a24-output-attachment", children: part.attachment.filename || "Attachment" });
2746
- if (part.kind === "data") return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { "data-agents24-data-part": part.name, className: "hidden", children: message.id });
3101
+ if (part.kind === "attachment") return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "a24-output-attachment", children: part.attachment.filename || "Attachment" });
3102
+ if (part.kind === "data") return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { "data-agents24-data-part": part.name, className: "hidden", children: message.id });
2747
3103
  return null;
2748
3104
  }
2749
3105
  function safeExternalUrl(value) {
@@ -2757,28 +3113,28 @@ function safeExternalUrl(value) {
2757
3113
  }
2758
3114
  function SafeUiBlocks({ bundle }) {
2759
3115
  const rows = Array.isArray(bundle.rows) ? bundle.rows : [];
2760
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("section", { "aria-label": typeof bundle.title === "string" ? bundle.title : "Agent result", className: "a24-ui-blocks", children: [
2761
- typeof bundle.title === "string" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h3", { children: bundle.title }) : null,
3116
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("section", { "aria-label": typeof bundle.title === "string" ? bundle.title : "Agent result", className: "a24-ui-blocks", children: [
3117
+ typeof bundle.title === "string" ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("h3", { children: bundle.title }) : null,
2762
3118
  rows.map((rawRow, rowIndex) => {
2763
3119
  const row = rawRow && typeof rawRow === "object" && !Array.isArray(rawRow) ? rawRow : {};
2764
3120
  const blocks = Array.isArray(row.blocks) ? row.blocks : [];
2765
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "a24-ui-blocks__row", children: blocks.map((rawBlock, blockIndex) => {
3121
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "a24-ui-blocks__row", children: blocks.map((rawBlock, blockIndex) => {
2766
3122
  const block = rawBlock && typeof rawBlock === "object" && !Array.isArray(rawBlock) ? rawBlock : {};
2767
3123
  const title = typeof block.title === "string" ? block.title : "Result";
2768
3124
  const kind = String(block.kind || "note");
2769
3125
  if (kind === "table" && Array.isArray(block.columns) && Array.isArray(block.rows)) {
2770
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "a24-ui-block", children: [
2771
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h4", { children: title }),
2772
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "a24-ui-table-scroll", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("table", { children: [
2773
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("tr", { children: block.columns.map((column, index) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("th", { children: String(column) }, index)) }) }),
2774
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("tbody", { children: block.rows.map((rawCells, index) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("tr", { children: (Array.isArray(rawCells) ? rawCells : []).map((cell, cellIndex) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("td", { children: String(cell) }, cellIndex)) }, index)) })
3126
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "a24-ui-block", children: [
3127
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("h4", { children: title }),
3128
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "a24-ui-table-scroll", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("table", { children: [
3129
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("tr", { children: block.columns.map((column, index) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("th", { children: String(column) }, index)) }) }),
3130
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("tbody", { children: block.rows.map((rawCells, index) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("tr", { children: (Array.isArray(rawCells) ? rawCells : []).map((cell, cellIndex) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("td", { children: String(cell) }, cellIndex)) }, index)) })
2775
3131
  ] }) })
2776
3132
  ] }, String(block.id || blockIndex));
2777
3133
  }
2778
3134
  const value = block.value ?? block.text ?? "";
2779
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "a24-ui-block", children: [
2780
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h4", { children: title }),
2781
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { children: String(value) })
3135
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "a24-ui-block", children: [
3136
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("h4", { children: title }),
3137
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { children: String(value) })
2782
3138
  ] }, String(block.id || blockIndex));
2783
3139
  }) }, `row-${rowIndex}`);
2784
3140
  })
@@ -2812,38 +3168,38 @@ function AgentResponseTimeline({
2812
3168
  if (segment.kind === "tool") {
2813
3169
  const isLatestActivity = segmentIndex === segments.length - 1;
2814
3170
  const showOrb = toolViewMode === "closed" ? isLoading && isLatestActivity : Boolean(activeToolId && (segment.part.id === activeToolId || segment.part.toolCallId === activeToolId));
2815
- rendered.push(/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(AgentChatToolRow, { activeToolId, part: segment.part, showOrb, slots: toolSlots }, segment.part.id));
3171
+ rendered.push(/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(AgentChatToolRow, { activeToolId, part: segment.part, showOrb, slots: toolSlots }, segment.part.id));
2816
3172
  continue;
2817
3173
  }
2818
3174
  if (segment.kind === "tool-session") {
2819
- rendered.push(/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(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}`));
3175
+ rendered.push(/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(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}`));
2820
3176
  continue;
2821
3177
  }
2822
3178
  if (segment.kind === "subagent") {
2823
- rendered.push(/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(AgentChatSubagentGroup, { isLatestActivity: segmentIndex === segments.length - 1, isLive: isLoading, part: segment.part, slots: toolSlots }, segment.part.id));
3179
+ rendered.push(/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(AgentChatSubagentGroup, { isLatestActivity: segmentIndex === segments.length - 1, isLive: isLoading, part: segment.part, slots: toolSlots }, segment.part.id));
2824
3180
  continue;
2825
3181
  }
2826
3182
  const part = segment.part;
2827
3183
  if (part.kind === "model-turn") {
2828
3184
  const key = activeActivityClaimed ? part.id : `${message.id}-active-activity`;
2829
3185
  activeActivityClaimed = true;
2830
- rendered.push(/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(AgentChatActivityRow, { label: "Planning next moves", modelTurnId: part.modelTurnId }, key));
3186
+ rendered.push(/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(AgentChatActivityRow, { renderActivityIcon: toolSlots?.renderActivityIcon, label: "Planning next moves", modelTurnId: part.modelTurnId }, key));
2831
3187
  continue;
2832
3188
  }
2833
3189
  if (part.kind === "node-activity") {
2834
3190
  const key = activeActivityClaimed ? part.id : `${message.id}-active-activity`;
2835
3191
  if (part.status === "running") activeActivityClaimed = true;
2836
3192
  rendered.push(
2837
- part.status === "running" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(AgentChatActivityRow, { label: part.label, onSelect: onNodeSelect ? () => onNodeSelect(part) : void 0 }, key) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(AgentChatNodeActivityRow, { onSelect: onNodeSelect, part }, part.id)
3193
+ part.status === "running" ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(AgentChatActivityRow, { renderActivityIcon: toolSlots?.renderActivityIcon, label: part.label, onSelect: onNodeSelect ? () => onNodeSelect(part) : void 0 }, key) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(AgentChatNodeActivityRow, { renderActivityIcon: toolSlots?.renderActivityIcon, onSelect: onNodeSelect, part }, part.id)
2838
3194
  );
2839
3195
  continue;
2840
3196
  }
2841
3197
  if (part.kind === "ui-blocks" && renderUiBlocks) {
2842
- rendered.push(/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(React9.Fragment, { children: renderUiBlocks(part) }, part.id));
3198
+ rendered.push(/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(React12.Fragment, { children: renderUiBlocks(part) }, part.id));
2843
3199
  continue;
2844
3200
  }
2845
3201
  rendered.push(
2846
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(React9.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3202
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(React12.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2847
3203
  AgentChatPart,
2848
3204
  {
2849
3205
  activeToolId,
@@ -2860,16 +3216,16 @@ function AgentResponseTimeline({
2860
3216
  );
2861
3217
  }
2862
3218
  if (rendered.length === 0 && message.content) {
2863
- rendered.push(/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(MessageResponse, { children: message.content }, `${message.id}-fallback`));
3219
+ rendered.push(/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(MessageResponse, { children: message.content }, `${message.id}-fallback`));
2864
3220
  }
2865
3221
  if (rendered.length === 0 && isLoading) {
2866
- rendered.push(/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(AgentChatActivityRow, { label: "Thinking" }, `${message.id}-active-activity`));
3222
+ rendered.push(/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(AgentChatActivityRow, { renderActivityIcon: toolSlots?.renderActivityIcon, label: "Thinking" }, `${message.id}-active-activity`));
2867
3223
  }
2868
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "space-y-3", children: rendered });
3224
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "space-y-3", children: rendered });
2869
3225
  }
2870
3226
 
2871
3227
  // src/ui/agent-chat-message.tsx
2872
- var import_jsx_runtime11 = require("react/jsx-runtime");
3228
+ var import_jsx_runtime12 = require("react/jsx-runtime");
2873
3229
  var DEFAULT_USER_MESSAGE_COLLAPSE_THRESHOLD = 360;
2874
3230
  var DEFAULT_USER_MESSAGE_COLLAPSED_LINES = 4;
2875
3231
  function shouldCollapseUserMessage(content, threshold = DEFAULT_USER_MESSAGE_COLLAPSE_THRESHOLD) {
@@ -2882,8 +3238,8 @@ function AgentChatUserMessageContent({
2882
3238
  showLessLabel = "Show less",
2883
3239
  showMoreLabel = "Show more"
2884
3240
  }) {
2885
- const [isExpanded, setIsExpanded] = React10.useState(false);
2886
- const contentId = React10.useId();
3241
+ const [isExpanded, setIsExpanded] = React13.useState(false);
3242
+ const contentId = React13.useId();
2887
3243
  const shouldCollapse = shouldCollapseUserMessage(content, collapseThreshold);
2888
3244
  const Icon = isExpanded ? import_lucide_react7.ChevronUp : import_lucide_react7.ChevronDown;
2889
3245
  const clampedStyle = shouldCollapse && !isExpanded ? {
@@ -2892,9 +3248,9 @@ function AgentChatUserMessageContent({
2892
3248
  display: "-webkit-box",
2893
3249
  overflow: "hidden"
2894
3250
  } : void 0;
2895
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex min-w-0 flex-col gap-3", children: [
2896
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { id: contentId, className: "min-w-0", style: clampedStyle, children: content }),
2897
- shouldCollapse ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
3251
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex min-w-0 flex-col gap-3", children: [
3252
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { id: contentId, className: "min-w-0", style: clampedStyle, children: content }),
3253
+ shouldCollapse ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
2898
3254
  "button",
2899
3255
  {
2900
3256
  "aria-controls": contentId,
@@ -2907,7 +3263,7 @@ function AgentChatUserMessageContent({
2907
3263
  type: "button",
2908
3264
  children: [
2909
3265
  isExpanded ? showLessLabel : showMoreLabel,
2910
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Icon, { "aria-hidden": "true", className: "size-4", strokeWidth: 2 })
3266
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Icon, { "aria-hidden": "true", className: "size-4", strokeWidth: 2 })
2911
3267
  ]
2912
3268
  }
2913
3269
  ) : null
@@ -2917,9 +3273,9 @@ function AgentChatUserMessageActionRow({
2917
3273
  children,
2918
3274
  className
2919
3275
  }) {
2920
- const rowRef = React10.useRef(null);
2921
- const [visible, setVisible] = React10.useState(false);
2922
- React10.useEffect(() => {
3276
+ const rowRef = React13.useRef(null);
3277
+ const [visible, setVisible] = React13.useState(false);
3278
+ React13.useEffect(() => {
2923
3279
  const message = rowRef.current?.closest('[data-slot="message"]');
2924
3280
  if (!message) return;
2925
3281
  const show = () => setVisible(true);
@@ -2938,7 +3294,7 @@ function AgentChatUserMessageActionRow({
2938
3294
  message.removeEventListener("focusout", hideWhenFocusLeaves);
2939
3295
  };
2940
3296
  }, []);
2941
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3297
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2942
3298
  "div",
2943
3299
  {
2944
3300
  ref: rowRef,
@@ -2977,7 +3333,7 @@ function AgentChatMessage({
2977
3333
  const resolvedUserContent = userMessageContent ?? message.content;
2978
3334
  const shouldRenderContent = isAssistant || Boolean(renderUserContent || resolvedUserContent);
2979
3335
  const showAttachments = Boolean(message.attachments?.length) && (message.role === "user" || showAssistantAttachments);
2980
- const renderedActions = actions ?? (actionHandlers ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3336
+ const renderedActions = actions ?? (actionHandlers ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2981
3337
  AgentChatDefaultActions,
2982
3338
  {
2983
3339
  additionalActions,
@@ -2987,14 +3343,14 @@ function AgentChatMessage({
2987
3343
  }
2988
3344
  ) : null);
2989
3345
  const defaultUserActions = !actions && Boolean(actionHandlers) && !isAssistant;
2990
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
3346
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
2991
3347
  ChatMessage,
2992
3348
  {
2993
3349
  className,
2994
3350
  dir,
2995
3351
  from: message.role,
2996
3352
  children: [
2997
- showAttachments ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3353
+ showAttachments ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2998
3354
  ChatAttachmentRows,
2999
3355
  {
3000
3356
  attachments: message.attachments || [],
@@ -3003,14 +3359,14 @@ function AgentChatMessage({
3003
3359
  resolveContent: attachmentContentResolver
3004
3360
  }
3005
3361
  ) : null,
3006
- shouldRenderContent ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
3362
+ shouldRenderContent ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
3007
3363
  ChatMessageContent,
3008
3364
  {
3009
3365
  className: cn(isAssistant ? "bg-transparent p-0" : void 0, contentClassName),
3010
3366
  dir,
3011
3367
  from: message.role,
3012
3368
  children: [
3013
- isAssistant ? renderAssistantContent ? renderAssistantContent({ isStreaming, message }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3369
+ isAssistant ? renderAssistantContent ? renderAssistantContent({ isStreaming, message }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3014
3370
  Timeline,
3015
3371
  {
3016
3372
  getHitlActionState,
@@ -3021,7 +3377,7 @@ function AgentChatMessage({
3021
3377
  parts: message.parts
3022
3378
  }
3023
3379
  ) : null,
3024
- !isAssistant ? renderUserContent ? renderUserContent({ message }) : resolvedUserContent ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3380
+ !isAssistant ? renderUserContent ? renderUserContent({ message }) : resolvedUserContent ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3025
3381
  AgentChatUserMessageContent,
3026
3382
  {
3027
3383
  collapsedLines: userMessageCollapsedLines,
@@ -3034,108 +3390,7 @@ function AgentChatMessage({
3034
3390
  ]
3035
3391
  }
3036
3392
  ) : null,
3037
- renderedActions ? defaultUserActions ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(AgentChatUserMessageActionRow, { className: actionsClassName, children: renderedActions }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: cn(dir === "rtl" ? "mt-1 h-9 w-full" : "mt-1 h-9 w-full -translate-x-2", actionsClassName), children: renderedActions }) : null
3038
- ]
3039
- }
3040
- );
3041
- }
3042
-
3043
- // src/ui/agent-chat-context-status.tsx
3044
- var React11 = __toESM(require("react"), 1);
3045
- var import_jsx_runtime12 = require("react/jsx-runtime");
3046
- var COMPACT_TOKENS = new Intl.NumberFormat("en-US", { notation: "compact" });
3047
- var PERCENT = new Intl.NumberFormat("en-US", {
3048
- style: "percent",
3049
- maximumFractionDigits: 1
3050
- });
3051
- var EXACT_SOURCES = /* @__PURE__ */ new Set(["exact", "provider_usage"]);
3052
- function contextStatusMetrics(contextWindow) {
3053
- const maximum = Number(contextWindow?.max_tokens || 0);
3054
- const input = Math.max(0, Number(contextWindow?.input_tokens || 0));
3055
- const admissionInput = Math.max(0, Number(contextWindow?.admission_input_tokens ?? input));
3056
- const usageRatio = Math.max(0, Math.min(1, Number(
3057
- contextWindow?.usage_ratio ?? input / maximum
3058
- )));
3059
- const admissionRatio = Math.max(0, Math.min(1, Number(
3060
- contextWindow?.admission_ratio ?? admissionInput / maximum
3061
- )));
3062
- return { maximum, input, admissionInput, usageRatio, admissionRatio };
3063
- }
3064
- function AgentChatContextStatus({
3065
- className,
3066
- contextWindow
3067
- }) {
3068
- const tooltipId = React11.useId();
3069
- const [open, setOpen] = React11.useState(false);
3070
- const { maximum, input, admissionInput, usageRatio, admissionRatio } = contextStatusMetrics(contextWindow);
3071
- const percentage = Math.round(usageRatio * 100);
3072
- const exact = EXACT_SOURCES.has(contextWindow?.source || "unknown");
3073
- if (!Number.isFinite(maximum) || maximum <= 0) return null;
3074
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
3075
- "div",
3076
- {
3077
- className: cn("a24-context-status", className),
3078
- onBlurCapture: (event) => {
3079
- if (!event.currentTarget.contains(event.relatedTarget)) setOpen(false);
3080
- },
3081
- onFocusCapture: () => setOpen(true),
3082
- onMouseEnter: () => setOpen(true),
3083
- onMouseLeave: () => setOpen(false),
3084
- children: [
3085
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
3086
- "button",
3087
- {
3088
- "aria-describedby": open ? tooltipId : void 0,
3089
- "aria-label": `Context window ${percentage}% used`,
3090
- className: "a24-context-status__trigger",
3091
- onKeyDown: (event) => {
3092
- if (event.key === "Escape") setOpen(false);
3093
- },
3094
- type: "button",
3095
- children: [
3096
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("svg", { "aria-hidden": "true", className: "a24-context-status__ring", viewBox: "0 0 24 24", children: [
3097
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("circle", { className: "a24-context-status__track", cx: "12", cy: "12", r: "10" }),
3098
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3099
- "circle",
3100
- {
3101
- className: "a24-context-status__value",
3102
- cx: "12",
3103
- cy: "12",
3104
- pathLength: "100",
3105
- r: "10",
3106
- strokeDasharray: `${percentage} 100`
3107
- }
3108
- )
3109
- ] }),
3110
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { children: [
3111
- percentage,
3112
- "%"
3113
- ] })
3114
- ]
3115
- }
3116
- ),
3117
- open ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "a24-context-status__popover", id: tooltipId, role: "tooltip", children: [
3118
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "a24-context-status__sr-only", children: "Context window" }),
3119
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("strong", { children: [
3120
- PERCENT.format(usageRatio),
3121
- " context used"
3122
- ] }),
3123
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { children: [
3124
- exact ? "Input" : "Estimated input",
3125
- ": ",
3126
- COMPACT_TOKENS.format(input),
3127
- " / ",
3128
- COMPACT_TOKENS.format(maximum),
3129
- " tokens"
3130
- ] }),
3131
- admissionInput !== input ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { children: [
3132
- "Safety bound: ",
3133
- COMPACT_TOKENS.format(admissionInput),
3134
- " tokens (",
3135
- PERCENT.format(admissionRatio),
3136
- ")"
3137
- ] }) : null
3138
- ] }) : null
3393
+ renderedActions ? defaultUserActions ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(AgentChatUserMessageActionRow, { className: actionsClassName, children: renderedActions }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: cn(dir === "rtl" ? "mt-1 h-9 w-full" : "mt-1 h-9 w-full -translate-x-2", actionsClassName), children: renderedActions }) : null
3139
3394
  ]
3140
3395
  }
3141
3396
  );
@@ -3365,68 +3620,6 @@ function McpConnectRequiredCard({
3365
3620
  }
3366
3621
  );
3367
3622
  }
3368
-
3369
- // src/ui/use-audio-recorder.ts
3370
- var React12 = __toESM(require("react"), 1);
3371
- var RECORDING_CANDIDATES = ["audio/webm;codecs=opus", "audio/webm", "audio/mp4"];
3372
- function recordingExtension(mimeType) {
3373
- return mimeType.startsWith("audio/mp4") ? "m4a" : "webm";
3374
- }
3375
- function useAudioRecorder(onRecorded, options = {}) {
3376
- const recorderRef = React12.useRef(null);
3377
- const streamRef = React12.useRef(null);
3378
- const onRecordedRef = React12.useRef(onRecorded);
3379
- const onStreamChangeRef = React12.useRef(options.onStreamChange);
3380
- onRecordedRef.current = onRecorded;
3381
- onStreamChangeRef.current = options.onStreamChange;
3382
- const [isRecording, setIsRecording] = React12.useState(false);
3383
- const [error, setError] = React12.useState(null);
3384
- const stop = React12.useCallback(() => recorderRef.current?.stop(), []);
3385
- const start = React12.useCallback(async () => {
3386
- setError(null);
3387
- if (!navigator.mediaDevices?.getUserMedia || typeof MediaRecorder === "undefined") {
3388
- setError("Audio recording is not supported in this browser.");
3389
- return;
3390
- }
3391
- try {
3392
- const mimeType = RECORDING_CANDIDATES.find((candidate) => MediaRecorder.isTypeSupported(candidate));
3393
- if (!mimeType) {
3394
- setError("Audio recording is not supported in this browser.");
3395
- return;
3396
- }
3397
- const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
3398
- onStreamChangeRef.current?.(stream);
3399
- const recorder = new MediaRecorder(stream, { mimeType });
3400
- const chunks = [];
3401
- recorder.ondataavailable = (event) => {
3402
- if (event.data.size) chunks.push(event.data);
3403
- };
3404
- recorder.onstop = () => {
3405
- const actualType = recorder.mimeType || "audio/webm";
3406
- onRecordedRef.current(new File([new Blob(chunks, { type: actualType })], `recording-${Date.now()}.${recordingExtension(actualType)}`, { type: actualType }));
3407
- stream.getTracks().forEach((track) => track.stop());
3408
- onStreamChangeRef.current?.(null);
3409
- recorderRef.current = null;
3410
- streamRef.current = null;
3411
- setIsRecording(false);
3412
- };
3413
- recorderRef.current = recorder;
3414
- streamRef.current = stream;
3415
- recorder.start();
3416
- setIsRecording(true);
3417
- } catch {
3418
- streamRef.current?.getTracks().forEach((track) => track.stop());
3419
- onStreamChangeRef.current?.(null);
3420
- setError("Microphone access is unavailable.");
3421
- }
3422
- }, []);
3423
- React12.useEffect(() => () => {
3424
- recorderRef.current?.stop();
3425
- streamRef.current?.getTracks().forEach((track) => track.stop());
3426
- onStreamChangeRef.current?.(null);
3427
- }, []);
3428
- return { error, isRecording, start, stop };
3429
- }
3430
3623
  // Annotate the CommonJS export names for ESM import in node:
3431
3624
  0 && (module.exports = {
3432
3625
  AgentChatActivityRow,
@@ -3505,6 +3698,7 @@ function useAudioRecorder(onRecorded, options = {}) {
3505
3698
  resolvedHitlLabel,
3506
3699
  revokeAgentChatComposerFiles,
3507
3700
  shouldCollapseUserMessage,
3701
+ useAgentChatComposer,
3508
3702
  useAudioRecorder,
3509
3703
  useChatComposerFocus,
3510
3704
  useCompletionActionEntrance,