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