@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.
- package/README.md +28 -1
- package/dist/chunk-INO5DSSJ.js +86 -0
- package/dist/chunk-INO5DSSJ.js.map +1 -0
- package/dist/generated/delegation-types.d.ts +129 -0
- package/dist/registry/agents24-chat.json +267 -0
- package/dist/runtime/index.cjs +53 -1
- package/dist/runtime/index.cjs.map +1 -1
- package/dist/runtime/index.d.ts +2 -0
- package/dist/runtime/index.js +62 -78
- package/dist/runtime/index.js.map +1 -1
- package/dist/runtime/input-contract.d.ts +3 -1
- package/dist/runtime/use-delegation-tasks.d.ts +20 -0
- package/dist/scaffold/app.d.ts +3 -0
- package/dist/scaffold/components/chat/chat-composer.d.ts +5 -3
- package/dist/scaffold/components/chat/subagent-tasks.d.ts +3 -0
- package/dist/scaffold/components/chat/theme-menu.d.ts +1 -1
- package/dist/scaffold/components/theme-provider.d.ts +3 -1
- package/dist/scaffold/index.cjs +420 -333
- package/dist/scaffold/index.cjs.map +1 -1
- package/dist/scaffold/index.d.ts +5 -2
- package/dist/scaffold/index.js +415 -332
- package/dist/scaffold/index.js.map +1 -1
- package/dist/styles.css +14 -0
- package/dist/ui/adapters.d.ts +5 -2
- package/dist/ui/agent-chat-composer.d.ts +8 -2
- package/dist/ui/agent-chat-queue.d.ts +0 -2
- package/dist/ui/index.cjs +817 -623
- package/dist/ui/index.cjs.map +1 -1
- package/dist/ui/index.d.ts +1 -0
- package/dist/ui/index.js +776 -622
- package/dist/ui/index.js.map +1 -1
- package/dist/ui/tool-sessions.d.ts +10 -2
- package/dist/ui/use-agent-chat-composer.d.ts +47 -0
- package/dist/ui/use-audio-recorder.d.ts +5 -3
- package/package.json +11 -6
- package/scaffold/host-styles.css +3 -0
- package/scaffold/inherit.css +4 -0
- package/scaffold/manifest.json +2 -2
- package/scaffold/src/app.tsx +5 -0
- package/scaffold/src/chat.css +35 -1
- package/scaffold/src/components/chat/chat-composer.tsx +31 -97
- package/scaffold/src/components/chat/chat-shell.tsx +7 -5
- package/scaffold/src/components/chat/external-link-dialog.tsx +6 -5
- package/scaffold/src/components/chat/identity-control.tsx +0 -1
- package/scaffold/src/components/chat/subagent-tasks.tsx +109 -0
- package/scaffold/src/components/chat/theme-menu.tsx +3 -1
- package/scaffold/src/components/theme-provider.tsx +28 -23
- package/scaffold/src/components/ui/tooltip.tsx +3 -0
- package/scaffold/src/host.css +11 -0
- package/scaffold/src/index.css +1 -213
- package/scaffold/src/index.tsx +9 -2
- 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
|
|
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
|
|
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,
|
|
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/
|
|
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 =
|
|
915
|
-
const
|
|
916
|
-
const
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
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 =
|
|
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
|
-
|
|
935
|
-
filesRef.current = files;
|
|
1311
|
+
React6.useEffect(() => {
|
|
936
1312
|
onAttachmentCountChange?.(files.length);
|
|
937
1313
|
}, [files, onAttachmentCountChange]);
|
|
938
|
-
|
|
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
|
-
|
|
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
|
-
|
|
978
|
-
|
|
979
|
-
|
|
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:
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
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:
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
className:
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
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,
|
|
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": "
|
|
1081
|
-
className: cn("a24-
|
|
1082
|
-
disabled:
|
|
1083
|
-
onClick: () =>
|
|
1084
|
-
|
|
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,
|
|
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
|
-
|
|
1091
|
-
|
|
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,
|
|
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
|
|
1471
|
+
var React7 = __toESM(require("react"), 1);
|
|
1141
1472
|
var import_lucide_react3 = require("lucide-react");
|
|
1142
|
-
var
|
|
1473
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
1143
1474
|
function QueueRow({ message, onRemove, onEdit }) {
|
|
1144
|
-
const [busy, setBusy] =
|
|
1145
|
-
const [error, setError] =
|
|
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,
|
|
1159
|
-
/* @__PURE__ */ (0,
|
|
1160
|
-
/* @__PURE__ */ (0,
|
|
1161
|
-
/* @__PURE__ */ (0,
|
|
1162
|
-
message.status === "blocked" ? /* @__PURE__ */ (0,
|
|
1163
|
-
["steer_pending", "delivering"].includes(message.status) ? /* @__PURE__ */ (0,
|
|
1164
|
-
editable ? /* @__PURE__ */ (0,
|
|
1165
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
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,
|
|
1177
|
-
/* @__PURE__ */ (0,
|
|
1178
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
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] =
|
|
1526
|
+
const [resumeError, setResumeError] = React7.useState(null);
|
|
1196
1527
|
if (!pending.length && !queue?.paused) return null;
|
|
1197
|
-
return /* @__PURE__ */ (0,
|
|
1198
|
-
queue?.paused ? /* @__PURE__ */ (0,
|
|
1199
|
-
/* @__PURE__ */ (0,
|
|
1200
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
1205
|
-
/* @__PURE__ */ (0,
|
|
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
|
|
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
|
|
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 =
|
|
1225
|
-
|
|
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 =
|
|
1234
|
-
const entered =
|
|
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
|
-
|
|
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] =
|
|
1265
|
-
const [feedbackTrigger, setFeedbackTrigger] =
|
|
1266
|
-
const [reason, setReason] =
|
|
1267
|
-
const [comment, setComment] =
|
|
1268
|
-
const [optimisticCopied, setOptimisticCopied] =
|
|
1269
|
-
const copyResetTimer =
|
|
1270
|
-
|
|
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
|
-
|
|
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,
|
|
1292
|
-
sentAt ? /* @__PURE__ */ (0,
|
|
1293
|
-
handlers.onCopy ? /* @__PURE__ */ (0,
|
|
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,
|
|
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,
|
|
1307
|
-
handlers.onRetry ? /* @__PURE__ */ (0,
|
|
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,
|
|
1659
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react4.RefreshCcw, { className: "size-4" })
|
|
1314
1660
|
}
|
|
1315
1661
|
) : null,
|
|
1316
|
-
canWriteFeedback ? /* @__PURE__ */ (0,
|
|
1662
|
+
canWriteFeedback ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1317
1663
|
ChatMessageAction,
|
|
1318
1664
|
{
|
|
1319
1665
|
label: "Like",
|
|
1320
|
-
|
|
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,
|
|
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,
|
|
1328
|
-
/* @__PURE__ */ (0,
|
|
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
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
1358
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
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,
|
|
1381
|
-
/* @__PURE__ */ (0,
|
|
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
|
|
1735
|
+
void writeFeedback({ rating: null }, true);
|
|
1387
1736
|
},
|
|
1388
1737
|
type: "button",
|
|
1389
1738
|
children: "Remove feedback"
|
|
1390
1739
|
}
|
|
1391
1740
|
),
|
|
1392
|
-
/* @__PURE__ */ (0,
|
|
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
|
|
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,
|
|
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,
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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] =
|
|
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,
|
|
1693
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
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,
|
|
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,
|
|
1719
|
-
/* @__PURE__ */ (0,
|
|
1720
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
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] =
|
|
1742
|
-
const [drafts, setDrafts] =
|
|
2091
|
+
const [step, setStep] = React9.useState(0);
|
|
2092
|
+
const [drafts, setDrafts] = React9.useState(
|
|
1743
2093
|
() => emptyAskUserDrafts(questions)
|
|
1744
2094
|
);
|
|
1745
|
-
const headingRef =
|
|
1746
|
-
const questionsRef =
|
|
1747
|
-
const resolvedButtonRef =
|
|
1748
|
-
const wasResolvedRef =
|
|
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
|
-
|
|
2100
|
+
React9.useEffect(() => {
|
|
1751
2101
|
setStep(0);
|
|
1752
2102
|
setDrafts(emptyAskUserDrafts(questionsRef.current));
|
|
1753
2103
|
}, [part.interruptId]);
|
|
1754
|
-
|
|
2104
|
+
React9.useEffect(() => {
|
|
1755
2105
|
headingRef.current?.focus();
|
|
1756
2106
|
}, [step]);
|
|
1757
|
-
|
|
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,
|
|
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,
|
|
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,
|
|
1804
|
-
/* @__PURE__ */ (0,
|
|
1805
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
1815
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
1889
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
1902
|
-
/* @__PURE__ */ (0,
|
|
1903
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
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,
|
|
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
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
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
|
|
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,
|
|
2006
|
-
if (safeHref.startsWith("#")) return /* @__PURE__ */ (0,
|
|
2007
|
-
return /* @__PURE__ */ (0,
|
|
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,
|
|
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 (!
|
|
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
|
|
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 =
|
|
2399
|
+
const childArray = React10.Children.toArray(children).filter(
|
|
2050
2400
|
(child) => child !== null && child !== ""
|
|
2051
2401
|
);
|
|
2052
|
-
const isImageOnly = childArray.length === 1 &&
|
|
2402
|
+
const isImageOnly = childArray.length === 1 && React10.isValidElement(childArray[0]) && childArray[0].props.node?.tagName === "img";
|
|
2053
2403
|
if (isImageOnly) {
|
|
2054
|
-
return /* @__PURE__ */ (0,
|
|
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,
|
|
2407
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { ...domProps, children });
|
|
2058
2408
|
}
|
|
2059
|
-
return /* @__PURE__ */ (0,
|
|
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 =
|
|
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 =
|
|
2447
|
+
const streamdownComponents = React10.useMemo(
|
|
2098
2448
|
() => ({ ...MESSAGE_RESPONSE_COMPONENTS, ...components }),
|
|
2099
2449
|
[components]
|
|
2100
2450
|
);
|
|
2101
|
-
return /* @__PURE__ */ (0,
|
|
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
|
|
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
|
|
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,
|
|
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,
|
|
2189
|
-
slots?.onResourceAction ? /* @__PURE__ */ (0,
|
|
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] =
|
|
2267
|
-
const [previous, setPrevious] =
|
|
2268
|
-
const displayedRef =
|
|
2269
|
-
const latestRef =
|
|
2270
|
-
const shownAtRef =
|
|
2271
|
-
|
|
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
|
-
|
|
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,
|
|
2297
|
-
previous ? /* @__PURE__ */ (0,
|
|
2298
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
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,
|
|
2310
|
-
/* @__PURE__ */ (0,
|
|
2311
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
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,
|
|
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] =
|
|
2323
|
-
const contentId =
|
|
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,
|
|
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,
|
|
2337
|
-
running ? /* @__PURE__ */ (0,
|
|
2338
|
-
/* @__PURE__ */ (0,
|
|
2339
|
-
] }) : /* @__PURE__ */ (0,
|
|
2340
|
-
running ? /* @__PURE__ */ (0,
|
|
2341
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
2345
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
2358
|
-
open ? /* @__PURE__ */ (0,
|
|
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,
|
|
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] =
|
|
2389
|
-
const contentId =
|
|
2390
|
-
const expandable =
|
|
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,
|
|
2394
|
-
onSelect && part ? /* @__PURE__ */ (0,
|
|
2395
|
-
showOrb ? /* @__PURE__ */ (0,
|
|
2396
|
-
/* @__PURE__ */ (0,
|
|
2397
|
-
] }) : /* @__PURE__ */ (0,
|
|
2398
|
-
showOrb ? /* @__PURE__ */ (0,
|
|
2399
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
2405
|
-
/* @__PURE__ */ (0,
|
|
2406
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
2421
|
-
/* @__PURE__ */ (0,
|
|
2422
|
-
open ? /* @__PURE__ */ (0,
|
|
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,
|
|
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,
|
|
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 =
|
|
2437
|
-
const [fades, setFades] =
|
|
2438
|
-
const updateFades =
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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 =
|
|
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,
|
|
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,
|
|
2524
|
-
return /* @__PURE__ */ (0,
|
|
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,
|
|
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 =
|
|
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,
|
|
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,
|
|
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,
|
|
2582
|
-
children.map((child) => /* @__PURE__ */ (0,
|
|
2583
|
-
/* @__PURE__ */ (0,
|
|
2584
|
-
child.detail ? /* @__PURE__ */ (0,
|
|
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,
|
|
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
|
|
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,
|
|
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,
|
|
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,
|
|
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] =
|
|
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 =
|
|
2625
|
-
const wasTerminal =
|
|
2626
|
-
|
|
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,
|
|
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,
|
|
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,
|
|
2652
|
-
/* @__PURE__ */ (0,
|
|
2653
|
-
requireComment ? /* @__PURE__ */ (0,
|
|
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,
|
|
2666
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
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,
|
|
2710
|
-
if (part.kind === "model-turn") return /* @__PURE__ */ (0,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
2746
|
-
if (part.kind === "data") return /* @__PURE__ */ (0,
|
|
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,
|
|
2761
|
-
typeof bundle.title === "string" ? /* @__PURE__ */ (0,
|
|
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,
|
|
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,
|
|
2771
|
-
/* @__PURE__ */ (0,
|
|
2772
|
-
/* @__PURE__ */ (0,
|
|
2773
|
-
/* @__PURE__ */ (0,
|
|
2774
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
2780
|
-
/* @__PURE__ */ (0,
|
|
2781
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
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] =
|
|
2886
|
-
const contentId =
|
|
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,
|
|
2896
|
-
/* @__PURE__ */ (0,
|
|
2897
|
-
shouldCollapse ? /* @__PURE__ */ (0,
|
|
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,
|
|
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 =
|
|
2921
|
-
const [visible, setVisible] =
|
|
2922
|
-
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|