diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/ToolsMenu/Tabs/SlashCommands/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/ToolsMenu/Tabs/SlashCommands/index.jsx index ab10ad0b7..9c652372d 100644 --- a/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/ToolsMenu/Tabs/SlashCommands/index.jsx +++ b/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/ToolsMenu/Tabs/SlashCommands/index.jsx @@ -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 && ( -
- - - {t("chat_window.add_new")} - -
- )} +
+ + + {t("chat_window.add_new")} + +
{/* Modals */} { 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); diff --git a/server/utils/agents/aibitat/plugins/chat-history.js b/server/utils/agents/aibitat/plugins/chat-history.js index 647aef1b7..203598cb2 100644 --- a/server/utils/agents/aibitat/plugins/chat-history.js +++ b/server/utils/agents/aibitat/plugins/chat-history.js @@ -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;