From 27222ec6c7183f83f1ea556a09d8509f4b838677 Mon Sep 17 00:00:00 2001 From: puzhen <1303385763@qq.com> Date: Fri, 21 Nov 2025 01:44:35 +0800 Subject: [PATCH 1/7] update --- src/components/AddWorker/ToolSelect.tsx | 31 +++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/src/components/AddWorker/ToolSelect.tsx b/src/components/AddWorker/ToolSelect.tsx index 0b29ebd66..92922f92e 100644 --- a/src/components/AddWorker/ToolSelect.tsx +++ b/src/components/AddWorker/ToolSelect.tsx @@ -56,7 +56,7 @@ const ToolSelect = forwardRef< const [integrations, setIntegrations] = useState([]); const fetchIntegrationsData = (keyword?: string) => { proxyFetchGet("/api/config/info").then((res) => { - if (res && typeof res === "object") { + if (res && typeof res === "object" && !res.error) { const baseURL = getProxyBaseURL(); const list = Object.entries(res) @@ -187,7 +187,13 @@ const ToolSelect = forwardRef< }; }); setIntegrations(list); + } else { + console.error("Failed to fetch integrations:", res); + setIntegrations([]); } + }).catch((error) => { + console.error("Error fetching integrations:", error); + setIntegrations([]); }); }; @@ -217,7 +223,16 @@ const ToolSelect = forwardRef< page: 1, size: 100, }).then((res) => { - setAllMcpList(res.items); + // Add defensive check for API errors + if (res && res.items && Array.isArray(res.items)) { + setAllMcpList(res.items); + } else { + console.error("Failed to fetch MCPs:", res); + setAllMcpList([]); + } + }).catch((error) => { + console.error("Error fetching MCPs:", error); + setAllMcpList([]); }); }; @@ -228,7 +243,7 @@ const ToolSelect = forwardRef< if (Array.isArray(res)) { ids = res.map((item: any) => item.mcp_id); dataList = res; - } else if (Array.isArray(res.items)) { + } else if (res && Array.isArray(res.items)) { ids = res.items.map((item: any) => item.mcp_id); dataList = res.items; } @@ -236,14 +251,22 @@ const ToolSelect = forwardRef< const customMcpList = dataList.filter((item: any) => item.mcp_id === 0); setCustomMcpList(customMcpList); + }).catch((error) => { + console.error("Error fetching installed MCPs:", error); + setInstalledIds([]); + setCustomMcpList([]); }); }; // only surface installed MCPs from the market list useEffect(() => { - if (!installedIds.length) { + // Add defensive check and fix logic: should filter when installedIds has items + if (Array.isArray(allMcpList) && installedIds.length > 0) { const filtered = allMcpList.filter((item) => installedIds.includes(item.id)); setMcpList(filtered); + } else if (Array.isArray(allMcpList)) { + // If no installed IDs, show empty list instead of all + setMcpList([]); } }, [allMcpList, installedIds]); From c66c3b926ca72bd3f099f40d5ac58f62bf696bc6 Mon Sep 17 00:00:00 2001 From: LuoPengcheng <2653972504@qq.com> Date: Fri, 21 Nov 2025 01:44:49 +0800 Subject: [PATCH 2/7] feat: add-task hover --- .../GroupedHistoryView/ProjectDialog.tsx | 15 +++++++++------ .../GroupedHistoryView/ProjectGroup.tsx | 12 +++++++----- .../GroupedHistoryView/TaskItem.tsx | 6 ++++-- src/components/GroupedHistoryView/index.tsx | 19 +++++++++++-------- 4 files changed, 31 insertions(+), 21 deletions(-) diff --git a/src/components/GroupedHistoryView/ProjectDialog.tsx b/src/components/GroupedHistoryView/ProjectDialog.tsx index 55d635367..abfadd8b0 100644 --- a/src/components/GroupedHistoryView/ProjectDialog.tsx +++ b/src/components/GroupedHistoryView/ProjectDialog.tsx @@ -20,6 +20,7 @@ import { LoaderCircle, } from "lucide-react"; import { useProjectStore } from "@/store/projectStore"; +import { TooltipSimple } from "@/components/ui/tooltip"; interface ProjectDialogProps { open: boolean; @@ -182,12 +183,14 @@ export default function ProjectDialog({ {t("layout.total-tasks")} -
- - - {project.task_count} - -
+ +
+ + + {project.task_count} + +
+
diff --git a/src/components/GroupedHistoryView/ProjectGroup.tsx b/src/components/GroupedHistoryView/ProjectGroup.tsx index 4418d9a76..5c02016fb 100644 --- a/src/components/GroupedHistoryView/ProjectGroup.tsx +++ b/src/components/GroupedHistoryView/ProjectGroup.tsx @@ -335,10 +335,12 @@ export default function ProjectGroup({ {project.total_tokens ? project.total_tokens.toLocaleString() : "0"} - - - {project.task_count} - + + + + {project.task_count} + +
{/* End: Status and menu */} @@ -401,4 +403,4 @@ export default function ProjectGroup({ /> ); -} \ No newline at end of file +} diff --git a/src/components/GroupedHistoryView/TaskItem.tsx b/src/components/GroupedHistoryView/TaskItem.tsx index e93f7a89a..8d92a7add 100644 --- a/src/components/GroupedHistoryView/TaskItem.tsx +++ b/src/components/GroupedHistoryView/TaskItem.tsx @@ -88,7 +88,9 @@ export default function TaskItem({ `} >
- + + +
); -} \ No newline at end of file +} diff --git a/src/components/GroupedHistoryView/index.tsx b/src/components/GroupedHistoryView/index.tsx index 9f9264357..f5e1fd734 100644 --- a/src/components/GroupedHistoryView/index.tsx +++ b/src/components/GroupedHistoryView/index.tsx @@ -6,6 +6,7 @@ import ProjectGroup from "./ProjectGroup"; import { useTranslation } from "react-i18next"; import { Loader2, FolderOpen, Pin, Hash, LayoutGrid, List, Sparkles, Sparkle } from "lucide-react"; import { Tag } from "@/components/ui/tag"; +import { TooltipSimple } from "@/components/ui/tooltip"; import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { useGlobalStore } from "@/store/globalStore"; import { proxyFetchDelete, proxyFetchPut } from "@/api/http"; @@ -264,13 +265,15 @@ export default function GroupedHistoryView({ - - - {t("layout.total-tasks")} - - {allProjects.reduce((total, project) => total + project.task_count, 0)} - - + + + + {t("layout.total-tasks")} + + {allProjects.reduce((total, project) => total + project.task_count, 0)} + + +
); -} \ No newline at end of file +} From 137d5845552aca3bca95a61e980df9587150dde3 Mon Sep 17 00:00:00 2001 From: LuoPengcheng <2653972504@qq.com> Date: Fri, 21 Nov 2025 01:55:32 +0800 Subject: [PATCH 3/7] feat: remove top hover --- src/components/GroupedHistoryView/index.tsx | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/components/GroupedHistoryView/index.tsx b/src/components/GroupedHistoryView/index.tsx index f5e1fd734..34c639963 100644 --- a/src/components/GroupedHistoryView/index.tsx +++ b/src/components/GroupedHistoryView/index.tsx @@ -6,7 +6,6 @@ import ProjectGroup from "./ProjectGroup"; import { useTranslation } from "react-i18next"; import { Loader2, FolderOpen, Pin, Hash, LayoutGrid, List, Sparkles, Sparkle } from "lucide-react"; import { Tag } from "@/components/ui/tag"; -import { TooltipSimple } from "@/components/ui/tooltip"; import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { useGlobalStore } from "@/store/globalStore"; import { proxyFetchDelete, proxyFetchPut } from "@/api/http"; @@ -265,15 +264,13 @@ export default function GroupedHistoryView({ - - - - {t("layout.total-tasks")} - - {allProjects.reduce((total, project) => total + project.task_count, 0)} - - - + + + {t("layout.total-tasks")} + + {allProjects.reduce((total, project) => total + project.task_count, 0)} + +
Date: Fri, 21 Nov 2025 01:59:24 +0800 Subject: [PATCH 4/7] Revert "fix: stop task not working as expected (#713)" This reverts commit bfcc50046901269ec46a2564902ad89727416514, reversing changes made to 2bf89fd27df2b5d2a09cc454ec0a35d544c4ed6d. --- src/components/ChatBox/index.tsx | 43 +++------ src/store/chatStore.ts | 155 ++----------------------------- 2 files changed, 20 insertions(+), 178 deletions(-) diff --git a/src/components/ChatBox/index.tsx b/src/components/ChatBox/index.tsx index 3a5565d91..78cec6aa9 100644 --- a/src/components/ChatBox/index.tsx +++ b/src/components/ChatBox/index.tsx @@ -162,16 +162,11 @@ export default function ChatBox(): JSX.Element { const isFinished = chatStore.tasks[_taskId as string].status === "finished"; const hasWaitComfirm = chatStore.tasks[_taskId as string]?.hasWaitComfirm; - // Check if this task was manually stopped (finished but without natural completion) - const wasTaskStopped = isFinished && !chatStore.tasks[_taskId as string].messages.some( - m => m.step === "end" // Natural completion has an "end" step message - ); - // Continue conversation if: - // 1. Has wait confirm (simple query response) - but not if task was stopped - // 2. Task is naturally finished (complex task completed) - but not if task was stopped + // 1. Has wait confirm (simple query response) + // 2. Task is finished (complex task completed) // 3. Has any messages but pending (ongoing conversation) - const shouldContinueConversation = (hasWaitComfirm && !wasTaskStopped) || (isFinished && !wasTaskStopped) || (hasMessages && chatStore.tasks[_taskId as string].status === "pending"); + const shouldContinueConversation = hasWaitComfirm || isFinished || (hasMessages && chatStore.tasks[_taskId as string].status === "pending"); if (shouldContinueConversation) { // Check if this is the very first message and task hasn't started @@ -421,38 +416,28 @@ export default function ChatBox(): JSX.Element { const handleSkip = async () => { const taskId = chatStore.activeTaskId as string; setIsPauseResumeLoading(true); - + try { - // First, try to notify backend to skip the task + // Skip the current task await fetchPost(`/chat/${projectStore.activeProjectId}/skip-task`, { project_id: projectStore.activeProjectId }); - // Only stop local task if backend call succeeds - chatStore.stopTask(taskId); + // Update task status to finished + chatStore.setStatus(taskId, 'finished'); chatStore.setIsPending(taskId, false); - + + // toast.success("Task skipped successfully", { + // closeButton: true, + // }); toast.success("Task stopped successfully", { closeButton: true, }); } catch (error) { console.error("Failed to skip task:", error); - - // If backend call failed, still try to stop local task as fallback - // but with different messaging to user - try { - chatStore.stopTask(taskId); - chatStore.setIsPending(taskId, false); - toast.warning("Task stopped locally, but backend notification failed. Backend task may continue running.", { - closeButton: true, - duration: 5000, - }); - } catch (localError) { - console.error("Failed to stop task locally:", localError); - toast.error("Failed to stop task completely. Please refresh the page.", { - closeButton: true, - }); - } + toast.error("Failed to skip task", { + closeButton: true, + }); } finally { setIsPauseResumeLoading(false); } diff --git a/src/store/chatStore.ts b/src/store/chatStore.ts index 26d68c83d..146f6d9cd 100644 --- a/src/store/chatStore.ts +++ b/src/store/chatStore.ts @@ -51,7 +51,6 @@ export interface ChatStore { tasks: { [key: string]: Task }; create: (id?: string, type?: any) => string; removeTask: (taskId: string) => void; - stopTask: (taskId: string) => void; setStatus: (taskId: string, status: 'running' | 'finished' | 'pending' | 'pause') => void; setActiveTaskId: (taskId: string) => void; replay: (taskId: string, question: string, time: number) => Promise; @@ -115,9 +114,6 @@ export type VanillaChatStore = { // Track auto-confirm timers per task to avoid reusing stale timers across rounds const autoConfirmTimers: Record> = {}; -// Track active SSE connections for proper cleanup -const activeSSEControllers: Record = {}; - const chatStore = (initial?: Partial) => createStore()( (set, get) => ({ activeTaskId: null, @@ -193,16 +189,6 @@ const chatStore = (initial?: Partial) => createStore()( console.warn('Error clearing auto-confirm timer in removeTask:', error); } - // Clean up SSE connection if it exists - try { - if (activeSSEControllers[taskId]) { - activeSSEControllers[taskId].abort(); - delete activeSSEControllers[taskId]; - } - } catch (error) { - console.warn('Error aborting SSE connection in removeTask:', error); - } - set((state) => { delete state.tasks[taskId]; return ({ @@ -212,58 +198,6 @@ const chatStore = (initial?: Partial) => createStore()( }) }) }, - stopTask(taskId: string) { - // Abort the SSE connection for this task - try { - if (activeSSEControllers[taskId]) { - console.log(`Stopping SSE connection for task ${taskId}`); - activeSSEControllers[taskId].abort(); - delete activeSSEControllers[taskId]; - } - } catch (error) { - console.warn('Error aborting SSE connection in stopTask:', error); - // Even if abort fails, still clean up the reference - try { - delete activeSSEControllers[taskId]; - } catch (cleanupError) { - console.warn('Error cleaning up SSE controller reference:', cleanupError); - } - } - - // Clean up any pending auto-confirm timers - try { - if (autoConfirmTimers[taskId]) { - clearTimeout(autoConfirmTimers[taskId]); - delete autoConfirmTimers[taskId]; - } - } catch (error) { - console.warn('Error clearing auto-confirm timer in stopTask:', error); - } - - // Update task status to finished - ensure this happens even if cleanup fails - try { - set((state) => { - // Check if task exists before updating - if (!state.tasks[taskId]) { - console.warn(`Task ${taskId} not found when trying to stop it`); - return state; - } - - return { - ...state, - tasks: { - ...state.tasks, - [taskId]: { - ...state.tasks[taskId], - status: 'finished' - }, - }, - }; - }); - } catch (error) { - console.error('Error updating task status to finished in stopTask:', error); - } - }, startTask: async (taskId: string, type?: string, shareToken?: string, delayTime?: number, messageContent?: string, messageAttaches?: File[]) => { // ✅ Wait for backend to be ready before starting task (except for replay/share) if (!type || type === 'normal') { @@ -275,7 +209,7 @@ const chatStore = (initial?: Partial) => createStore()( const { addMessages } = get(); addMessages(taskId, { id: generateUniqueId(), - role: 'agent', + role: 'system', content: '❌ Backend service is not ready. Please wait a moment and try again, or restart the application if the problem persists.', }); return; @@ -487,42 +421,26 @@ const chatStore = (initial?: Partial) => createStore()( // during active message processing let lockedChatStore = targetChatStore; let lockedTaskId = newTaskId; - - // Create AbortController for this task's SSE connection - // First check if there's already an active SSE connection for this task - if (activeSSEControllers[newTaskId]) { - console.warn(`Task ${newTaskId} already has an active SSE connection, aborting old one`); - try { - activeSSEControllers[newTaskId].abort(); - } catch (error) { - console.warn('Error aborting existing SSE connection:', error); - } - delete activeSSEControllers[newTaskId]; - } - - const abortController = new AbortController(); - activeSSEControllers[newTaskId] = abortController; - + // Getter functions that use the locked references instead of dynamic ones const getCurrentChatStore = () => { return lockedChatStore.getState(); }; - + // Get the locked task ID - this won't change during the SSE session const getCurrentTaskId = () => { return lockedTaskId; }; - + // Function to update locked references (only for special cases like replay) const updateLockedReferences = (newChatStore: VanillaChatStore, newTaskId: string) => { lockedChatStore = newChatStore; lockedTaskId = newTaskId; }; - + fetchEventSource(api, { method: !type ? "POST" : "GET", openWhenHidden: true, - signal: abortController.signal, // Add abort signal for proper cleanup headers: { "Content-Type": "application/json", "Authorization": type == 'replay' ? `Bearer ${token}` : undefined as unknown as string }, body: !type ? JSON.stringify({ project_id: project_id, @@ -567,32 +485,6 @@ const chatStore = (initial?: Partial) => createStore()( return; } - // Check if this task has been stopped before processing any message - // But allow messages that switch to new tasks (like confirmed events) - const lockedTaskId = getCurrentTaskId(); - const currentTask = getCurrentChatStore().tasks[lockedTaskId]; - - // Only ignore messages if: - // 1. The task doesn't exist, OR - // 2. The task is finished AND it's not a task-switching event - const isTaskSwitchingEvent = agentMessages.step === "confirmed" || - agentMessages.step === "new_task_state" || - agentMessages.step === "end"; - - // More robust check - only ignore if task doesn't exist OR - // task is finished and it's not a legitimate flow-control event - if (!currentTask) { - console.log(`Task ${lockedTaskId} not found, ignoring SSE message for step: ${agentMessages.step}`); - return; - } - - if (currentTask.status === 'finished' && !isTaskSwitchingEvent) { - // Only ignore non-essential messages for finished tasks - // Allow flow control messages through even for finished tasks - console.log(`Ignoring SSE message for finished task ${lockedTaskId}, step: ${agentMessages.step}`); - return; - } - console.log("agentMessages", agentMessages); const agentNameMap = { developer_agent: "Developer Agent", @@ -1737,31 +1629,12 @@ const chatStore = (initial?: Partial) => createStore()( // For other errors, log and throw to stop retrying console.error('[fetchEventSource] Fatal error, stopping connection:', err); - - // Clean up AbortController on error with robust error handling - try { - if (activeSSEControllers[newTaskId]) { - delete activeSSEControllers[newTaskId]; - console.log(`Cleaned up SSE controller for task ${newTaskId} after error`); - } - } catch (cleanupError) { - console.warn('Error cleaning up AbortController on SSE error:', cleanupError); - } throw err; }, // Server closes connection onclose() { - console.log("SSE connection closed"); - // Clean up AbortController when connection closes with robust error handling - try { - if (activeSSEControllers[newTaskId]) { - delete activeSSEControllers[newTaskId]; - console.log(`Cleaned up SSE controller for task ${newTaskId} after connection close`); - } - } catch (cleanupError) { - console.warn('Error cleaning up AbortController on SSE close:', cleanupError); - } + console.log("server closed"); }, }); @@ -2380,22 +2253,6 @@ const chatStore = (initial?: Partial) => createStore()( console.error('Error during timer cleanup in clearTasks:', error); } - // Clean up all active SSE connections - try { - Object.keys(activeSSEControllers).forEach(taskId => { - try { - if (activeSSEControllers[taskId]) { - activeSSEControllers[taskId].abort(); - delete activeSSEControllers[taskId]; - } - } catch (error) { - console.warn(`Error aborting SSE connection for task ${taskId}:`, error); - } - }); - } catch (error) { - console.error('Error during SSE cleanup in clearTasks:', error); - } - window.ipcRenderer.invoke('restart-backend') .then((res) => { console.log('restart-backend', res) From 75e0d5d3e16a38918489830fb134162f058ed1da Mon Sep 17 00:00:00 2001 From: LuoPengcheng <2653972504@qq.com> Date: Fri, 21 Nov 2025 02:00:26 +0800 Subject: [PATCH 5/7] minor format --- src/components/GroupedHistoryView/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/GroupedHistoryView/index.tsx b/src/components/GroupedHistoryView/index.tsx index 34c639963..9f9264357 100644 --- a/src/components/GroupedHistoryView/index.tsx +++ b/src/components/GroupedHistoryView/index.tsx @@ -429,4 +429,4 @@ export default function GroupedHistoryView({
); -} +} \ No newline at end of file From 8774db71e870bef035148c7e24b398676c5e51dc Mon Sep 17 00:00:00 2001 From: LuoPengcheng <2653972504@qq.com> Date: Fri, 21 Nov 2025 02:04:27 +0800 Subject: [PATCH 6/7] minor format --- .../GroupedHistoryView/ProjectDialog.tsx | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/components/GroupedHistoryView/ProjectDialog.tsx b/src/components/GroupedHistoryView/ProjectDialog.tsx index abfadd8b0..55d635367 100644 --- a/src/components/GroupedHistoryView/ProjectDialog.tsx +++ b/src/components/GroupedHistoryView/ProjectDialog.tsx @@ -20,7 +20,6 @@ import { LoaderCircle, } from "lucide-react"; import { useProjectStore } from "@/store/projectStore"; -import { TooltipSimple } from "@/components/ui/tooltip"; interface ProjectDialogProps { open: boolean; @@ -183,14 +182,12 @@ export default function ProjectDialog({ {t("layout.total-tasks")} - -
- - - {project.task_count} - -
-
+
+ + + {project.task_count} + +
From 8cd96e55d36ae3f85a958581a924cad3f3d739ee Mon Sep 17 00:00:00 2001 From: Wendong-Fan Date: Fri, 21 Nov 2025 02:21:21 +0800 Subject: [PATCH 7/7] fix --- src/components/GroupedHistoryView/ProjectGroup.tsx | 2 +- src/components/GroupedHistoryView/TaskItem.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/GroupedHistoryView/ProjectGroup.tsx b/src/components/GroupedHistoryView/ProjectGroup.tsx index 5c02016fb..1410ac3a9 100644 --- a/src/components/GroupedHistoryView/ProjectGroup.tsx +++ b/src/components/GroupedHistoryView/ProjectGroup.tsx @@ -335,7 +335,7 @@ export default function ProjectGroup({ {project.total_tokens ? project.total_tokens.toLocaleString() : "0"} - + {project.task_count} diff --git a/src/components/GroupedHistoryView/TaskItem.tsx b/src/components/GroupedHistoryView/TaskItem.tsx index 8d92a7add..8ec8f7e54 100644 --- a/src/components/GroupedHistoryView/TaskItem.tsx +++ b/src/components/GroupedHistoryView/TaskItem.tsx @@ -88,7 +88,7 @@ export default function TaskItem({ `} >
- +