Fix double /reset in agent mode (#5516)
fix double /reset in agent mode Co-authored-by: Timothy Carambat <rambat1010@gmail.com>
This commit is contained in:
parent
ae9960b8ba
commit
5eb9842533
@ -7,7 +7,6 @@ import AddPresetModal from "./SlashPresets/AddPresetModal";
|
|||||||
import EditPresetModal from "./SlashPresets/EditPresetModal";
|
import EditPresetModal from "./SlashPresets/EditPresetModal";
|
||||||
import PublishEntityModal from "@/components/CommunityHub/PublishEntityModal";
|
import PublishEntityModal from "@/components/CommunityHub/PublishEntityModal";
|
||||||
import showToast from "@/utils/toast";
|
import showToast from "@/utils/toast";
|
||||||
import { useIsAgentSessionActive } from "@/utils/chat/agent";
|
|
||||||
import { PROMPT_INPUT_EVENT } from "@/components/WorkspaceChat/ChatContainer/PromptInput";
|
import { PROMPT_INPUT_EVENT } from "@/components/WorkspaceChat/ChatContainer/PromptInput";
|
||||||
import useToolsMenuItems from "../../useToolsMenuItems";
|
import useToolsMenuItems from "../../useToolsMenuItems";
|
||||||
import SlashCommandRow from "./SlashCommandRow";
|
import SlashCommandRow from "./SlashCommandRow";
|
||||||
@ -20,7 +19,6 @@ export default function SlashCommandsTab({
|
|||||||
registerItemCount,
|
registerItemCount,
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const isActiveAgentSession = useIsAgentSessionActive();
|
|
||||||
const {
|
const {
|
||||||
isOpen: isAddModalOpen,
|
isOpen: isAddModalOpen,
|
||||||
openModal: openAddModal,
|
openModal: openAddModal,
|
||||||
@ -49,38 +47,31 @@ export default function SlashCommandsTab({
|
|||||||
setPresets(presets);
|
setPresets(presets);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Build the list of selectable items for keyboard navigation and rendering
|
// Build the list of selectable items for keyboard navigation and rendering.
|
||||||
// Command names must stay as static English strings since the backend
|
// /reset is a static English string since the backend matches it exactly.
|
||||||
// matches against exact "/reset" and "/exit" commands.
|
// During an agent session it ends the session AND clears the chat.
|
||||||
const items = useMemo(() => {
|
const items = useMemo(
|
||||||
const builtIn = isActiveAgentSession
|
() => [
|
||||||
? {
|
{
|
||||||
command: "/exit",
|
|
||||||
description: t("chat_window.preset_exit_description"),
|
|
||||||
autoSubmit: true,
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
command: "/reset",
|
command: "/reset",
|
||||||
description: t("chat_window.preset_reset_description"),
|
description: t("chat_window.preset_reset_description"),
|
||||||
autoSubmit: true,
|
autoSubmit: true,
|
||||||
};
|
},
|
||||||
|
|
||||||
return [
|
|
||||||
builtIn,
|
|
||||||
...presets.map((preset) => ({
|
...presets.map((preset) => ({
|
||||||
command: preset.command,
|
command: preset.command,
|
||||||
description: preset.description,
|
description: preset.description,
|
||||||
autoSubmit: false,
|
autoSubmit: false,
|
||||||
preset,
|
preset,
|
||||||
})),
|
})),
|
||||||
];
|
],
|
||||||
}, [isActiveAgentSession, presets]);
|
[presets, t]
|
||||||
|
);
|
||||||
|
|
||||||
const handleUseCommand = useCallback(
|
const handleUseCommand = useCallback(
|
||||||
(command, autoSubmit = false) => {
|
(command, autoSubmit = false) => {
|
||||||
setShowing(false);
|
setShowing(false);
|
||||||
|
|
||||||
// Auto-submit commands (/reset, /exit) fire immediately
|
// Auto-submit commands (/reset) fire immediately
|
||||||
if (autoSubmit) {
|
if (autoSubmit) {
|
||||||
sendCommand({ text: command, autoSubmit: true });
|
sendCommand({ text: command, autoSubmit: true });
|
||||||
promptRef?.current?.focus();
|
promptRef?.current?.focus();
|
||||||
@ -189,7 +180,6 @@ export default function SlashCommandsTab({
|
|||||||
))}
|
))}
|
||||||
|
|
||||||
{/* Add new */}
|
{/* Add new */}
|
||||||
{!isActiveAgentSession && (
|
|
||||||
<div
|
<div
|
||||||
onClick={openAddModal}
|
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"
|
className="flex items-center gap-1.5 px-2 py-1 rounded cursor-pointer hover:bg-zinc-700/50 light:hover:bg-slate-100"
|
||||||
@ -203,7 +193,6 @@ export default function SlashCommandsTab({
|
|||||||
{t("chat_window.add_new")}
|
{t("chat_window.add_new")}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Modals */}
|
{/* Modals */}
|
||||||
<AddPresetModal
|
<AddPresetModal
|
||||||
|
|||||||
@ -46,6 +46,7 @@ export default function ChatContainer({ workspace, knownHistory = [] }) {
|
|||||||
const { files, parseAttachments } = useContext(DndUploaderContext);
|
const { files, parseAttachments } = useContext(DndUploaderContext);
|
||||||
const { chatHistoryRef } = useChatContainerQuickScroll();
|
const { chatHistoryRef } = useChatContainerQuickScroll();
|
||||||
const pendingMessageChecked = useRef(false);
|
const pendingMessageChecked = useRef(false);
|
||||||
|
const pendingResetRef = useRef(false);
|
||||||
|
|
||||||
const { listening, resetTranscript } = useSpeechRecognition({
|
const { listening, resetTranscript } = useSpeechRecognition({
|
||||||
clearTranscriptOnListen: true,
|
clearTranscriptOnListen: true,
|
||||||
@ -240,7 +241,13 @@ export default function ChatContainer({ workspace, knownHistory = [] }) {
|
|||||||
attachments,
|
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;
|
if (!promptMessage || !promptMessage?.userMessage) return false;
|
||||||
@ -304,6 +311,11 @@ export default function ChatContainer({ workspace, knownHistory = [] }) {
|
|||||||
socket.addEventListener("close", (_event) => {
|
socket.addEventListener("close", (_event) => {
|
||||||
setAgentSessionActive(false);
|
setAgentSessionActive(false);
|
||||||
window.dispatchEvent(new CustomEvent(AGENT_SESSION_END));
|
window.dispatchEvent(new CustomEvent(AGENT_SESSION_END));
|
||||||
|
// 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) => [
|
setChatHistory((prev) => [
|
||||||
...prev.filter((msg) => !!msg.content),
|
...prev.filter((msg) => !!msg.content),
|
||||||
{
|
{
|
||||||
@ -318,6 +330,7 @@ export default function ChatContainer({ workspace, knownHistory = [] }) {
|
|||||||
pending: false,
|
pending: false,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
}
|
||||||
setLoadingResponse(false);
|
setLoadingResponse(false);
|
||||||
setWebsocket(null);
|
setWebsocket(null);
|
||||||
setSocketId(null);
|
setSocketId(null);
|
||||||
|
|||||||
@ -12,6 +12,12 @@ const chatHistory = {
|
|||||||
return {
|
return {
|
||||||
name: this.name,
|
name: this.name,
|
||||||
setup: function (aibitat) {
|
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
|
// pre-register a workspace chat ID to secure it in the DB
|
||||||
aibitat.onMessage(async (message) => {
|
aibitat.onMessage(async (message) => {
|
||||||
if (message.from !== "USER") return;
|
if (message.from !== "USER") return;
|
||||||
@ -54,6 +60,7 @@ const chatHistory = {
|
|||||||
|
|
||||||
aibitat.onMessage(async () => {
|
aibitat.onMessage(async () => {
|
||||||
try {
|
try {
|
||||||
|
if (aibitat._aborted) return;
|
||||||
const lastResponses = aibitat.chats.slice(-2);
|
const lastResponses = aibitat.chats.slice(-2);
|
||||||
if (lastResponses.length !== 2) return;
|
if (lastResponses.length !== 2) return;
|
||||||
const [prev, last] = lastResponses;
|
const [prev, last] = lastResponses;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user