mirror of
https://github.com/Mintplex-Labs/anything-llm
synced 2026-04-25 17:15:37 +02:00
fix double /reset in agent mode
This commit is contained in:
@@ -7,7 +7,6 @@ import AddPresetModal from "./SlashPresets/AddPresetModal";
|
||||
import EditPresetModal from "./SlashPresets/EditPresetModal";
|
||||
import PublishEntityModal from "@/components/CommunityHub/PublishEntityModal";
|
||||
import showToast from "@/utils/toast";
|
||||
import { useIsAgentSessionActive } from "@/utils/chat/agent";
|
||||
import { PROMPT_INPUT_EVENT } from "@/components/WorkspaceChat/ChatContainer/PromptInput";
|
||||
import useToolsMenuItems from "../../useToolsMenuItems";
|
||||
import SlashCommandRow from "./SlashCommandRow";
|
||||
@@ -20,7 +19,6 @@ export default function SlashCommandsTab({
|
||||
registerItemCount,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const isActiveAgentSession = useIsAgentSessionActive();
|
||||
const {
|
||||
isOpen: isAddModalOpen,
|
||||
openModal: openAddModal,
|
||||
@@ -49,38 +47,31 @@ export default function SlashCommandsTab({
|
||||
setPresets(presets);
|
||||
};
|
||||
|
||||
// Build the list of selectable items for keyboard navigation and rendering
|
||||
// Command names must stay as static English strings since the backend
|
||||
// matches against exact "/reset" and "/exit" commands.
|
||||
const items = useMemo(() => {
|
||||
const builtIn = isActiveAgentSession
|
||||
? {
|
||||
command: "/exit",
|
||||
description: t("chat_window.preset_exit_description"),
|
||||
autoSubmit: true,
|
||||
}
|
||||
: {
|
||||
command: "/reset",
|
||||
description: t("chat_window.preset_reset_description"),
|
||||
autoSubmit: true,
|
||||
};
|
||||
|
||||
return [
|
||||
builtIn,
|
||||
// Build the list of selectable items for keyboard navigation and rendering.
|
||||
// /reset is a static English string since the backend matches it exactly.
|
||||
// During an agent session it ends the session AND clears the chat.
|
||||
const items = useMemo(
|
||||
() => [
|
||||
{
|
||||
command: "/reset",
|
||||
description: t("chat_window.preset_reset_description"),
|
||||
autoSubmit: true,
|
||||
},
|
||||
...presets.map((preset) => ({
|
||||
command: preset.command,
|
||||
description: preset.description,
|
||||
autoSubmit: false,
|
||||
preset,
|
||||
})),
|
||||
];
|
||||
}, [isActiveAgentSession, presets]);
|
||||
],
|
||||
[presets, t]
|
||||
);
|
||||
|
||||
const handleUseCommand = useCallback(
|
||||
(command, autoSubmit = false) => {
|
||||
setShowing(false);
|
||||
|
||||
// Auto-submit commands (/reset, /exit) fire immediately
|
||||
// Auto-submit commands (/reset) fire immediately
|
||||
if (autoSubmit) {
|
||||
sendCommand({ text: command, autoSubmit: true });
|
||||
promptRef?.current?.focus();
|
||||
@@ -189,21 +180,19 @@ export default function SlashCommandsTab({
|
||||
))}
|
||||
|
||||
{/* Add new */}
|
||||
{!isActiveAgentSession && (
|
||||
<div
|
||||
onClick={openAddModal}
|
||||
className="flex items-center gap-1.5 px-2 py-1 rounded cursor-pointer hover:bg-zinc-700/50 light:hover:bg-slate-100"
|
||||
>
|
||||
<Plus
|
||||
size={12}
|
||||
weight="bold"
|
||||
className="text-white light:text-slate-900"
|
||||
/>
|
||||
<span className="text-xs text-white light:text-slate-900">
|
||||
{t("chat_window.add_new")}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
onClick={openAddModal}
|
||||
className="flex items-center gap-1.5 px-2 py-1 rounded cursor-pointer hover:bg-zinc-700/50 light:hover:bg-slate-100"
|
||||
>
|
||||
<Plus
|
||||
size={12}
|
||||
weight="bold"
|
||||
className="text-white light:text-slate-900"
|
||||
/>
|
||||
<span className="text-xs text-white light:text-slate-900">
|
||||
{t("chat_window.add_new")}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Modals */}
|
||||
<AddPresetModal
|
||||
|
||||
@@ -46,6 +46,7 @@ export default function ChatContainer({ workspace, knownHistory = [] }) {
|
||||
const { files, parseAttachments } = useContext(DndUploaderContext);
|
||||
const { chatHistoryRef } = useChatContainerQuickScroll();
|
||||
const pendingMessageChecked = useRef(false);
|
||||
const pendingResetRef = useRef(false);
|
||||
|
||||
const { listening, resetTranscript } = useSpeechRecognition({
|
||||
clearTranscriptOnListen: true,
|
||||
@@ -240,7 +241,13 @@ export default function ChatContainer({ workspace, knownHistory = [] }) {
|
||||
attachments,
|
||||
})
|
||||
);
|
||||
return;
|
||||
|
||||
// /reset during an active agent session should end the session AND
|
||||
// clear the chat in a single action. The send above triggers the
|
||||
// server to abort the agent and close the socket; fall through to the
|
||||
// /reset flow below which resets memory + clears chat history.
|
||||
if (promptMessage.userMessage.trim() !== "/reset") return;
|
||||
pendingResetRef.current = true;
|
||||
}
|
||||
|
||||
if (!promptMessage || !promptMessage?.userMessage) return false;
|
||||
@@ -304,20 +311,26 @@ export default function ChatContainer({ workspace, knownHistory = [] }) {
|
||||
socket.addEventListener("close", (_event) => {
|
||||
setAgentSessionActive(false);
|
||||
window.dispatchEvent(new CustomEvent(AGENT_SESSION_END));
|
||||
setChatHistory((prev) => [
|
||||
...prev.filter((msg) => !!msg.content),
|
||||
{
|
||||
uuid: v4(),
|
||||
type: "statusResponse",
|
||||
content: "Agent session complete.",
|
||||
role: "assistant",
|
||||
sources: [],
|
||||
closed: true,
|
||||
error: null,
|
||||
animate: false,
|
||||
pending: false,
|
||||
},
|
||||
]);
|
||||
// When the close was triggered by /reset, skip the "Agent session
|
||||
// complete." status - the pending /reset flow will clear history.
|
||||
if (pendingResetRef.current) {
|
||||
pendingResetRef.current = false;
|
||||
} else {
|
||||
setChatHistory((prev) => [
|
||||
...prev.filter((msg) => !!msg.content),
|
||||
{
|
||||
uuid: v4(),
|
||||
type: "statusResponse",
|
||||
content: "Agent session complete.",
|
||||
role: "assistant",
|
||||
sources: [],
|
||||
closed: true,
|
||||
error: null,
|
||||
animate: false,
|
||||
pending: false,
|
||||
},
|
||||
]);
|
||||
}
|
||||
setLoadingResponse(false);
|
||||
setWebsocket(null);
|
||||
setSocketId(null);
|
||||
|
||||
@@ -12,6 +12,12 @@ const chatHistory = {
|
||||
return {
|
||||
name: this.name,
|
||||
setup: function (aibitat) {
|
||||
// If the agent is aborted (e.g. user sent /reset mid-response), skip
|
||||
// the pending save so a completing in-flight response doesn't reappear.
|
||||
aibitat.onAbort(() => {
|
||||
aibitat._aborted = true;
|
||||
});
|
||||
|
||||
// pre-register a workspace chat ID to secure it in the DB
|
||||
aibitat.onMessage(async (message) => {
|
||||
if (message.from !== "USER") return;
|
||||
@@ -54,6 +60,7 @@ const chatHistory = {
|
||||
|
||||
aibitat.onMessage(async () => {
|
||||
try {
|
||||
if (aibitat._aborted) return;
|
||||
const lastResponses = aibitat.chats.slice(-2);
|
||||
if (lastResponses.length !== 2) return;
|
||||
const [prev, last] = lastResponses;
|
||||
|
||||
Reference in New Issue
Block a user